From e5c4b6aa13b242f6c3fe23059316288e1b1e2261 Mon Sep 17 00:00:00 2001 From: ExtraNetwork Date: Tue, 12 May 2026 17:04:54 +0300 Subject: [PATCH] first commit --- .drone.yml | 102 + .editorconfig | 15 + .env.example | 54 + .gitignore | 38 + .styleci.yml | 6 + API_FLOW.md | 391 + Dockerfile | 82 + PROJECT_SUMMARY.md | 233 + README.md | 51 + app/Channels/OneSignalChannel.php | 22 + app/Console/Commands/.gitkeep | 0 .../ApplicationFillTableLanguageKey.php | 48 + .../ApplicationLanguageBaseData.php | 48 + .../ApplicationLanguageFiles.php | 48 + .../BestAvailableRateSyncService.php | 356 + .../MetaCancellationService.php | 130 + .../PropertyBookingSyncService.php | 72 + .../PropertyChannelSyncService.php | 222 + .../PropertyMetaRoomRatePriceService.php | 344 + .../PropertyMetaRoomRatePushService.php | 309 + .../PropertyMetaRoomRateService.php | 288 + .../RemovePaymentTokenService.php | 116 + .../ChannelManager/ReservationPullService.php | 957 + .../ChannelManager/ReservationPushService.php | 150 + .../RoomAvailabilityPushService.php | 262 + .../ChannelManager/RoomRatePushService.php | 255 + .../_ReselivaAvailRateUpdateService.php | 497 + .../_ReservationPullService.php | 784 + .../_ReservationPushService.php | 281 + .../Commands/ChannelManager/akgun.xlsx | Bin 0 -> 53811 bytes .../CurrencyRates/CurrencyRatesService.php | 188 + .../Commands/Data/DashboardCacheService.php | 149 + app/Console/Commands/Data/HotelBedsList.php | 128 + app/Console/Commands/Data/WeatherService.php | 130 + app/Console/Commands/Google/GoogleReview.php | 140 + .../Commands/Google/GoogleStaticMap.php | 107 + .../Commands/Google/GoogleVisioLabel.php | 72 + .../Jobs/BookingCommissionService.php | 122 + .../Jobs/BookingEngineSearchReportService.php | 194 + app/Console/Commands/Jobs/DataFetch.php | 92 + app/Console/Commands/Jobs/MailJobs.php | 97 + .../Commands/Jobs/PriceComparisonService.php | 252 + .../Commands/Jobs/PropertyCatalogService.php | 113 + .../Commands/Jobs/PropertyInvoiceService.php | 117 + .../Commands/Jobs/PropertySummaryService.php | 124 + .../Commands/Jobs/SummaryReportMail.php | 386 + .../Commands/Jobs/SummaryReportMailSales.php | 310 + .../PropertyReviewAnalyzeService.php | 239 + .../PropertyReviewScheduleService.php | 62 + .../PropertyReviewService.php | 738 + .../PropertyTrialCheck/PropertyTrialCheck.php | 125 + .../PropertyTrialCheck/PropertyTrialMail.php | 83 + .../PropertyWebCheckDns.php | 57 + .../PropertyWebMenuMappingConsole.php | 64 + app/Console/Kernel.php | 161 + app/Core/Contracts/MailInterface.php | 8 + app/Core/Helper/LanguageService.php | 196 + app/Core/Helper/PhpHelper.php | 570 + app/Core/Helper/compat_l5.php | 264 + app/Core/Helper/helpers.php | 463 + app/Core/Mail/AffiliateRequestMail.php | 60 + app/Core/Mail/BaseMail.php | 13 + .../BookingCancellationConfirmCodeMail.php | 85 + .../Mail/BookingCancellationRequestMail.php | 123 + .../Mail/BookingEngineSearchReportMail.php | 54 + app/Core/Mail/BookingInvoiceUpdateMail.php | 120 + app/Core/Mail/BookingPaymentDataCodeMail.php | 101 + .../Mail/BookingPropertyAddonUpdateMail.php | 113 + app/Core/Mail/BookingTicketMail.php | 140 + app/Core/Mail/CancelBookingMail.php | 277 + .../Mail/ChannelManagerNotificationMail.php | 70 + app/Core/Mail/ContactFormMail.php | 58 + app/Core/Mail/DailyReportMail.php | 55 + app/Core/Mail/DailyReportMailSales.php | 58 + app/Core/Mail/EnwContactFormMail.php | 54 + app/Core/Mail/InventoryActionMail.php | 60 + app/Core/Mail/InventoryPdfLinkMail.php | 60 + app/Core/Mail/LogMail.php | 52 + app/Core/Mail/ManualPaymentMail.php | 64 + app/Core/Mail/ModifiedBookingMail.php | 264 + app/Core/Mail/NewBookingMail.php | 68 + app/Core/Mail/OfferAcceptMail.php | 106 + app/Core/Mail/OfferPreConfirmCustomerMail.php | 91 + app/Core/Mail/OfferPreConfirmPropertyMail.php | 91 + app/Core/Mail/OfferSendMail.php | 86 + app/Core/Mail/PropertyProductOfferMail.php | 100 + app/Core/Mail/TrialFirstMail.php | 104 + app/Core/Mail/TrialSecondMail.php | 104 + app/Core/Mail/UserCreateMail.php | 46 + app/Core/Mail/UserForgotPassword.php | 48 + app/Core/Mail/WellcomeMail.php | 47 + .../Payment/BankOfGeorgia/BankOfGeorgia.php | 224 + app/Core/Payment/Esnekpos/Esnekpos.php | 239 + app/Core/Payment/HalkOde/HalkOde.php | 254 + app/Core/Payment/KuveytTurk/KuveytTurk.php | 241 + app/Core/Payment/Moka/Moka.php | 176 + app/Core/Payment/Payriff/Payriff.php | 170 + app/Core/Payment/Pos/AkPos.php | 701 + app/Core/Payment/Pos/EstPos.php | 881 + app/Core/Payment/Pos/EstPosV3.php | 913 + .../Pos/Exceptions/BankClassNullException.php | 25 + .../Pos/Exceptions/BankNotFoundException.php | 25 + .../UnsupportedPaymentModelException.php | 25 + .../UnsupportedTransactionTypeException.php | 25 + app/Core/Payment/Pos/GarantiPos.php | 1059 + app/Core/Payment/Pos/Pos.php | 230 + app/Core/Payment/Pos/PosHelpersTrait.php | 99 + app/Core/Payment/Pos/PosInterface.php | 141 + app/Core/Payment/Pos/PosNet.php | 1106 + app/Core/Payment/Pos/PosNetCrypt.php | 170 + app/Core/Payment/Pos/QPos.php | 644 + app/Core/Payment/Pos/VPos.php | 656 + app/Core/Payment/Pos/VkfPos.php | 689 + app/Core/Payment/Pos/config/pos.php | 202 + app/Core/Payment/Pos/ex/.gitignore | 4 + app/Core/Payment/Pos/ex/LICENCE.md | 3 + app/Core/Payment/Pos/ex/LICENSE.txt | 21 + app/Core/Payment/Pos/ex/README.md | 190 + app/Core/Payment/Pos/ex/composer.json | 31 + .../Pos/ex/examples/akbank/3d-pay/_config.php | 30 + .../Pos/ex/examples/akbank/3d-pay/form.php | 64 + .../Pos/ex/examples/akbank/3d-pay/index.php | 57 + .../ex/examples/akbank/3d-pay/response.php | 108 + .../Pos/ex/examples/akbank/3d/_config.php | 32 + .../Pos/ex/examples/akbank/3d/form.php | 64 + .../Pos/ex/examples/akbank/3d/index.php | 57 + .../Pos/ex/examples/akbank/3d/response.php | 108 + .../ex/examples/akbank/regular/_config.php | 31 + .../Pos/ex/examples/akbank/regular/cancel.php | 34 + .../Pos/ex/examples/akbank/regular/direct.php | 35 + .../ex/examples/akbank/regular/history.php | 34 + .../Pos/ex/examples/akbank/regular/index.php | 48 + .../Pos/ex/examples/akbank/regular/post.php | 66 + .../Pos/ex/examples/akbank/regular/refund.php | 35 + .../ex/examples/akbank/regular/response.php | 114 + .../Pos/ex/examples/akbank/regular/status.php | 34 + .../ex/examples/garanti/3d-pay/_config.php | 33 + .../Pos/ex/examples/garanti/3d-pay/form.php | 68 + .../Pos/ex/examples/garanti/3d-pay/index.php | 57 + .../ex/examples/garanti/3d-pay/response.php | 109 + .../Pos/ex/examples/garanti/3d/_config.php | 33 + .../Pos/ex/examples/garanti/3d/form.php | 64 + .../Pos/ex/examples/garanti/3d/index.php | 57 + .../Pos/ex/examples/garanti/3d/response.php | 108 + .../ex/examples/garanti/regular/_config.php | 34 + .../ex/examples/garanti/regular/cancel.php | 39 + .../ex/examples/garanti/regular/direct.php | 35 + .../ex/examples/garanti/regular/history.php | 36 + .../Pos/ex/examples/garanti/regular/index.php | 44 + .../Pos/ex/examples/garanti/regular/post.php | 70 + .../ex/examples/garanti/regular/refund.php | 39 + .../ex/examples/garanti/regular/response.php | 125 + .../ex/examples/garanti/regular/status.php | 36 + .../Pos/ex/examples/template/_footer.php | 15 + .../Pos/ex/examples/template/_header.php | 16 + .../Pos/ex/examples/ykb/3d/_config.php | 38 + .../Payment/Pos/ex/examples/ykb/3d/form.php | 48 + .../Payment/Pos/ex/examples/ykb/3d/index.php | 77 + .../Pos/ex/examples/ykb/3d/response.php | 112 + .../Pos/ex/examples/ykb/regular/_config.php | 31 + .../Pos/ex/examples/ykb/regular/cancel.php | 43 + .../Pos/ex/examples/ykb/regular/direct.php | 35 + .../Pos/ex/examples/ykb/regular/history.php | 34 + .../Pos/ex/examples/ykb/regular/index.php | 44 + .../Pos/ex/examples/ykb/regular/post.php | 70 + .../Pos/ex/examples/ykb/regular/refund.php | 36 + .../Pos/ex/examples/ykb/regular/response.php | 119 + .../Pos/ex/examples/ykb/regular/status.php | 34 + app/Core/Payment/Pos/ex/phpunit.xml | 25 + app/Core/Payment/Pos/ex/tests/EstPostTest.php | 149 + .../Payment/Pos/ex/tests/GarantiPosTest.php | 98 + .../Payment/Pos/ex/tests/PosNetCryptTest.php | 35 + app/Core/Payment/Pos/ex/tests/PosNetTest.php | 110 + app/Core/Payment/Pos/ex/tests/PosTest.php | 94 + app/Core/Payment/QNBPay/QNBPay.php | 254 + app/Core/Payment/RetailPay/RetailPay.php | 268 + app/Core/Payment/Sipay/Sipay.php | 253 + .../Payment/TBCBankGeorgia/TBCBankGeorgia.php | 230 + app/Core/Payment/WeePay/WeePay.php | 283 + .../Permission/AbstractRelatedPermission.php | 153 + .../Permission/RoutePermissionAuthorize.php | 171 + .../ApiAccessTokenRepository.php | 19 + .../ApplicationCacheRepository.php | 57 + .../AwardsCertificateCategoryRepository.php | 19 + .../Booking/BookingAddonRepository.php | 19 + .../Booking/BookingContactRepository.php | 19 + .../BookingPaymentDataCheckRepository.php | 19 + .../Booking/BookingPaymentDataRepository.php | 19 + .../Booking/BookingPaymentRepository.php | 19 + .../Repository/Booking/BookingRepository.php | 19 + .../Booking/BookingRoomPaxRepository.php | 19 + .../Booking/BookingRoomRepository.php | 19 + .../Booking/BookingStatusRepository.php | 19 + .../Booking/BookingTicketRepository.php | 19 + .../PropertyChannelCouponRepository.php | 19 + .../ChannelManagerRepository.php | 19 + .../ChannelManagerBookingRepository.php | 19 + .../ChannelManagerLogRepository.php | 19 + .../ChannelManagerMappingRepository.php | 19 + ...hannelManagerPropertyMappingRepository.php | 19 + ...elManagerPropertyRateMappingRepository.php | 19 + .../Repository/Country/CountryRepository.php | 19 + .../Currency/CurrencyRepository.php | 20 + .../CurrencyRates/CurrencyRatesRepository.php | 19 + .../EleqouentAbstractRepository.php | 522 + .../GeneralTimezoneRepository.php | 19 + .../Ip2NationCountriesRepository.php | 19 + .../IpNation/IpNationRepository.php | 19 + app/Core/Repository/Jobs/JobsRepository.php | 19 + .../Language/LanguageRepository.php | 19 + .../LanguageBase/LanguageBaseRepository.php | 19 + .../LanguageTranslateRepository.php | 19 + app/Core/Repository/Offer/OfferRepository.php | 19 + .../OfferAccommodationMappingRepository.php | 19 + .../OfferCancellationPolicyRepository.php | 19 + .../OfferConfirmTypeRepository.php | 19 + .../OfferContactMappingRepository.php | 19 + .../OfferFactMappingRepository.php | 19 + .../OfferImportantNotesRepository.php | 19 + .../OfferPaymentTypeRepository.php | 19 + .../OfferPhotoMappingRepository.php | 19 + .../OfferPrice/OfferPriceRepository.php | 19 + .../OfferRoomMappingRepository.php | 19 + .../PaymentBinNumberRepository.php | 19 + .../PaymentTransactionRepository.php | 19 + .../PaymentType/PaymentTypeRepository.php | 19 + .../Permission/PermissionRepository.php | 27 + .../PermissionGroupRepository.php | 19 + .../PermissionGroupMappingRepository.php | 19 + .../PermissionGroupUserMappingRepository.php | 19 + .../PhotoGoogleLabelRepository.php | 20 + .../PlaceCategoryFieldRepository.php | 19 + .../PlaceCategoryFieldMappingRepository.php | 19 + .../PlaceCategoryFieldOptionRepository.php | 19 + ...egoryFieldOptionFieldMappingRepository.php | 19 + .../ProductParameterMappingRepository.php | 20 + .../Repository/Product/ProductRepository.php | 20 + .../PropertyProductMappingRepository.php | 20 + .../PromotionType/PromotionTypeRepository.php | 20 + .../Property/PropertyRepository.php | 19 + .../PropertyAdditionalInfoKeyRepository.php | 19 + .../PropertyAdditionalInfoRepository.php | 19 + .../PropertyAddon/PropertyAddonRepository.php | 19 + .../PropertyChannelAddonRepository.php | 19 + .../PropertyAvailabilityTypeRepository.php | 19 + .../PropertyAwardsCertificateRepository.php | 20 + .../PropertyBookingEngineRepository.php | 19 + .../PropertyBookingEngineSearchRepository.php | 19 + .../PropertyBookingPaymentTypeRepository.php | 19 + .../PropertyBookingTypeRepository.php | 19 + .../PropertyBrand/PropertyBrandRepository.php | 19 + .../PropertyCancellationPolicyRepository.php | 19 + .../PropertyChain/PropertyChainRepository.php | 20 + .../PropertyChannelContactRepository.php | 19 + .../PropertyChannelRepository.php | 19 + .../PropertyChannelTaxRepository.php | 19 + ...tyChannelBookingPaymentSetupRepository.php | 19 + .../PropertyChannelCategoryRepository.php | 19 + ...tyChannelGroupChannelMappingRepository.php | 19 + .../PropertyChannelGroupRepository.php | 19 + .../PropertyChannelMappingRepository.php | 19 + ...ateCancellationPolicyMappingRepository.php | 19 + ...atePricingPolicyAdultMappingRepository.php | 19 + ...atePricingPolicyChildMappingRepository.php | 19 + .../PropertyCompetitorGroupRepository.php | 19 + ...opertyCompetitorGroupMappingRepository.php | 18 + .../PropertyCompetitorMappingRepository.php | 19 + .../PropertyConfigRepository.php | 19 + .../PropertyContactRepository.php | 19 + .../PropertyContentRepository.php | 19 + .../PropertyContentCategoryRepository.php | 19 + .../PropertyExecutiveRepository.php | 19 + .../PropertyExecutiveTypeRepository.php | 19 + .../PropertyFact/PropertyFactRepository.php | 19 + .../PropertyFactAttributeRepository.php | 19 + .../PropertyFactMappingRepository.php | 19 + .../PropertyGroup/PropertyGroupRepository.php | 19 + .../PropertyGroupMappingRepository.php | 19 + .../PropertyInvoiceRepository.php | 19 + .../PropertyLanguageSpokenRepository.php | 19 + .../PropertyModuleRepository.php | 20 + .../PropertyModuleMappingRepository.php | 20 + .../PropertyNonrefundableRepository.php | 19 + .../PropertyPaymentInstallmentRepository.php | 19 + .../PropertyPaymentMappingRepository.php | 19 + .../PropertyPhoto/PropertyPhotoRepository.php | 20 + .../PropertyPhotoCategoryRepository.php | 20 + ...rtyPhotoCategoryLabelMappingRepository.php | 20 + .../PropertyPlace/PropertyPlaceRepository.php | 19 + .../PropertyPlaceCategoryRepository.php | 19 + ...pertyPlaceCategoryFieldValueRepository.php | 19 + .../PropertyPlaceFactRepository.php | 19 + .../PropertyPlaceFactMappingRepository.php | 19 + .../PropertyPlaceFactTitleRepository.php | 19 + ...rtyPlaceFactTitleFactMappingRepository.php | 19 + .../PropertyPlacePhotoMappingRepository.php | 19 + .../PropertyPlaceWorkingHourRepository.php | 19 + .../PropertyPricingPolicyAdultRepository.php | 19 + .../PropertyPricingPolicyChildRepository.php | 19 + .../PropertyPromotionRepository.php | 20 + .../PropertyPromotionMappingRepository.php | 20 + .../PropertyQuickPricingMappingRepository.php | 19 + .../PropertyRoom/PropertyRoomRepository.php | 19 + ...ropertyRoomAvailabilityQueueRepository.php | 19 + .../PropertyRoomAvailabilityRepository.php | 19 + .../PropertyRoomBedRepository.php | 19 + .../PropertyRoomBedTypeRepository.php | 19 + .../PropertyRoomConnectedRepository.php | 19 + .../PropertyRoomFactMappingRepository.php | 19 + .../PropertyRoomPhotoMappingRepository.php | 19 + .../PropertyRoomPricingTypeRepository.php | 19 + .../PropertyRoomRateRepository.php | 19 + ...opertyRoomRateChannelMappingRepository.php | 19 + ...ertyRoomRateInclusionMappingRepository.php | 19 + .../PropertyRoomRateMappingRepository.php | 19 + ...PropertyRoomRateMappingSetupRepository.php | 19 + .../PropertyRoomRatePriceQueueRepository.php | 19 + .../PropertyRoomRatePriceRepository.php | 19 + .../PropertyRoomSizeTypeRepository.php | 20 + .../PropertyRoomTypeRepository.php | 19 + .../PropertyRoomViewMappingRepository.php | 19 + .../PropertyRoomViewTypeRepository.php | 20 + .../PropertySummaryRepository.php | 19 + .../PropertyType/PropertyTypeRepository.php | 20 + .../PropertyUnit/PropertyUnitRepository.php | 19 + .../PropertyWebComponentMappingRepository.php | 19 + .../PropertyWebComponentRepository.php | 19 + .../PropertyWebGroupRepository.php | 19 + .../PropertyWebMetaMappingRepository.php | 19 + .../PropertyWeb/PropertyWebMetaRepository.php | 19 + .../PropertyWebMetaTagRepository.php | 19 + .../PropertyWeb/PropertyWebRepository.php | 19 + .../PropertyWebReviewRepository.php | 19 + .../PropertyWebAboutUsRepository.php | 18 + .../PropertyWebColorMappingRepository.php | 19 + .../PropertyWebContentCategoryRepository.php | 19 + .../PropertyWebContentRepository.php | 19 + .../PropertyWebLanguageMappingRepository.php | 19 + .../PropertyWebLogRepository.php | 19 + .../PropertyWebMenuRepository.php | 19 + .../PropertyWebMenuMappingRepository.php | 19 + .../PropertyWebPhotoMappingRepository.php | 19 + .../PropertyWebPlaceMappingRepository.php | 19 + .../PropertyWebPopupRepository.php | 19 + .../PropertyWebRoomMappingRepository.php | 19 + .../PropertyWebSetupRepository.php | 19 + .../PropertyWebTemplateRepository.php | 19 + .../PropertyReviewCategoryRepository.php | 19 + ...PropertyReviewChannelMappingRepository.php | 19 + .../PropertyReviewChannelRepository.php | 19 + .../PropertyReviewRepository.php | 19 + .../ServiceLog/ServiceLogRepository.php | 19 + .../SiteConfig/SiteConfigRepository.php | 19 + .../TempProperty/TempPropertyRepository.php | 70 + app/Core/Repository/User/UserRepository.php | 19 + .../UserPropertyMappingRepository.php | 19 + .../VWDestination/VWDestinationRepository.php | 19 + app/Core/Service/ApiAccessTokenService.php | 114 + app/Core/Service/ApplicationCacheService.php | 103 + app/Core/Service/BookingAddonService.php | 155 + app/Core/Service/BookingContactService.php | 137 + app/Core/Service/BookingPaymentService.php | 386 + app/Core/Service/BookingRoomPaxService.php | 131 + app/Core/Service/BookingRoomService.php | 144 + app/Core/Service/BookingService.php | 952 + app/Core/Service/BookingTicketService.php | 160 + app/Core/Service/ChannelManager/Athena.php | 304 + app/Core/Service/ChannelManager/Channex.php | 1277 + .../Service/ChannelManager/ElektraWeb.php | 226 + app/Core/Service/ChannelManager/Fina.php | 255 + .../Service/ChannelManager/HotelRunner.php | 284 + .../Service/ChannelManager/HyperGuest.php | 416 + app/Core/Service/ChannelManager/Mirai.php | 685 + app/Core/Service/ChannelManager/Reseliva.php | 1236 + .../Service/ChannelManager/SistemOtel.php | 252 + app/Core/Service/ChannelManager/Trivago.php | 208 + app/Core/Service/ChannelManager/_Mirai.php | 667 + .../Service/ChannelManagerBookingService.php | 145 + app/Core/Service/ChannelManagerLogService.php | 119 + .../Service/ChannelManagerMappingService.php | 134 + .../ChannelManagerPropertyMappingService.php | 134 + ...annelManagerPropertyRateMappingService.php | 134 + app/Core/Service/ChannelManagerService.php | 134 + .../CompetitorPriceAnalysisService.php | 130 + app/Core/Service/CountryService.php | 43 + app/Core/Service/CurrencyService.php | 145 + app/Core/Service/DashboardPlusService.php | 878 + app/Core/Service/DestinationService.php | 107 + app/Core/Service/EnwContactFormService.php | 85 + app/Core/Service/FindCountryCodeService.php | 122 + app/Core/Service/GeneralTimezoneService.php | 109 + .../Google/GoogleVisionLabelService.php | 72 + app/Core/Service/JobsService.php | 20 + app/Core/Service/JwtService.php | 79 + app/Core/Service/LanguageBaseService.php | 581 + app/Core/Service/LanguageService.php | 196 + app/Core/Service/ManualPaymentMailService.php | 148 + app/Core/Service/MyWebContentService.php | 2596 ++ app/Core/Service/NewBookingMailService.php | 364 + app/Core/Service/NotificationService.php | 162 + .../OfferAccommodationMappingService.php | 282 + .../OfferCancellationPolicyService.php | 175 + .../Service/OfferContactMappingService.php | 279 + app/Core/Service/OfferFactMappingService.php | 338 + .../Service/OfferImportantNotesService.php | 219 + app/Core/Service/OfferPaymentTypeService.php | 155 + app/Core/Service/OfferPhotoMappingService.php | 449 + app/Core/Service/OfferPriceService.php | 232 + app/Core/Service/OfferRoomMappingService.php | 296 + app/Core/Service/OfferService.php | 1484 ++ app/Core/Service/OneSignalService.php | 68 + app/Core/Service/PdfContentService.php | 635 + app/Core/Service/PermissionGroupService.php | 28 + .../PermissionGroupUserMappingService.php | 49 + app/Core/Service/PermissionService.php | 181 + .../ProductParameterMappingService.php | 43 + app/Core/Service/ProductService.php | 263 + .../PropertyAdditionalInfoService.php | 470 + app/Core/Service/PropertyAddonService.php | 205 + .../PropertyAwardsCertificateService.php | 531 + .../PropertyBookingEngineSearchService.php | 163 + .../Service/PropertyBookingEngineService.php | 288 + .../PropertyBookingPaymentTypeService.php | 88 + app/Core/Service/PropertyBrandService.php | 500 + .../PropertyCancellationPolicyService.php | 572 + app/Core/Service/PropertyChainService.php | 95 + ...pertyChannelBookingPaymentSetupService.php | 397 + .../PropertyChannelCategoryService.php | 122 + .../Service/PropertyChannelContactService.php | 256 + .../Service/PropertyChannelCouponService.php | 179 + ...pertyChannelGroupChannelMappingService.php | 131 + .../Service/PropertyChannelGroupService.php | 278 + .../Service/PropertyChannelMappingService.php | 1214 + app/Core/Service/PropertyChannelService.php | 367 + .../PropertyCompetitorGroupService.php | 371 + app/Core/Service/PropertyConfigService.php | 475 + app/Core/Service/PropertyContactService.php | 330 + app/Core/Service/PropertyContentService.php | 291 + app/Core/Service/PropertyExecutiveService.php | 625 + .../Service/PropertyExecutiveTypeService.php | 151 + .../Service/PropertyFactMappingService.php | 338 + app/Core/Service/PropertyFactService.php | 758 + .../Service/PropertyGroupMappingService.php | 88 + app/Core/Service/PropertyGroupService.php | 83 + app/Core/Service/PropertyInvoiceService.php | 329 + .../Service/PropertyModuleMappingService.php | 253 + app/Core/Service/PropertyNetworkService.php | 190 + .../Service/PropertyNonrefundableService.php | 361 + app/Core/Service/PropertyPaymentService.php | 3850 +++ .../PropertyPersonPricingPolicyService.php | 902 + .../PropertyPhotoCategoryService.php | 105 + .../PropertyPhotoService.php | 1212 + app/Core/Service/PropertyPlaceService.php | 2027 ++ .../Service/PropertyProductMappingService.php | 123 + app/Core/Service/PropertyPromotionService.php | 863 + .../Service/PropertyQuickPricingService.php | 130 + .../PropertyRoomAvailabilityQueueService.php | 115 + .../PropertyRoomAvailabilityService.php | 802 + app/Core/Service/PropertyRoomBedService.php | 260 + .../Service/PropertyRoomBedTypeService.php | 209 + .../PropertyRoomFactMappingService.php | 423 + .../PropertyRoomPhotoMappingService.php | 254 + .../PropertyRoomRateChannelMappingService.php | 340 + ...ropertyRoomRateInclusionMappingService.php | 145 + .../PropertyRoomRateMappingService.php | 705 + .../PropertyRoomRateMappingSetupService.php | 140 + .../PropertyRoomRatePriceQueueService.php | 115 + .../Service/PropertyRoomRatePriceService.php | 1183 + app/Core/Service/PropertyRoomRateService.php | 573 + app/Core/Service/PropertyRoomService.php | 2082 ++ .../Service/PropertyRoomSizeTypeService.php | 44 + app/Core/Service/PropertyRoomTypeService.php | 252 + .../Service/PropertyRoomViewTypeService.php | 58 + app/Core/Service/PropertyService.php | 690 + app/Core/Service/PropertySummaryService.php | 255 + app/Core/Service/PropertyTypeService.php | 95 + .../Service/PropertyWebAboutUsService.php | 207 + .../PropertyWebColorMappingService.php | 217 + .../Service/PropertyWebContentService.php | 340 + .../PropertyWebLanguageMappingService.php | 186 + app/Core/Service/PropertyWebLogService.php | 209 + .../Service/PropertyWebMenuMappingService.php | 221 + app/Core/Service/PropertyWebMenuService.php | 91 + app/Core/Service/PropertyWebMetaService.php | 222 + .../PropertyWebPhotoMappingService.php | 90 + .../PropertyWebPlaceMappingService.php | 182 + app/Core/Service/PropertyWebPopupService.php | 399 + .../Service/PropertyWebRoomMappingService.php | 202 + app/Core/Service/PropertyWebService.php | 1179 + app/Core/Service/PropertyWebSetupService.php | 159 + .../Service/PropertyWebTemplateService.php | 125 + app/Core/Service/QRCodeService.php | 33 + .../Service/ReputationManagementService.php | 227 + app/Core/Service/ServiceLogService.php | 154 + app/Core/Service/SiteConfigService.php | 171 + app/Core/Service/TempPropertyService.php | 89 + .../ThirdPartyServices/MondayService.php | 122 + .../Service/UserPropertyMappingService.php | 254 + app/Core/Service/UserService.php | 518 + app/Core/Validator/BaseValidator.php | 109 + .../Booking/BookingUpdateValidator.php | 47 + .../BookingEngine/ContractUploadValidator.php | 49 + .../DestinationSearchValidator.php | 55 + .../EnwContactFormCreateValidator.php | 46 + app/Core/Validator/ExampleValidator.php | 66 + .../Validator/Offer/OfferCreateValidator.php | 47 + .../Validator/Offer/OfferStatusValidator.php | 45 + .../Validator/Offer/OfferUpdateValidator.php | 46 + .../OfferAccommodationMappingAddValidator.php | 49 + ...ferAccommodationMappingCreateValidator.php | 44 + ...OfferCancellationPolicyCreateValidator.php | 54 + .../OfferContactMappingAddValidator.php | 47 + .../OfferContactMappingCreateValidator.php | 45 + .../OfferFactMappingAddValidator.php | 45 + .../OfferFactMappingCreateValidator.php | 47 + .../OfferImportantNotesCreateValidator.php | 45 + .../OfferPaymentTypeCreateValidator.php | 42 + .../OfferCoverPhotoAddValidator.php | 46 + .../OfferPhotoMappingAddValidator.php | 46 + .../OfferPhotoMappingCreateValidator.php | 45 + .../OfferPrice/OfferPriceCreateValidator.php | 51 + .../OfferRoomMappingAddValidator.php | 49 + .../OfferRoomMappingCreateValidator.php | 46 + .../Property/PropertyBrandAddValidator.php | 51 + .../Property/PropertyBrandPhotoValidator.php | 48 + .../Property/PropertyCreateValidator.php | 50 + .../PropertyLocationUpdateValidator.php | 51 + .../Property/PropertyPhotoValidator.php | 48 + .../Property/PropertySearchValidator.php | 50 + .../Property/PropertyUpdateValidator.php | 73 + .../PropertyAdditionalInfoAddValidator.php | 50 + .../PropertyAddon/PropertyAddonValidator.php | 47 + ...ropertyAwardCertificateUploadValidator.php | 48 + ...opertyAwardsCertificateCreateValidator.php | 45 + ...opertyAwardsCertificateUpdateValidator.php | 46 + .../PropertyTicketCreateValidator.php | 47 + .../PropertyTicketGetValidator.php | 45 + ...PropertyCancellationPolicyAddValidator.php | 67 + ...pertyCancellationPolicyUpdateValidator.php | 78 + ...RateChannelCancellationPolicyValidator.php | 57 + .../PropertyChannelAddValidator.php | 51 + .../PropertyChannelDeleteValidator.php | 52 + .../PropertyChannelUpdateValidator.php | 52 + ...ChannelBookingPaymentSetupAddValidator.php | 57 + .../PropertyChannelCouponValidator.php | 49 + .../PropertyChannelMappingAddValidator.php | 53 + .../PropertyChannelMappingRemoveValidator.php | 51 + .../PropertyChannelMappingUpdateValidator.php | 53 + .../PropertyChannelSetupValidator.php | 96 + .../PropertyChannelSetupAddValidator.php | 56 + .../PropertyChannelSetupUpdateValidator.php | 56 + ...SetupWithMissingParametersAddValidator.php | 50 + .../PropertyConfigCreateValidator.php | 49 + .../PropertyContactCreateValidator.php | 79 + .../PropertyContactUpdateValidator.php | 73 + .../PropertyContentCreateValidator.php | 54 + .../PropertyExecutiveCreateValidator.php | 57 + .../PropertyExecutiveUpdateValidator.php | 55 + .../PropertyFactCheckboxValidator.php | 51 + .../PropertyFactSearchValidator.php | 49 + .../PropertyNonrefundableAddValidator.php | 56 + ...pertyPaymentTransactionCreateValidator.php | 57 + ...pertyPaymentTransactionUpdateValidator.php | 58 + ...ertyPaymentInstallmentsUpdateValidator.php | 56 + .../PropertyPaymentMappingCreateValidator.php | 102 + ...pertyPaymentMappingSetDefaultValidator.php | 53 + ...rtyPaymentMappingStatusUpdateValidator.php | 54 + .../PropertyPaymentMappingUpdateValidator.php | 54 + ...PropertyPricingPolicyAdultAddValidator.php | 76 + ...pertyPricingPolicyAdultUpdateValidator.php | 86 + ...PropertyPricingPolicyChildAddValidator.php | 73 + ...pertyPricingPolicyChildUpdateValidator.php | 88 + ...annelPersonPricingAdultPolicyValidator.php | 58 + ...annelPersonPricingChildPolicyValidator.php | 58 + .../PropertyPlaceCreateValidator.php | 69 + ...ropertyPlaceFactMappingUpdateValidator.php | 47 + .../PropertyPlacePhotoMappingAddValidator.php | 88 + .../PropertyPlaceUpdateValidator.php | 68 + .../PropertyPromotionAddValidator.php | 84 + .../PropertyPromotionUpdateValidator.php | 83 + ...pdateRoomRateChannelPromotionValidator.php | 56 + .../PropertyQuickPricingCreateValidator.php | 48 + .../PropertyRoom/PropertyRoomAddValidator.php | 69 + .../PropertyRoomAndBedAddValidator.php | 81 + .../PropertyRoomAndBedUpdateValidator.php | 91 + .../PropertyRoomDeleteValidator.php | 54 + .../PropertyRoomFactMappingAddValidator.php | 69 + .../PropertyRoomInventoryValidator.php | 51 + .../PropertyRoomPhotoMappingAddValidator.php | 88 + .../PropertyRoomUpdateValidator.php | 72 + .../PropertyRoomAvailabilityAddValidator.php | 62 + ...rtyRoomAvailabilityBulkInsertValidator.php | 63 + ...rtyRoomAvailabilityBulkUpdateValidator.php | 56 + ...ropertyRoomAvailabilityDeleteValidator.php | 54 + ...ropertyRoomAvailabilityUpdateValidator.php | 53 + .../PropertyRoomBedAddValidator.php | 53 + .../PropertyRoomRateAddValidator.php | 53 + ...pertyRoomRateAddWithInclusionValidator.php | 70 + .../PropertyRoomRateDeleteValidator.php | 54 + .../PropertyRoomRateUpdateValidator.php | 71 + ...ertyRoomRateChannelMappingAddValidator.php | 54 + ...yRoomRateChannelMappingUpdateValidator.php | 54 + ...tyRoomRateInclusionMappingAddValidator.php | 54 + .../PropertyRoomRateMappingAddValidator.php | 55 + ...tyRoomRateMappingAddWithSetupValidator.php | 59 + ...pertyRoomRateMappingConnectedValidator.php | 55 + ...PropertyRoomRateMappingDeleteValidator.php | 54 + ...pertyRoomRateMappingSetStatusValidator.php | 48 + ...PropertyRoomRateMappingUpdateValidator.php | 55 + ...opertyRoomRateMappingSetupAddValidator.php | 61 + .../PropertyRoomRatePriceAddValidator.php | 62 + ...opertyRoomRatePriceBulkInsertValidator.php | 65 + ...opertyRoomRatePriceBulkUpdateValidator.php | 59 + .../PropertyRoomRatePriceDeleteValidator.php | 54 + .../PropertyRoomRatePriceUpdateValidator.php | 53 + .../PropertyWebContactFormValidator.php | 52 + .../PropertyWebCreateValidator.php | 88 + .../PropertyWebPublishValidator.php | 44 + .../PropertyWebUpdateContentValidator.php | 50 + .../PropertyWebUpdateValidator.php | 76 + .../PropertyWebContentCreateValidator.php | 52 + .../PropertyWebTemplateCreateValidator.php | 43 + .../TempPropertySearchValidator.php | 50 + .../User/ChangePasswordValidator.php | 69 + .../Validator/User/ResetPasswordValidator.php | 55 + .../Validator/User/UserCreateValidator.php | 73 + .../Validator/User/UserLoginValidator.php | 50 + .../User/UserNewPasswordValidator.php | 52 + .../User/UserProfileUpdateValidator.php | 44 + .../UserPropertyMappingAddValidator.php | 53 + .../UserPropertyMappingRemoveValidator.php | 52 + app/Events/Event.php | 10 + app/Events/ExampleEvent.php | 16 + app/Exceptions/ApiErrorException.php | 25 + app/Exceptions/Handler.php | 96 + app/Export/PropertyBookingListExport.php | 78 + app/Export/PropertyCompetitorExport.php | 75 + app/Http/Controllers/AuthController.php | 328 + .../BookingEngine/V1/BookingController.php | 2661 ++ .../V1/BookingEngineBaseController.php | 109 + .../V1/PaymentLinkController.php | 489 + .../V1/PropertyBookingEngineController.php | 357 + .../BookingEngine/V1/SearchController.php | 2136 ++ .../Athena/v1/AthenaController.php | 488 + .../Channex/v1/ChannexController.php | 228 + .../ElektraWeb/v1/ElektraWebController.php | 893 + .../ElektraWeb/v1/_ElektraWebController.php | 809 + .../ChannelManager/Fina/v1/FinaController.php | 488 + .../HotelRunner/v1/HotelRunnerController.php | 825 + .../HyperGuest/v1/HyperGuestController.php | 635 + .../ChannelManager/OneC/v1/OneCController.php | 640 + .../Reseliva/v1/ReselivaController.php | 1069 + .../SistemOtel/v1/SistemOtelController.php | 827 + app/Http/Controllers/Controller.php | 12 + .../MetaSearch/Google/v1/GoogleController.php | 372 + .../Trivago/v1/TrivagoController.php | 872 + .../Trivago/v1/_TrivagoController.php | 884 + .../MetaSearch/Yandex/v1/YandexController.php | 541 + app/Http/Controllers/PaymentController.php | 257 + app/Http/Controllers/TestController.php | 491 + app/Http/Controllers/UserController.php | 845 + app/Http/Controllers/V1/AIController.php | 84 + .../V1/CompetitorPriceAnalysisController.php | 1235 + .../Controllers/V1/CurrencyController.php | 49 + .../Controllers/V1/DestinationController.php | 75 + .../V1/EnwContactFormController.php | 74 + .../Controllers/V1/ExportPdfController.php | 741 + .../Controllers/V1/LanguageController.php | 138 + .../Controllers/V1/MyWebContentController.php | 1277 + app/Http/Controllers/V1/PaymentController.php | 368 + app/Http/Controllers/V1/ProductController.php | 105 + .../V1/PropertyAdditionalInfoController.php | 131 + .../V1/PropertyAddonController.php | 349 + .../PropertyAwardCertificatesController.php | 256 + .../V1/PropertyBookingController.php | 888 + .../V1/PropertyBookingTicketController.php | 293 + .../V1/PropertyBrandController.php | 158 + .../PropertyCancellationPolicyController.php | 207 + .../V1/PropertyChainController.php | 72 + ...tyChannelBookingPaymentSetupController.php | 111 + .../V1/PropertyChannelCategoryController.php | 74 + .../V1/PropertyChannelContactController.php | 262 + .../V1/PropertyChannelController.php | 207 + .../V1/PropertyChannelGroupController.php | 497 + .../V1/PropertyChannelMappingController.php | 447 + .../V1/PropertyCompetitorGroupController.php | 184 + .../V1/PropertyConfigController.php | 165 + .../V1/PropertyContactController.php | 175 + .../V1/PropertyContentController.php | 122 + .../Controllers/V1/PropertyController.php | 1220 + .../V1/PropertyCouponController.php | 204 + .../V1/PropertyExecutiveController.php | 271 + .../Controllers/V1/PropertyFactController.php | 183 + .../V1/PropertyFactMappingController.php | 165 + .../V1/PropertyNonrefundableController.php | 130 + .../V1/PropertyOfferController.php | 1073 + .../PropertyPersonPricingPolicyController.php | 431 + .../V1/PropertyPhotoCategoryController.php | 76 + .../V1/PropertyPhotoController.php | 381 + .../V1/PropertyPlaceController.php | 520 + .../V1/PropertyPromotionController.php | 264 + .../V1/PropertyQuickPricingController.php | 490 + .../V1/PropertyRoomBedController.php | 119 + .../V1/PropertyRoomBedTypeController.php | 74 + .../Controllers/V1/PropertyRoomController.php | 665 + .../V1/PropertyRoomFactMappingController.php | 135 + .../V1/PropertyRoomPhotoMappingController.php | 109 + ...opertyRoomRateChannelMappingController.php | 177 + .../V1/PropertyRoomRateController.php | 527 + ...ertyRoomRateInclusionMappingController.php | 124 + .../V1/PropertyRoomRateMappingController.php | 1630 ++ ...PropertyRoomRateMappingSetupController.php | 124 + .../V1/PropertyRoomRatePriceController.php | 217 + .../V1/PropertyRoomSizeTypeController.php | 60 + .../V1/PropertyRoomTypeController.php | 112 + .../V1/PropertyRoomViewTypeController.php | 66 + .../Controllers/V1/PropertyTypeController.php | 72 + .../V1/PropertyWebComponentController.php | 250 + .../V1/PropertyWebContentController.php | 296 + .../Controllers/V1/PropertyWebController.php | 2418 ++ .../V1/PropertyWebPopupController.php | 255 + .../V1/ReputationManagementController.php | 570 + .../Controllers/V1/TempPropertyController.php | 67 + .../Controllers/bulutController.backup.php | 364 + app/Http/Controllers/bulutController.php | 1079 + .../Controllers/propertyInsertController.php | 1786 ++ app/Http/Middleware/Authenticate.php | 44 + .../BookingEngineTokenMiddleware.php | 168 + ...eckPropertyChannelConnectionMiddleware.php | 58 + .../Middleware/ContentWizardMiddleware.php | 56 + app/Http/Middleware/CorsMiddleware.php | 35 + app/Http/Middleware/ExampleMiddleware.php | 20 + app/Http/Middleware/JwtMiddleware.php | 70 + .../Middleware/LanguageSettingMiddleware.php | 27 + app/Http/Middleware/MyWebTokenMiddleware.php | 67 + app/Http/Middleware/PropertyMiddleware.php | 93 + .../UserRoutePermissionAuthorize.php | 45 + app/Jobs/ExampleJob.php | 26 + app/Jobs/Job.php | 24 + app/Jobs/PropertyCatalogServiceJob.php | 37 + app/Jobs/PropertyReviewAnalyzeServiceJob.php | 32 + app/Jobs/PropertyReviewServiceJob.php | 35 + app/Jobs/SlackLogJob.php | 52 + app/Listeners/ExampleListener.php | 31 + app/Models/ApiAccessToken.php | 44 + app/Models/AwardsCertificateCategory.php | 39 + app/Models/BaseModel.php | 113 + app/Models/Booking.php | 163 + app/Models/BookingAddon.php | 38 + app/Models/BookingContact.php | 46 + app/Models/BookingPayment.php | 34 + app/Models/BookingPaymentData.php | 35 + app/Models/BookingPaymentDataCheck.php | 30 + app/Models/BookingRoom.php | 42 + app/Models/BookingRoomPax.php | 34 + app/Models/BookingStatus.php | 22 + app/Models/BookingTicket.php | 40 + app/Models/ChannelManager.php | 20 + app/Models/ChannelManagerBooking.php | 41 + app/Models/ChannelManagerLog.php | 20 + app/Models/ChannelManagerMapping.php | 25 + app/Models/ChannelManagerPropertyMapping.php | 29 + .../ChannelManagerPropertyRateMapping.php | 24 + app/Models/Country.php | 18 + app/Models/Currency.php | 21 + app/Models/CurrencyRates.php | 21 + app/Models/Destination.php | 18 + app/Models/FailedJobs.php | 25 + app/Models/GeneralTimezone.php | 33 + app/Models/IpNation.php | 30 + app/Models/IpNationCountries.php | 25 + app/Models/Jobs.php | 27 + app/Models/Language.php | 30 + app/Models/LanguageBase.php | 30 + app/Models/LanguageTranslate.php | 30 + app/Models/Offer.php | 130 + app/Models/OfferAcceptStatus.php | 30 + app/Models/OfferAccommodationMapping.php | 49 + app/Models/OfferCancellationPolicy.php | 33 + app/Models/OfferConfirmType.php | 32 + app/Models/OfferContactMapping.php | 39 + app/Models/OfferFactMapping.php | 36 + app/Models/OfferImportantNotes.php | 33 + app/Models/OfferPaymentType.php | 33 + app/Models/OfferPhotoMapping.php | 38 + app/Models/OfferPrice.php | 33 + app/Models/OfferRoomMapping.php | 57 + app/Models/PaymentBinNumber.php | 29 + app/Models/PaymentTransaction.php | 102 + app/Models/PaymentTransactionStatus.php | 30 + app/Models/PaymentType.php | 38 + app/Models/Permission.php | 36 + app/Models/PermissionGroup.php | 40 + app/Models/PermissionGroupMapping.php | 38 + app/Models/PermissionGroupUserMapping.php | 88 + app/Models/PermissionMenuModel.php | 79 + app/Models/PhotoGoogleLabel.php | 41 + app/Models/PlaceCategoryField.php | 42 + app/Models/PlaceCategoryFieldMapping.php | 48 + app/Models/PlaceCategoryFieldOption.php | 33 + .../PlaceCategoryFieldOptionFieldMapping.php | 41 + app/Models/Product.php | 30 + app/Models/ProductParameter.php | 28 + app/Models/ProductParameterMapping.php | 33 + app/Models/PromotionType.php | 37 + app/Models/Property.php | 202 + app/Models/PropertyAdditionalInfo.php | 26 + app/Models/PropertyAdditionalInfoKey.php | 24 + app/Models/PropertyAddon.php | 36 + app/Models/PropertyAvailabilityType.php | 30 + app/Models/PropertyAwardsCertificate.php | 39 + app/Models/PropertyBookingEngine.php | 76 + app/Models/PropertyBookingEngineSearch.php | 30 + app/Models/PropertyBookingPaymentType.php | 30 + app/Models/PropertyBookingType.php | 30 + app/Models/PropertyBrand.php | 44 + app/Models/PropertyCancellationPolicy.php | 21 + app/Models/PropertyChain.php | 34 + app/Models/PropertyChannel.php | 59 + app/Models/PropertyChannelAddon.php | 37 + .../PropertyChannelBookingPaymentSetup.php | 45 + app/Models/PropertyChannelCategory.php | 33 + app/Models/PropertyChannelContact.php | 31 + app/Models/PropertyChannelCoupon.php | 22 + app/Models/PropertyChannelGroup.php | 32 + .../PropertyChannelGroupChannelMapping.php | 32 + app/Models/PropertyChannelMapping.php | 74 + ...annelRoomRateCancellationPolicyMapping.php | 25 + ...annelRoomRatePricingPolicyAdultMapping.php | 40 + ...annelRoomRatePricingPolicyChildMapping.php | 41 + app/Models/PropertyChannelTax.php | 33 + app/Models/PropertyCompetitorGroup.php | 31 + app/Models/PropertyCompetitorGroupMapping.php | 22 + app/Models/PropertyCompetitorMapping.php | 28 + app/Models/PropertyConfig.php | 29 + app/Models/PropertyContact.php | 138 + app/Models/PropertyContent.php | 36 + app/Models/PropertyContentCategory.php | 35 + app/Models/PropertyExecutive.php | 98 + app/Models/PropertyExecutiveType.php | 33 + app/Models/PropertyFact.php | 37 + app/Models/PropertyFactAttribute.php | 33 + app/Models/PropertyFactMapping.php | 45 + app/Models/PropertyGroup.php | 24 + app/Models/PropertyGroupMapping.php | 29 + app/Models/PropertyInvoice.php | 18 + app/Models/PropertyLanguageSpoken.php | 46 + app/Models/PropertyMeta.php | 35 + app/Models/PropertyMetaRoomRate.php | 22 + app/Models/PropertyMetaRoomRateMapping.php | 26 + app/Models/PropertyMetaRoomRatePrice.php | 22 + app/Models/PropertyModule.php | 33 + app/Models/PropertyModuleMapping.php | 36 + app/Models/PropertyNonrefundable.php | 45 + app/Models/PropertyPaymentInstallment.php | 30 + app/Models/PropertyPaymentMapping.php | 47 + app/Models/PropertyPhoto.php | 69 + app/Models/PropertyPhotoCategory.php | 37 + .../PropertyPhotoCategoryLabelMapping.php | 35 + app/Models/PropertyPlace.php | 84 + app/Models/PropertyPlaceCategory.php | 34 + .../PropertyPlaceCategoryFieldValue.php | 49 + app/Models/PropertyPlaceFact.php | 33 + app/Models/PropertyPlaceFactMapping.php | 40 + app/Models/PropertyPlaceFactTitle.php | 33 + .../PropertyPlaceFactTitleFactMapping.php | 53 + app/Models/PropertyPlacePhotoMapping.php | 44 + app/Models/PropertyPlaceWorkingHour.php | 34 + app/Models/PropertyPriceComparison.php | 43 + app/Models/PropertyPricingPolicyAdult.php | 40 + app/Models/PropertyPricingPolicyChild.php | 40 + app/Models/PropertyProductMapping.php | 38 + app/Models/PropertyProductOffer.php | 50 + app/Models/PropertyPromotion.php | 55 + app/Models/PropertyPromotionMapping.php | 43 + app/Models/PropertyQuickPricingMapping.php | 33 + app/Models/PropertyReview.php | 51 + app/Models/PropertyReviewCategory.php | 19 + app/Models/PropertyReviewCategoryMapping.php | 35 + app/Models/PropertyReviewChannel.php | 35 + app/Models/PropertyReviewChannelMapping.php | 42 + app/Models/PropertyReviewFetchStatus.php | 20 + app/Models/PropertyReviewKeywordMapping.php | 19 + app/Models/PropertyRoom.php | 97 + app/Models/PropertyRoomAvailability.php | 37 + app/Models/PropertyRoomAvailabilityQueue.php | 42 + app/Models/PropertyRoomBed.php | 41 + app/Models/PropertyRoomBedType.php | 39 + app/Models/PropertyRoomConnected.php | 33 + app/Models/PropertyRoomFactMapping.php | 47 + app/Models/PropertyRoomPhotoMapping.php | 43 + app/Models/PropertyRoomPricingType.php | 20 + app/Models/PropertyRoomRate.php | 50 + app/Models/PropertyRoomRateChannelMapping.php | 68 + .../PropertyRoomRateInclusionMapping.php | 38 + app/Models/PropertyRoomRateMapping.php | 55 + app/Models/PropertyRoomRateMappingSetup.php | 34 + app/Models/PropertyRoomRatePrice.php | 37 + app/Models/PropertyRoomRatePriceQueue.php | 42 + app/Models/PropertyRoomSizeType.php | 18 + app/Models/PropertyRoomType.php | 32 + app/Models/PropertyRoomViewMapping.php | 37 + app/Models/PropertyRoomViewType.php | 32 + app/Models/PropertyStatus.php | 33 + app/Models/PropertySummary.php | 18 + app/Models/PropertyType.php | 32 + app/Models/PropertyUnit.php | 33 + app/Models/PropertyWeb.php | 154 + app/Models/PropertyWebAboutUs.php | 24 + app/Models/PropertyWebColorMapping.php | 20 + app/Models/PropertyWebComponent.php | 36 + app/Models/PropertyWebComponentMapping.php | 45 + app/Models/PropertyWebContent.php | 60 + app/Models/PropertyWebContentCategory.php | 29 + app/Models/PropertyWebGroup.php | 56 + app/Models/PropertyWebLanguageMapping.php | 20 + app/Models/PropertyWebLog.php | 25 + app/Models/PropertyWebMenu.php | 20 + app/Models/PropertyWebMenuMapping.php | 25 + app/Models/PropertyWebMeta.php | 26 + app/Models/PropertyWebMetaMapping.php | 52 + app/Models/PropertyWebMetaTag.php | 26 + app/Models/PropertyWebPhotoMapping.php | 38 + app/Models/PropertyWebPlaceMapping.php | 24 + app/Models/PropertyWebPopup.php | 55 + app/Models/PropertyWebReview.php | 39 + app/Models/PropertyWebRoomMapping.php | 24 + app/Models/PropertyWebSetup.php | 19 + app/Models/PropertyWebTemplate.php | 32 + app/Models/PropertyWebWeather.php | 48 + app/Models/ServiceLog.php | 28 + app/Models/SiteConfig.php | 29 + app/Models/TempProperty.php | 28 + app/Models/User.php | 44 + app/Models/UserPropertyMapping.php | 40 + app/Models/VWDestination.php | 29 + app/Models/vwActiveProperty.php | 28 + app/Models/vwBookingEngineSearch.php | 28 + app/Models/vwBookingSummary.php | 29 + app/Models/vwBookingSummaryAll.php | 34 + .../DirectPushNotificationUser.php | 80 + app/Notifications/NewUserNotification.php | 50 + .../PushNotificationPropertyUser.php | 91 + app/Providers/AppServiceProvider.php | 20 + app/Providers/AuthServiceProvider.php | 39 + app/Providers/EventServiceProvider.php | 19 + artisan | 35 + bootstrap/app.php | 133 + composer.json | 60 + config/app.php | 53 + config/cache.php | 101 + config/database.php | 135 + config/filesystems.php | 69 + config/language-pack-de.php | 90 + config/language-pack-en.php | 91 + config/language-pack-tr.php | 90 + config/languageSupport.php | 6 + config/mail.php | 125 + config/queue.php | 86 + database/factories/ModelFactory.php | 21 + database/migrations/.gitkeep | 0 .../2019_09_17_103201_create_user_table.php | 51 + ...019_09_17_103202_create_language_table.php | 68 + ..._17_103203_create_property_chain_table.php | 42 + ...019_09_17_103204_create_currency_table.php | 66 + ...9_17_103205_create_property_type_table.php | 64 + ...019_09_17_103610_create_property_table.php | 55 + ...9_17_110258_create_temp_property_table.php | 41 + ...431_create_user_property_mapping_table.php | 46 + ...create_property_content_category_table.php | 65 + ...6_123443_create_property_content_table.php | 52 + ...9_30_130301_create_property_fact_table.php | 68 + ...9_30_130303_create_property_unit_table.php | 60 + ...4_create_property_fact_attribute_table.php | 69 + ..._property_fact_attribute_mapping_table.php | 41 + ...133_create_property_fact_mapping_table.php | 47 + ...2_111700_create_property_contact_table.php | 56 + ...9_create_property_executive_type_table.php | 64 + ...120724_create_property_executive_table.php | 53 + ...ate_property_additional_info_key_table.php | 63 + ..._create_property_additional_info_table.php | 46 + ..._07_141400_create_property_photo_table.php | 135 + ...11_121418_create_property_config_table.php | 46 + ...14_111402_create_property_module_table.php | 64 + ...9_10_18_102754_create_permission_table.php | 133 + ...6_103124_create_api_access_token_table.php | 40 + ...2019_11_14_174036_add_hash_key_to_user.php | 34 + .../2019_11_15_151514_create_jobs_table.php | 47 + ..._12_18_104857_create_site_config_table.php | 45 + ...101312_create_property_room_type_table.php | 68 + ...2_24_162318_create_property_room_table.php | 50 + ...49_create_property_room_bed_type_table.php | 58 + ..._094857_create_property_room_bed_table.php | 45 + ...create_property_channel_category_table.php | 59 + ...7_135558_create_property_channel_table.php | 47 + ..._create_property_channel_mapping_table.php | 44 + ...160355_create_property_room_rate_table.php | 45 + ...reate_property_room_rate_mapping_table.php | 50 + ...erty_room_rate_inclusion_mapping_table.php | 43 + ...operty_room_rate_channel_mapping_table.php | 44 + ...property_room_rate_mapping_setup_table.php | 47 + ..._create_property_room_rate_price_table.php | 57 + ...reate_property_room_availability_table.php | 47 + ...454_add_max_stay_to_property_room_rate.php | 32 + ...5_165436_add_image_to_property_channel.php | 32 + ...5243_drop_unique_to_property_executive.php | 44 + ...113758_add_mobile2_to_property_contact.php | 36 + ...al_media_addresses_to_property_contact.php | 32 + .../2020_05_18_154634_create_offer_table.php | 236 + ...reate_property_room_fact_mapping_table.php | 43 + ...eate_property_room_photo_mapping_table.php | 48 + ..._28_203313_create_property_brand_table.php | 48 + ...ffer_accommodation_mapping_date_format.php | 44 + ...4115_add_column_offer_table_offer_code.php | 23 + ...olumn_offer_table_accept_status_column.php | 24 + ..._is_cover_photo_to_offer_photo_mapping.php | 34 + ..._table_status_and_accept_status_fields.php | 31 + ...erty_type_id_name_official_name_fields.php | 30 + ...13428_update_property_fact_table_icons.php | 76 + ..._column_from_offer_cancellation_policy.php | 34 + .../2020_06_16_171417_update_fact_order.php | 46 + .../2020_06_17_145049_update_fact_parent.php | 46 + ...2020_06_17_150913_create_country_table.php | 37 + ...06_18_103740_update_property_fact_data.php | 36 + ...20_06_18_132804_country_table_add_data.php | 272 + ...rop_column_checkin_from_property_table.php | 60 + ...dd_column_property_table_country_field.php | 26 + ..._add_column_country_table_status_field.php | 33 + ...9_093503_change_columns_property_table.php | 38 + ...tension_field_property_executive_table.php | 33 + ...06_19_105603_add_columns_to_user_table.php | 35 + ...22_143629_drop_table_destination_table.php | 28 + ..._property_room_exclude_occupancy_field.php | 23 + ...hange_columns_property_executive_table.php | 39 + ...10_change_columns_property_brand_table.php | 34 + ...nge_table_language_table_create_values.php | 309 + ...d_data_to_property_additional_info_key.php | 48 + ..._06_30_171839_add_column_room_bed_type.php | 32 + ...ve_contact_name_and_property_id_fields.php | 34 + ...6_30_182422_update_data_to_site_config.php | 37 + .../2020_07_02_092052_drop_locale_tables.php | 45 + ...7_03_143603_add_language_key_to_tables.php | 75 + ..._create_table_property_room_view_table.php | 42 + ...reate_property_room_view_mapping_table.php | 45 + ...07_134958_add_data_property_type_table.php | 327 + ...3007_add_data_property_room_type_table.php | 255 + ..._add_data_property_room_bed_type_table.php | 145 + ...07_07_145651_create_property_web_table.php | 73 + ...add_data_property_executive_type_table.php | 119 + ..._clear_unique_property_executive_table.php | 30 + ..._08_114601_change_property_chain_table.php | 3378 +++ ...d_language_translate_clear_foreingkeys.php | 37 + ...ate_data_property_room_view_type_table.php | 104 + ...dd_title_language_key_to_property_fact.php | 33 + ...95749_truncate_related_property_tables.php | 68 + ...48_add_icon_to_property_executive_type.php | 34 + ...7_24_143309_add_language_key_to_tables.php | 30 + ...commodation_type_to_property_room_rate.php | 35 + ..._111815_add_room_size_to_property_room.php | 25 + ...4333_create_property_web_photo_mapping.php | 45 + ...nation_id_column_from_property_contact.php | 31 + ..._published_dns_checked_to_property_web.php | 34 + ...p_and_add_columns_property_brand_table.php | 31 + ..._20_102828_create_table_room_size_type.php | 51 + ..._room_size_type_on_property_room_table.php | 31 + ...4_create_property_booking_payment_type.php | 94 + ...9_add_same_colmuns_to_property_channel.php | 32 + ...ndent_data_update_property_chain_table.php | 28 + ...8_26_174248_update_siteconfig_20200826.php | 32 + ..._27_173249_update_site_config_20200827.php | 32 + ...roperty_channel_mapping_setup_20200828.php | 38 + ...30_create_property_nonrefundable_table.php | 49 + ..._to_property_room_rate_channel_mapping.php | 38 + ...30_delete_descption_from_property_room.php | 32 + ...0026_create_property_availability_type.php | 53 + ...020_09_03_104816_edit_property_channel.php | 50 + ...cted_to_property_channel_mapping_setup.php | 24 + ...450_change_title_column_property_brand.php | 24 + ...55_update_property_executive_type_data.php | 21 + ..._09_09_110916_add_ticked_code_to_offer.php | 39 + ..._edit_property_channel_currency_column.php | 38 + ...rency_code_to_property_channel_mapping.php | 39 + ..._09_14_112835_inventroy_columns_design.php | 45 + ...45608_add_description_to_property_room.php | 25 + ...09_15_092617_redesign_property_content.php | 35 + ..._150003_add_file_ext_to_property_brand.php | 25 + ..._17_112959_add_column_property_channel.php | 32 + ...1723_add_column_checkin_checkout_offer.php | 30 + ...20_09_21_105331_drop_is_manually_input.php | 26 + ...ate_property_cancellation_policy_table.php | 46 + ..._to_property_room_rate_channel_mapping.php | 28 + ...ty_cancellation_policy_channel_mapping.php | 45 + ...reate_property_room_pricing_type_table.php | 50 + ...8_add_columns_property_channel_mapping.php | 45 + ...ate_property_cancellation_policy_setup.php | 62 + ...ty_channel_booking_payment_setup_table.php | 51 + ..._to_property_cancellation_policy_setup.php | 28 + ...p_property_channel_mapping_setup_table.php | 29 + ...1744_update_property_room_pricing_type.php | 29 + ...drop_default_sell_to_room_rate_mapping.php | 26 + ...add_column_and_data_icon_channel_setup.php | 43 + ...olumn_property_room_rate_mapping_table.php | 20 + ...rty_room_availability_channel_nullable.php | 24 + ...stay_max_stay_property_room_rate_table.php | 22 + ...020_10_02_100536_person_pricing_module.php | 114 + ...update_column_data_property_fact_table.php | 21 + ...08_set_property_room_pricing_type_code.php | 22 + ...operty_cancellation_policy_setup_merge.php | 46 + ...perty_cancellation_policy_effect_price.php | 28 + ...perty_pricing_policy_adult_refactoring.php | 44 + ...g_payment_setup_add_channel_mapping_id.php | 43 + ...up_table_and_cancellation_policy_table.php | 24 + ..._10_16_161510_booking_engine_migration.php | 43 + ..._update_fact_atrribute_and_unit_tables.php | 37 + ..._column_name_cancellation_policy_table.php | 37 + ...22_151625_property_room_add_room_count.php | 26 + ...020_10_27_101720_create_booking_tables.php | 155 + ...020_11_02_101613_booking_relationships.php | 187 + ...1_02_133050_update_property_fact_table.php | 27 + ..._room_size_type_on_property_room_table.php | 21 + ..._add_column_property_room_fact_mapping.php | 21 + ...ith_myweb_slider_property_photos_table.php | 28 + ...dd_column_languages_property_web_table.php | 29 + ..._102122_create_table_property_web_menu.php | 60 + ...olumn_and_data_property_web_menu_table.php | 28 + ...131542_create_table_property_web_setup.php | 41 + ...olumn_and_data_property_web_menu_table.php | 28 + ...poerty_room_add_room_type_count_column.php | 32 + ...738_property_web_menu_add_alias_column.php | 36 + ...mapping_web_color_mapping_menu_sorting.php | 62 + ...21_drop_table_property_web_setup_table.php | 29 + ...group_and_property_group_mapping_table.php | 62 + ...t_typeto_property_pricing_policy_adult.php | 28 + ...21_create_table_property_web_log_table.php | 42 + ...column_property_web_log_table_ip_field.php | 23 + ...151737_add_installment_to_payment_type.php | 24 + ...olumn_pricing_policy_tables_name_field.php | 26 + ...4_130930_add_column_property_web_table.php | 21 + .../2020_12_15_134317_create_table_places.php | 260 + ...erty_room_rate_table_description_field.php | 28 + ...m_rate_mapping_table_description_field.php | 22 + ...08_create_property_place_photo_mapping.php | 43 + ...9_delete_property_place_uniqiue_fields.php | 28 + ...2_171855_update_site_config_gor_places.php | 33 + ...2_175505_payment_type_change_name_data.php | 58 + ...3_add_language_code_to_booking_contact.php | 24 + ...eate_table_property_web_about_us_table.php | 40 + ...30747_create_property_place_fact_title.php | 104 + ...332_create_property_place_fact_mapping.php | 50 + ..._05_174138_create_place_category_field.php | 149 + ...t_place_title_fact_mapping_data_create.php | 27 + ...3020_update_data_place_fact_icon_field.php | 28 + ...ify_column_property_web_about_us_value.php | 29 + ...5030_property_addtional_info_add_key01.php | 24 + ...1_12_145625_fill_place_category_fields.php | 33 + ...953_place_category_field_update_data01.php | 22 + ...35853_place_category_field_mapping_001.php | 32 + ...1_01_15_210010_property_web_migrations.php | 41 + ...01_19_102919_add_additioal_info_key_02.php | 24 + ...1_01_20_141411_add_data_web_menu_table.php | 29 + ...3_125159_create_table_general_timezone.php | 38 + ...1_23_132652_add_additional_info_key_03.php | 54 + ...3_165950_update_data_property_web_menu.php | 22 + ...2021_01_25_101757_property_unit_fixess.php | 37 + ...141158_update_data_property_fact_table.php | 24 + ...01_26_164106_property_place_add_fields.php | 32 + ...1_27_090330_add_additional_info_key_04.php | 29 + ...01_29_120411_awards_certificate_tables.php | 79 + ..._add_max_child_number_to_property_room.php | 32 + ...2416_add_fields_to_payment_transaction.php | 38 + ...01_144004_update_data_general_timezone.php | 24 + ...nt_transaction_type_colum_set_not_null.php | 35 + ...02_03_144403_update_date_property_fact.php | 28 + ...184742_update_data_property_place_fact.php | 23 + ...551_create_data_certificate_and_awards.php | 107 + ...1_02_10_095958_update_data_site_config.php | 27 + ...28_property_payment_transaction_status.php | 57 + ...02_12_094207_property_promotion_tables.php | 118 + ...2_16_130830_update_data_promotion_type.php | 23 + ...add_created_by_to_payment_transactions.php | 35 + ...021_02_20_125220_add_column_user_table.php | 23 + ...1_125017_add_data_promotion_type_table.php | 24 + ...1_03_03_163031_add_data_currency_table.php | 53 + ...columns_property_channel_mapping_table.php | 25 + ...e_property_room_rate_price_queue_table.php | 42 + ..._15_114954_create_booking_status_table.php | 51 + ..._key_property_addtional_info_key_table.php | 25 + ...172703_update_icon_property_fact_table.php | 29 + .../2021_03_31_134507_add_kvkk_keys.php | 34 + database/migrations/archive.zip | Bin 0 -> 351314 bytes database/seeds/DatabaseSeeder.php | 26 + database/seeds/DestinationTableSeeder.php | 34 + database/seeds/FirstRunSeeder.php | 775 + .../PropertyAdditionalInfoKeyLocaleSeeder.php | 60 + .../seeds/PropertyAdditionalInfoKeySeeder.php | 53 + database/seeds/PropertyChainSeeder.php | 133 + database/seeds/PropertyExecuteSeeder.php | 42 + .../seeds/PropertyExecuteTypeLocaleSeeder.php | 83 + database/seeds/PropertyExecuteTypeSeeder.php | 73 + database/seeds/PropertyFactSeeder.php | 5623 +++++ database/seeds/PropertyGoogleLabelSeeder.php | 123 + database/seeds/PropertyPhotoSeeder.php | 263 + database/seeds/PropertyTypeSeeder.php | 153 + database/seeds/UserTableSeeder.php | 54 + docker-compose.yml | 58 + php | 0 phpunit.xml | 26 + public/.htaccess | 21 + .../2026/1778058432_BU4ZCOX3BL.jpg" | Bin 0 -> 63842 bytes .../2026/1778058432_BU4ZCOX3BL_medium.jpg" | Bin 0 -> 47416 bytes .../2026/1778058432_BU4ZCOX3BL_thumbnail.jpg" | Bin 0 -> 13272 bytes .../2026/1778058498_KXQFEAR72N.jpg" | Bin 0 -> 63842 bytes .../2026/1778058498_KXQFEAR72N_medium.jpg" | Bin 0 -> 47416 bytes .../2026/1778058498_KXQFEAR72N_thumbnail.jpg" | Bin 0 -> 13272 bytes .../2026/1778058692_2PTNCQ6D0Y.jpg" | Bin 0 -> 63842 bytes .../2026/1778058692_2PTNCQ6D0Y_medium.jpg" | Bin 0 -> 47416 bytes .../2026/1778058692_2PTNCQ6D0Y_thumbnail.jpg" | Bin 0 -> 13272 bytes .../2026/1778058726_N4JQ1CR3DD.jpg" | Bin 0 -> 63842 bytes .../2026/1778058726_N4JQ1CR3DD_medium.jpg" | Bin 0 -> 47416 bytes .../2026/1778058726_N4JQ1CR3DD_thumbnail.jpg" | Bin 0 -> 13272 bytes .../2026/1778058727_PC7D0HUQU7.jpg" | Bin 0 -> 63842 bytes .../2026/1778058727_PC7D0HUQU7_medium.jpg" | Bin 0 -> 47416 bytes .../2026/1778058727_PC7D0HUQU7_thumbnail.jpg" | Bin 0 -> 13272 bytes .../2026/1778058728_HPCDZ6OGKO.jpg" | Bin 0 -> 63842 bytes .../2026/1778058728_HPCDZ6OGKO_medium.jpg" | Bin 0 -> 47416 bytes .../2026/1778058728_HPCDZ6OGKO_thumbnail.jpg" | Bin 0 -> 13272 bytes .../2026/1778058729_8Z63Y323LE.jpg" | Bin 0 -> 63842 bytes .../2026/1778058729_8Z63Y323LE_medium.jpg" | Bin 0 -> 47416 bytes .../2026/1778058729_8Z63Y323LE_thumbnail.jpg" | Bin 0 -> 13272 bytes .../2026/1778058729_U1CXBVYIWS.jpg" | Bin 0 -> 63842 bytes .../2026/1778058729_U1CXBVYIWS_medium.jpg" | Bin 0 -> 47416 bytes .../2026/1778058729_U1CXBVYIWS_thumbnail.jpg" | Bin 0 -> 13272 bytes .../2026/1778058964_HVHBI7CM27.jpg" | Bin 0 -> 63842 bytes .../2026/1778058964_HVHBI7CM27_medium.jpg" | Bin 0 -> 47416 bytes .../2026/1778058964_HVHBI7CM27_thumbnail.jpg" | Bin 0 -> 13272 bytes .../1778057851_IMD365C7C8.pdf" | Bin 0 -> 31687 bytes .../2026/logo/1778057791_250x250.png" | Bin 0 -> 6497 bytes .../2026/logo/1778057791_750x750.png" | Bin 0 -> 21739 bytes public/assets/css/bootstrap.css | 8070 ++++++ public/assets/css/load.css | 25 + public/assets/fav.ico | Bin 0 -> 1150 bytes public/assets/favicon.ico | Bin 0 -> 3870 bytes public/assets/img/banner/shape/shape-1.png | Bin 0 -> 2439 bytes public/assets/img/banner/shape/shape-2.png | Bin 0 -> 681 bytes public/assets/img/banner/shape/shape-3.png | Bin 0 -> 1066 bytes public/assets/img/banner/shape/shape-4.png | Bin 0 -> 4187 bytes .../assets/img/banner/shape/shape-4_tmp19510 | Bin 0 -> 9792 bytes public/assets/img/logo-white.png | Bin 0 -> 1547 bytes public/assets/img/logo.svg | 16 + public/assets/img/main-image.png | Bin 0 -> 934566 bytes public/assets/js/config.js | 93 + public/assets/js/jquery-2.2.4.min.js | 4 + public/assets/manifest.json | 15 + public/docs.html | 113 + public/excel/.gitignore | 2 + public/img/background.jpg | Bin 0 -> 44817 bytes public/img/channel-icons/agodacom.png | Bin 0 -> 9048 bytes public/img/channel-icons/algotels.png | Bin 0 -> 7731 bytes public/img/channel-icons/bookingcom.png | Bin 0 -> 6417 bytes public/img/channel-icons/default.psd | Bin 0 -> 485717 bytes public/img/channel-icons/edreams.png | Bin 0 -> 8105 bytes public/img/channel-icons/expediacom.png | Bin 0 -> 7173 bytes public/img/channel-icons/google.png | Bin 0 -> 8721 bytes public/img/channel-icons/holidaycheck.png | Bin 0 -> 7647 bytes public/img/channel-icons/hopper.png | Bin 0 -> 15565 bytes public/img/channel-icons/hotelscom.png | Bin 0 -> 7812 bytes public/img/channel-icons/prestigiacom.png | Bin 0 -> 12215 bytes public/img/channel-icons/priceline.png | Bin 0 -> 6829 bytes public/img/channel-icons/stayforlong.png | Bin 0 -> 6950 bytes public/img/channel-icons/travelupcom.png | Bin 0 -> 11643 bytes public/img/channel-icons/tripadvisor.png | Bin 0 -> 10556 bytes public/img/channel-icons/tripcom.png | Bin 0 -> 5947 bytes public/img/channel-icons/trivago.png | Bin 0 -> 10049 bytes public/img/channel-icons/viocom.png | Bin 0 -> 8254 bytes public/img/channel-icons/zenhotelscom.png | Bin 0 -> 9234 bytes public/img/component-icons/googleads.png | Bin 0 -> 23402 bytes .../img/component-icons/googleanalytics.png | Bin 0 -> 30270 bytes public/img/component-icons/googlereview.png | Bin 0 -> 73916 bytes .../googlesiteverification.png | Bin 0 -> 40545 bytes .../img/component-icons/googletagmanager.png | Bin 0 -> 22243 bytes public/img/component-icons/hotjar.png | Bin 0 -> 11638 bytes public/img/component-icons/jotform.png | Bin 0 -> 16781 bytes public/img/component-icons/metapixel.png | Bin 0 -> 37205 bytes .../img/component-icons/microsoftclarity.png | Bin 0 -> 49028 bytes public/img/component-icons/sojern.png | Bin 0 -> 14108 bytes public/img/component-icons/tawkto.png | Bin 0 -> 20628 bytes public/img/component-icons/template.psd | Bin 0 -> 586404 bytes public/img/logo.png | Bin 0 -> 23873 bytes public/img/logo_small.png | Bin 0 -> 7696 bytes public/img/noise.gif | Bin 0 -> 38442 bytes public/img/redirect-bank.png | Bin 0 -> 26266 bytes public/img/redirecting.gif | Bin 0 -> 4800 bytes .../weather-icons-monochrome/clear-day.png | Bin 0 -> 1177 bytes .../weather-icons-monochrome/clear-night.png | Bin 0 -> 758 bytes .../img/weather-icons-monochrome/cloudy.png | Bin 0 -> 644 bytes public/img/weather-icons-monochrome/fog.png | Bin 0 -> 840 bytes public/img/weather-icons-monochrome/hail.png | Bin 0 -> 1050 bytes .../partly-cloudy-day.png | Bin 0 -> 1031 bytes .../partly-cloudy-night.png | Bin 0 -> 879 bytes .../rain-snow-showers-day.png | Bin 0 -> 1364 bytes .../rain-snow-showers-night.png | Bin 0 -> 1408 bytes .../weather-icons-monochrome/rain-snow.png | Bin 0 -> 1155 bytes public/img/weather-icons-monochrome/rain.png | Bin 0 -> 940 bytes .../weather-icons-monochrome/showers-day.png | Bin 0 -> 1347 bytes .../showers-night.png | Bin 0 -> 1237 bytes public/img/weather-icons-monochrome/sleet.png | Bin 0 -> 1083 bytes .../snow-showers-day.png | Bin 0 -> 1427 bytes .../snow-showers-night.png | Bin 0 -> 1435 bytes public/img/weather-icons-monochrome/snow.png | Bin 0 -> 1206 bytes .../weather-icons-monochrome/thunder-rain.png | Bin 0 -> 1182 bytes .../thunder-showers-day.png | Bin 0 -> 1413 bytes .../thunder-showers-night.png | Bin 0 -> 1408 bytes .../img/weather-icons-monochrome/thunder.png | Bin 0 -> 749 bytes public/img/weather-icons-monochrome/wind.png | Bin 0 -> 804 bytes public/img/weather-icons/clear-day.png | Bin 0 -> 1624 bytes public/img/weather-icons/clear-night.png | Bin 0 -> 1222 bytes public/img/weather-icons/cloudy.png | Bin 0 -> 970 bytes public/img/weather-icons/fog.png | Bin 0 -> 1036 bytes public/img/weather-icons/hail.png | Bin 0 -> 1286 bytes .../img/weather-icons/partly-cloudy-day.png | Bin 0 -> 2245 bytes .../img/weather-icons/partly-cloudy-night.png | Bin 0 -> 1355 bytes .../weather-icons/rain-snow-showers-day.png | Bin 0 -> 1724 bytes .../weather-icons/rain-snow-showers-night.png | Bin 0 -> 1667 bytes public/img/weather-icons/rain-snow.png | Bin 0 -> 1242 bytes public/img/weather-icons/rain.png | Bin 0 -> 1130 bytes public/img/weather-icons/showers-day.png | Bin 0 -> 1713 bytes public/img/weather-icons/showers-night.png | Bin 0 -> 1689 bytes public/img/weather-icons/sleet.png | Bin 0 -> 1436 bytes public/img/weather-icons/snow-showers-day.png | Bin 0 -> 1845 bytes .../img/weather-icons/snow-showers-night.png | Bin 0 -> 1759 bytes public/img/weather-icons/snow.png | Bin 0 -> 1352 bytes public/img/weather-icons/thunder-rain.png | Bin 0 -> 1313 bytes .../img/weather-icons/thunder-showers-day.png | Bin 0 -> 1738 bytes .../weather-icons/thunder-showers-night.png | Bin 0 -> 1817 bytes public/img/weather-icons/thunder.png | Bin 0 -> 1030 bytes public/img/weather-icons/wind.png | Bin 0 -> 1133 bytes public/index.php | 28 + public/openapi.yaml | 3718 +++ resources/data/AE.csv | 860 + resources/data/AT.csv | 1276 + resources/data/BE.csv | 498 + resources/data/BG.csv | 581 + resources/data/BR.csv | 3506 +++ resources/data/CA.csv | 1824 ++ resources/data/CH.csv | 870 + resources/data/DE.csv | 4628 ++++ resources/data/DK.csv | 259 + resources/data/EG.csv | 528 + resources/data/ES.csv | 8785 +++++++ resources/data/FI.csv | 221 + resources/data/FR.csv | 6592 +++++ resources/data/IT.csv | 11130 +++++++++ resources/data/LU.csv | 41 + resources/data/MV.csv | 222 + resources/data/MX.csv | 3055 +++ resources/data/NL.csv | 735 + resources/data/PR.csv | 66 + resources/data/SE.csv | 515 + resources/data/TR-AYT.csv | 835 + resources/data/TR.csv | 4359 ++++ resources/data/UK.csv | 4132 ++++ resources/data/US.csv | 20197 ++++++++++++++++ resources/data/data.json | 4232 ++++ resources/data/geography.xml | 2 + .../data/google-vision-authentication.json | 12 + resources/postman/api-enw-collection.json | 12200 ++++++++++ .../auth-login.postman_collection.json | 114 + .../postman/auth-user.postman_collection.json | 479 + resources/views/.gitkeep | 0 .../_propertyProductOfferMail.blade.php | 112 + .../emails/affiliateRequestMail.blade.php | 119 + .../bookingCancellationConfirmCode.blade.php | 106 + .../bookingCancellationRequest.blade.php | 103 + .../bookingEngineSearchReportMail.blade.php | 285 + .../emails/bookingInvoiceUpdateMail.blade.php | 101 + .../emails/bookingPaymentDataCode.blade.php | 106 + .../bookingPropertyAddonUpdateMail.blade.php | 101 + .../views/emails/bookingTicketMail.blade.php | 101 + .../views/emails/cancelBookingMail.blade.php | 386 + .../channelManagerNotificationMail.blade.php | 120 + .../views/emails/contactFormMail.blade.php | 118 + .../views/emails/createUserMail.blade.php | 95 + .../views/emails/dailyReportMail.blade.php | 368 + .../emails/dailyReportMailSales.blade.php | 271 + .../views/emails/enwContactFormMail.blade.php | 117 + .../emails/inventoryActionMail.blade.php | 160 + .../emails/inventoryPdfLinkMail.blade.php | 108 + resources/views/emails/logMail.blade.php | 116 + .../views/emails/manualPaymentMail.blade.php | 110 + .../emails/modifiedBookingMail.blade.php | 383 + .../views/emails/newBookingMail.blade.php | 443 + .../views/emails/offerAcceptMail.blade.php | 106 + resources/views/emails/offerMail.blade.php | 103 + .../offerPreConfirmCustomerMail.blade.php | 106 + .../offerPreConfirmPropertyMail.blade.php | 106 + .../emails/propertyCatalogMail.blade.php | 102 + .../emails/propertyProductOfferMail.blade.php | 114 + .../emails/reminder/trialFirstMail.blade.php | 204 + .../emails/reminder/trialSecondMail.blade.php | 191 + .../views/emails/userForgotPassword.blade.php | 103 + resources/views/emails/wellCome.blade.php | 13 + resources/views/errors/404.blade.php | 1 + resources/views/index.blade.php | 439 + .../views/pdf/PropertyProductOffer.blade.php | 192 + resources/views/pdf/hotelContent.blade.php | 678 + resources/views/pdf/inventory.blade.php | 745 + resources/views/pdf/priceComparison.blade.php | 92 + resources/views/pdf/propertyCatalog.blade.php | 1496 ++ resources/views/threeDSecureForm.blade.php | 10 + routes/web.php | 667 + storage/Tripadvisor.json | 1 + storage/agoda.json | 1 + storage/app/.gitignore | 2 + storage/check_rooms.php | 60 + storage/fact.log | 110 + storage/framework/cache/.gitignore | 3 + storage/google.json | 1 + storage/logs/.gitignore | 2 + storage/off_fact.log | 112 + storage/response.json | 1 + storage/tmp.csv | 340 + tests/ExampleTest.php | 21 + tests/TestCase.php | 14 + text.txt.php | 1 + todo.md | 35 + 1425 files changed, 284735 insertions(+) create mode 100644 .drone.yml create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .styleci.yml create mode 100644 API_FLOW.md create mode 100644 Dockerfile create mode 100644 PROJECT_SUMMARY.md create mode 100644 README.md create mode 100644 app/Channels/OneSignalChannel.php create mode 100644 app/Console/Commands/.gitkeep create mode 100644 app/Console/Commands/ApplicationLanguageFiles/ApplicationFillTableLanguageKey.php create mode 100644 app/Console/Commands/ApplicationLanguageFiles/ApplicationLanguageBaseData.php create mode 100644 app/Console/Commands/ApplicationLanguageFiles/ApplicationLanguageFiles.php create mode 100644 app/Console/Commands/ChannelManager/BestAvailableRateSyncService.php create mode 100644 app/Console/Commands/ChannelManager/MetaCancellationService.php create mode 100644 app/Console/Commands/ChannelManager/PropertyBookingSyncService.php create mode 100644 app/Console/Commands/ChannelManager/PropertyChannelSyncService.php create mode 100644 app/Console/Commands/ChannelManager/PropertyMetaRoomRatePriceService.php create mode 100644 app/Console/Commands/ChannelManager/PropertyMetaRoomRatePushService.php create mode 100644 app/Console/Commands/ChannelManager/PropertyMetaRoomRateService.php create mode 100644 app/Console/Commands/ChannelManager/RemovePaymentTokenService.php create mode 100644 app/Console/Commands/ChannelManager/ReservationPullService.php create mode 100644 app/Console/Commands/ChannelManager/ReservationPushService.php create mode 100644 app/Console/Commands/ChannelManager/RoomAvailabilityPushService.php create mode 100644 app/Console/Commands/ChannelManager/RoomRatePushService.php create mode 100644 app/Console/Commands/ChannelManager/_ReselivaAvailRateUpdateService.php create mode 100644 app/Console/Commands/ChannelManager/_ReservationPullService.php create mode 100644 app/Console/Commands/ChannelManager/_ReservationPushService.php create mode 100644 app/Console/Commands/ChannelManager/akgun.xlsx create mode 100644 app/Console/Commands/CurrencyRates/CurrencyRatesService.php create mode 100644 app/Console/Commands/Data/DashboardCacheService.php create mode 100644 app/Console/Commands/Data/HotelBedsList.php create mode 100644 app/Console/Commands/Data/WeatherService.php create mode 100644 app/Console/Commands/Google/GoogleReview.php create mode 100644 app/Console/Commands/Google/GoogleStaticMap.php create mode 100644 app/Console/Commands/Google/GoogleVisioLabel.php create mode 100644 app/Console/Commands/Jobs/BookingCommissionService.php create mode 100644 app/Console/Commands/Jobs/BookingEngineSearchReportService.php create mode 100644 app/Console/Commands/Jobs/DataFetch.php create mode 100644 app/Console/Commands/Jobs/MailJobs.php create mode 100644 app/Console/Commands/Jobs/PriceComparisonService.php create mode 100644 app/Console/Commands/Jobs/PropertyCatalogService.php create mode 100644 app/Console/Commands/Jobs/PropertyInvoiceService.php create mode 100644 app/Console/Commands/Jobs/PropertySummaryService.php create mode 100644 app/Console/Commands/Jobs/SummaryReportMail.php create mode 100644 app/Console/Commands/Jobs/SummaryReportMailSales.php create mode 100644 app/Console/Commands/PropertyReviewService/PropertyReviewAnalyzeService.php create mode 100644 app/Console/Commands/PropertyReviewService/PropertyReviewScheduleService.php create mode 100644 app/Console/Commands/PropertyReviewService/PropertyReviewService.php create mode 100644 app/Console/Commands/PropertyTrialCheck/PropertyTrialCheck.php create mode 100644 app/Console/Commands/PropertyTrialCheck/PropertyTrialMail.php create mode 100644 app/Console/Commands/PropertyWebCheckDns/PropertyWebCheckDns.php create mode 100644 app/Console/Commands/PropertyWebMenuMapping/PropertyWebMenuMappingConsole.php create mode 100644 app/Console/Kernel.php create mode 100644 app/Core/Contracts/MailInterface.php create mode 100644 app/Core/Helper/LanguageService.php create mode 100644 app/Core/Helper/PhpHelper.php create mode 100644 app/Core/Helper/compat_l5.php create mode 100644 app/Core/Helper/helpers.php create mode 100644 app/Core/Mail/AffiliateRequestMail.php create mode 100644 app/Core/Mail/BaseMail.php create mode 100644 app/Core/Mail/BookingCancellationConfirmCodeMail.php create mode 100644 app/Core/Mail/BookingCancellationRequestMail.php create mode 100644 app/Core/Mail/BookingEngineSearchReportMail.php create mode 100644 app/Core/Mail/BookingInvoiceUpdateMail.php create mode 100644 app/Core/Mail/BookingPaymentDataCodeMail.php create mode 100644 app/Core/Mail/BookingPropertyAddonUpdateMail.php create mode 100644 app/Core/Mail/BookingTicketMail.php create mode 100644 app/Core/Mail/CancelBookingMail.php create mode 100644 app/Core/Mail/ChannelManagerNotificationMail.php create mode 100644 app/Core/Mail/ContactFormMail.php create mode 100644 app/Core/Mail/DailyReportMail.php create mode 100644 app/Core/Mail/DailyReportMailSales.php create mode 100644 app/Core/Mail/EnwContactFormMail.php create mode 100644 app/Core/Mail/InventoryActionMail.php create mode 100644 app/Core/Mail/InventoryPdfLinkMail.php create mode 100644 app/Core/Mail/LogMail.php create mode 100644 app/Core/Mail/ManualPaymentMail.php create mode 100644 app/Core/Mail/ModifiedBookingMail.php create mode 100644 app/Core/Mail/NewBookingMail.php create mode 100644 app/Core/Mail/OfferAcceptMail.php create mode 100644 app/Core/Mail/OfferPreConfirmCustomerMail.php create mode 100644 app/Core/Mail/OfferPreConfirmPropertyMail.php create mode 100644 app/Core/Mail/OfferSendMail.php create mode 100644 app/Core/Mail/PropertyProductOfferMail.php create mode 100644 app/Core/Mail/TrialFirstMail.php create mode 100644 app/Core/Mail/TrialSecondMail.php create mode 100644 app/Core/Mail/UserCreateMail.php create mode 100644 app/Core/Mail/UserForgotPassword.php create mode 100644 app/Core/Mail/WellcomeMail.php create mode 100644 app/Core/Payment/BankOfGeorgia/BankOfGeorgia.php create mode 100644 app/Core/Payment/Esnekpos/Esnekpos.php create mode 100644 app/Core/Payment/HalkOde/HalkOde.php create mode 100644 app/Core/Payment/KuveytTurk/KuveytTurk.php create mode 100644 app/Core/Payment/Moka/Moka.php create mode 100644 app/Core/Payment/Payriff/Payriff.php create mode 100644 app/Core/Payment/Pos/AkPos.php create mode 100644 app/Core/Payment/Pos/EstPos.php create mode 100644 app/Core/Payment/Pos/EstPosV3.php create mode 100644 app/Core/Payment/Pos/Exceptions/BankClassNullException.php create mode 100644 app/Core/Payment/Pos/Exceptions/BankNotFoundException.php create mode 100644 app/Core/Payment/Pos/Exceptions/UnsupportedPaymentModelException.php create mode 100644 app/Core/Payment/Pos/Exceptions/UnsupportedTransactionTypeException.php create mode 100644 app/Core/Payment/Pos/GarantiPos.php create mode 100644 app/Core/Payment/Pos/Pos.php create mode 100644 app/Core/Payment/Pos/PosHelpersTrait.php create mode 100644 app/Core/Payment/Pos/PosInterface.php create mode 100644 app/Core/Payment/Pos/PosNet.php create mode 100644 app/Core/Payment/Pos/PosNetCrypt.php create mode 100644 app/Core/Payment/Pos/QPos.php create mode 100644 app/Core/Payment/Pos/VPos.php create mode 100644 app/Core/Payment/Pos/VkfPos.php create mode 100644 app/Core/Payment/Pos/config/pos.php create mode 100644 app/Core/Payment/Pos/ex/.gitignore create mode 100644 app/Core/Payment/Pos/ex/LICENCE.md create mode 100644 app/Core/Payment/Pos/ex/LICENSE.txt create mode 100644 app/Core/Payment/Pos/ex/README.md create mode 100644 app/Core/Payment/Pos/ex/composer.json create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/3d-pay/_config.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/3d-pay/form.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/3d-pay/index.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/3d-pay/response.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/3d/_config.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/3d/form.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/3d/index.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/3d/response.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/regular/_config.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/regular/cancel.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/regular/direct.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/regular/history.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/regular/index.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/regular/post.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/regular/refund.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/regular/response.php create mode 100644 app/Core/Payment/Pos/ex/examples/akbank/regular/status.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/3d-pay/_config.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/3d-pay/form.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/3d-pay/index.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/3d-pay/response.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/3d/_config.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/3d/form.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/3d/index.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/3d/response.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/regular/_config.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/regular/cancel.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/regular/direct.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/regular/history.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/regular/index.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/regular/post.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/regular/refund.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/regular/response.php create mode 100644 app/Core/Payment/Pos/ex/examples/garanti/regular/status.php create mode 100644 app/Core/Payment/Pos/ex/examples/template/_footer.php create mode 100644 app/Core/Payment/Pos/ex/examples/template/_header.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/3d/_config.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/3d/form.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/3d/index.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/3d/response.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/regular/_config.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/regular/cancel.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/regular/direct.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/regular/history.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/regular/index.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/regular/post.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/regular/refund.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/regular/response.php create mode 100644 app/Core/Payment/Pos/ex/examples/ykb/regular/status.php create mode 100644 app/Core/Payment/Pos/ex/phpunit.xml create mode 100644 app/Core/Payment/Pos/ex/tests/EstPostTest.php create mode 100644 app/Core/Payment/Pos/ex/tests/GarantiPosTest.php create mode 100644 app/Core/Payment/Pos/ex/tests/PosNetCryptTest.php create mode 100644 app/Core/Payment/Pos/ex/tests/PosNetTest.php create mode 100644 app/Core/Payment/Pos/ex/tests/PosTest.php create mode 100644 app/Core/Payment/QNBPay/QNBPay.php create mode 100644 app/Core/Payment/RetailPay/RetailPay.php create mode 100644 app/Core/Payment/Sipay/Sipay.php create mode 100644 app/Core/Payment/TBCBankGeorgia/TBCBankGeorgia.php create mode 100644 app/Core/Payment/WeePay/WeePay.php create mode 100644 app/Core/Permission/AbstractRelatedPermission.php create mode 100644 app/Core/Permission/RoutePermissionAuthorize.php create mode 100644 app/Core/Repository/ApiAccessToken/ApiAccessTokenRepository.php create mode 100644 app/Core/Repository/ApplicationCache/ApplicationCacheRepository.php create mode 100644 app/Core/Repository/AwardsCertificateCategory/AwardsCertificateCategoryRepository.php create mode 100644 app/Core/Repository/Booking/BookingAddonRepository.php create mode 100644 app/Core/Repository/Booking/BookingContactRepository.php create mode 100644 app/Core/Repository/Booking/BookingPaymentDataCheckRepository.php create mode 100644 app/Core/Repository/Booking/BookingPaymentDataRepository.php create mode 100644 app/Core/Repository/Booking/BookingPaymentRepository.php create mode 100644 app/Core/Repository/Booking/BookingRepository.php create mode 100644 app/Core/Repository/Booking/BookingRoomPaxRepository.php create mode 100644 app/Core/Repository/Booking/BookingRoomRepository.php create mode 100644 app/Core/Repository/Booking/BookingStatusRepository.php create mode 100644 app/Core/Repository/Booking/BookingTicketRepository.php create mode 100644 app/Core/Repository/Booking/PropertyChannelCouponRepository.php create mode 100644 app/Core/Repository/ChannelManager/ChannelManagerRepository.php create mode 100644 app/Core/Repository/ChannelManagerBooking/ChannelManagerBookingRepository.php create mode 100644 app/Core/Repository/ChannelManagerLog/ChannelManagerLogRepository.php create mode 100644 app/Core/Repository/ChannelManagerMapping/ChannelManagerMappingRepository.php create mode 100644 app/Core/Repository/ChannelManagerPropertyMapping/ChannelManagerPropertyMappingRepository.php create mode 100644 app/Core/Repository/ChannelManagerPropertyRateMapping/ChannelManagerPropertyRateMappingRepository.php create mode 100644 app/Core/Repository/Country/CountryRepository.php create mode 100644 app/Core/Repository/Currency/CurrencyRepository.php create mode 100644 app/Core/Repository/CurrencyRates/CurrencyRatesRepository.php create mode 100644 app/Core/Repository/EleqouentAbstractRepository.php create mode 100644 app/Core/Repository/GeneralTimezone/GeneralTimezoneRepository.php create mode 100644 app/Core/Repository/Ip2NationCountries/Ip2NationCountriesRepository.php create mode 100644 app/Core/Repository/IpNation/IpNationRepository.php create mode 100644 app/Core/Repository/Jobs/JobsRepository.php create mode 100644 app/Core/Repository/Language/LanguageRepository.php create mode 100644 app/Core/Repository/LanguageBase/LanguageBaseRepository.php create mode 100644 app/Core/Repository/LanguageTranslate/LanguageTranslateRepository.php create mode 100644 app/Core/Repository/Offer/OfferRepository.php create mode 100644 app/Core/Repository/OfferAccommodationMapping/OfferAccommodationMappingRepository.php create mode 100644 app/Core/Repository/OfferCancellationPolicy/OfferCancellationPolicyRepository.php create mode 100644 app/Core/Repository/OfferConfirmType/OfferConfirmTypeRepository.php create mode 100644 app/Core/Repository/OfferContactMapping/OfferContactMappingRepository.php create mode 100644 app/Core/Repository/OfferFactMapping/OfferFactMappingRepository.php create mode 100644 app/Core/Repository/OfferImportantNotes/OfferImportantNotesRepository.php create mode 100644 app/Core/Repository/OfferPaymentType/OfferPaymentTypeRepository.php create mode 100644 app/Core/Repository/OfferPhotoMapping/OfferPhotoMappingRepository.php create mode 100644 app/Core/Repository/OfferPrice/OfferPriceRepository.php create mode 100644 app/Core/Repository/OfferRoomMapping/OfferRoomMappingRepository.php create mode 100644 app/Core/Repository/PaymentBinNumber/PaymentBinNumberRepository.php create mode 100644 app/Core/Repository/PaymentTransaction/PaymentTransactionRepository.php create mode 100644 app/Core/Repository/PaymentType/PaymentTypeRepository.php create mode 100644 app/Core/Repository/Permission/PermissionRepository.php create mode 100644 app/Core/Repository/PermissionGroup/PermissionGroupRepository.php create mode 100644 app/Core/Repository/PermissionGroupMapping/PermissionGroupMappingRepository.php create mode 100644 app/Core/Repository/PermissionGroupUserMapping/PermissionGroupUserMappingRepository.php create mode 100644 app/Core/Repository/PhotoGoogleLabel/PhotoGoogleLabelRepository.php create mode 100644 app/Core/Repository/PlaceCategoryField/PlaceCategoryFieldRepository.php create mode 100644 app/Core/Repository/PlaceCategoryFieldMapping/PlaceCategoryFieldMappingRepository.php create mode 100644 app/Core/Repository/PlaceCategoryFieldOption/PlaceCategoryFieldOptionRepository.php create mode 100644 app/Core/Repository/PlaceCategoryFieldOptionFieldMapping/PlaceCategoryFieldOptionFieldMappingRepository.php create mode 100644 app/Core/Repository/Product/ProductParameterMappingRepository.php create mode 100644 app/Core/Repository/Product/ProductRepository.php create mode 100644 app/Core/Repository/Product/PropertyProductMappingRepository.php create mode 100644 app/Core/Repository/PromotionType/PromotionTypeRepository.php create mode 100644 app/Core/Repository/Property/PropertyRepository.php create mode 100644 app/Core/Repository/PropertyAdditionalInfo/PropertyAdditionalInfoKeyRepository.php create mode 100644 app/Core/Repository/PropertyAdditionalInfo/PropertyAdditionalInfoRepository.php create mode 100644 app/Core/Repository/PropertyAddon/PropertyAddonRepository.php create mode 100644 app/Core/Repository/PropertyAddon/PropertyChannelAddonRepository.php create mode 100644 app/Core/Repository/PropertyAvailabilityType/PropertyAvailabilityTypeRepository.php create mode 100644 app/Core/Repository/PropertyAwardsCertificate/PropertyAwardsCertificateRepository.php create mode 100644 app/Core/Repository/PropertyBookingEngine/PropertyBookingEngineRepository.php create mode 100644 app/Core/Repository/PropertyBookingEngine/PropertyBookingEngineSearchRepository.php create mode 100644 app/Core/Repository/PropertyBookingPaymentType/PropertyBookingPaymentTypeRepository.php create mode 100644 app/Core/Repository/PropertyBookingType/PropertyBookingTypeRepository.php create mode 100644 app/Core/Repository/PropertyBrand/PropertyBrandRepository.php create mode 100644 app/Core/Repository/PropertyCancellationPolicy/PropertyCancellationPolicyRepository.php create mode 100644 app/Core/Repository/PropertyChain/PropertyChainRepository.php create mode 100644 app/Core/Repository/PropertyChannel/PropertyChannelContactRepository.php create mode 100644 app/Core/Repository/PropertyChannel/PropertyChannelRepository.php create mode 100644 app/Core/Repository/PropertyChannel/PropertyChannelTaxRepository.php create mode 100644 app/Core/Repository/PropertyChannelBookingPaymentSetup/PropertyChannelBookingPaymentSetupRepository.php create mode 100644 app/Core/Repository/PropertyChannelCategory/PropertyChannelCategoryRepository.php create mode 100644 app/Core/Repository/PropertyChannelGroup/PropertyChannelGroupChannelMappingRepository.php create mode 100644 app/Core/Repository/PropertyChannelGroup/PropertyChannelGroupRepository.php create mode 100644 app/Core/Repository/PropertyChannelMapping/PropertyChannelMappingRepository.php create mode 100644 app/Core/Repository/PropertyChannelRoomRateCancellationPolicyMapping/PropertyChannelRoomRateCancellationPolicyMappingRepository.php create mode 100644 app/Core/Repository/PropertyChannelRoomRatePricingPolicyAdultMapping/PropertyChannelRoomRatePricingPolicyAdultMappingRepository.php create mode 100644 app/Core/Repository/PropertyChannelRoomRatePricingPolicyChildMapping/PropertyChannelRoomRatePricingPolicyChildMappingRepository.php create mode 100644 app/Core/Repository/PropertyCompetitorGroup/PropertyCompetitorGroupRepository.php create mode 100644 app/Core/Repository/PropertyCompetitorGroupMapping/PropertyCompetitorGroupMappingRepository.php create mode 100644 app/Core/Repository/PropertyCompetitorMapping/PropertyCompetitorMappingRepository.php create mode 100644 app/Core/Repository/PropertyConfig/PropertyConfigRepository.php create mode 100644 app/Core/Repository/PropertyContact/PropertyContactRepository.php create mode 100644 app/Core/Repository/PropertyContent/PropertyContentRepository.php create mode 100644 app/Core/Repository/PropertyContentCategory/PropertyContentCategoryRepository.php create mode 100644 app/Core/Repository/PropertyExecutive/PropertyExecutiveRepository.php create mode 100644 app/Core/Repository/PropertyExecutiveType/PropertyExecutiveTypeRepository.php create mode 100644 app/Core/Repository/PropertyFact/PropertyFactRepository.php create mode 100644 app/Core/Repository/PropertyFactAttribute/PropertyFactAttributeRepository.php create mode 100644 app/Core/Repository/PropertyFactMapping/PropertyFactMappingRepository.php create mode 100644 app/Core/Repository/PropertyGroup/PropertyGroupRepository.php create mode 100644 app/Core/Repository/PropertyGroupMapping/PropertyGroupMappingRepository.php create mode 100644 app/Core/Repository/PropertyInvoice/PropertyInvoiceRepository.php create mode 100644 app/Core/Repository/PropertyLanguageSpoken/PropertyLanguageSpokenRepository.php create mode 100644 app/Core/Repository/PropertyModule/PropertyModuleRepository.php create mode 100644 app/Core/Repository/PropertyModuleMapping/PropertyModuleMappingRepository.php create mode 100644 app/Core/Repository/PropertyNonrefundable/PropertyNonrefundableRepository.php create mode 100644 app/Core/Repository/PropertyPaymentInstallment/PropertyPaymentInstallmentRepository.php create mode 100644 app/Core/Repository/PropertyPaymentMapping/PropertyPaymentMappingRepository.php create mode 100644 app/Core/Repository/PropertyPhoto/PropertyPhotoRepository.php create mode 100644 app/Core/Repository/PropertyPhotoCategory/PropertyPhotoCategoryRepository.php create mode 100644 app/Core/Repository/PropertyPhotoCategoryLabelMapping/PropertyPhotoCategoryLabelMappingRepository.php create mode 100644 app/Core/Repository/PropertyPlace/PropertyPlaceRepository.php create mode 100644 app/Core/Repository/PropertyPlaceCategory/PropertyPlaceCategoryRepository.php create mode 100644 app/Core/Repository/PropertyPlaceCategoryFieldValue/PropertyPlaceCategoryFieldValueRepository.php create mode 100644 app/Core/Repository/PropertyPlaceFact/PropertyPlaceFactRepository.php create mode 100644 app/Core/Repository/PropertyPlaceFactMapping/PropertyPlaceFactMappingRepository.php create mode 100644 app/Core/Repository/PropertyPlaceFactTitle/PropertyPlaceFactTitleRepository.php create mode 100644 app/Core/Repository/PropertyPlaceFactTitleFactMapping/PropertyPlaceFactTitleFactMappingRepository.php create mode 100644 app/Core/Repository/PropertyPlacePhotoMapping/PropertyPlacePhotoMappingRepository.php create mode 100644 app/Core/Repository/PropertyPlaceWorkingHour/PropertyPlaceWorkingHourRepository.php create mode 100644 app/Core/Repository/PropertyPricingPolicyAdult/PropertyPricingPolicyAdultRepository.php create mode 100644 app/Core/Repository/PropertyPricingPolicyChild/PropertyPricingPolicyChildRepository.php create mode 100644 app/Core/Repository/PropertyPromotion/PropertyPromotionRepository.php create mode 100644 app/Core/Repository/PropertyPromotionMapping/PropertyPromotionMappingRepository.php create mode 100644 app/Core/Repository/PropertyQuickPricingMapping/PropertyQuickPricingMappingRepository.php create mode 100644 app/Core/Repository/PropertyRoom/PropertyRoomRepository.php create mode 100644 app/Core/Repository/PropertyRoomAvailability/PropertyRoomAvailabilityQueueRepository.php create mode 100644 app/Core/Repository/PropertyRoomAvailability/PropertyRoomAvailabilityRepository.php create mode 100644 app/Core/Repository/PropertyRoomBed/PropertyRoomBedRepository.php create mode 100644 app/Core/Repository/PropertyRoomBedType/PropertyRoomBedTypeRepository.php create mode 100644 app/Core/Repository/PropertyRoomConnected/PropertyRoomConnectedRepository.php create mode 100644 app/Core/Repository/PropertyRoomFactMapping/PropertyRoomFactMappingRepository.php create mode 100644 app/Core/Repository/PropertyRoomPhotoMapping/PropertyRoomPhotoMappingRepository.php create mode 100644 app/Core/Repository/PropertyRoomPricingType/PropertyRoomPricingTypeRepository.php create mode 100644 app/Core/Repository/PropertyRoomRate/PropertyRoomRateRepository.php create mode 100644 app/Core/Repository/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingRepository.php create mode 100644 app/Core/Repository/PropertyRoomRateInclusionMapping/PropertyRoomRateInclusionMappingRepository.php create mode 100644 app/Core/Repository/PropertyRoomRateMapping/PropertyRoomRateMappingRepository.php create mode 100644 app/Core/Repository/PropertyRoomRateMappingSetup/PropertyRoomRateMappingSetupRepository.php create mode 100644 app/Core/Repository/PropertyRoomRatePrice/PropertyRoomRatePriceQueueRepository.php create mode 100644 app/Core/Repository/PropertyRoomRatePrice/PropertyRoomRatePriceRepository.php create mode 100644 app/Core/Repository/PropertyRoomSizeType/PropertyRoomSizeTypeRepository.php create mode 100644 app/Core/Repository/PropertyRoomType/PropertyRoomTypeRepository.php create mode 100644 app/Core/Repository/PropertyRoomViewMapping/PropertyRoomViewMappingRepository.php create mode 100644 app/Core/Repository/PropertyRoomViewType/PropertyRoomViewTypeRepository.php create mode 100644 app/Core/Repository/PropertySummary/PropertySummaryRepository.php create mode 100644 app/Core/Repository/PropertyType/PropertyTypeRepository.php create mode 100644 app/Core/Repository/PropertyUnit/PropertyUnitRepository.php create mode 100644 app/Core/Repository/PropertyWeb/PropertyWebComponentMappingRepository.php create mode 100644 app/Core/Repository/PropertyWeb/PropertyWebComponentRepository.php create mode 100644 app/Core/Repository/PropertyWeb/PropertyWebGroupRepository.php create mode 100644 app/Core/Repository/PropertyWeb/PropertyWebMetaMappingRepository.php create mode 100644 app/Core/Repository/PropertyWeb/PropertyWebMetaRepository.php create mode 100644 app/Core/Repository/PropertyWeb/PropertyWebMetaTagRepository.php create mode 100644 app/Core/Repository/PropertyWeb/PropertyWebRepository.php create mode 100644 app/Core/Repository/PropertyWeb/PropertyWebReviewRepository.php create mode 100644 app/Core/Repository/PropertyWebAboutUs/PropertyWebAboutUsRepository.php create mode 100644 app/Core/Repository/PropertyWebColorMapping/PropertyWebColorMappingRepository.php create mode 100644 app/Core/Repository/PropertyWebContent/PropertyWebContentCategoryRepository.php create mode 100644 app/Core/Repository/PropertyWebContent/PropertyWebContentRepository.php create mode 100644 app/Core/Repository/PropertyWebLanguageMapping/PropertyWebLanguageMappingRepository.php create mode 100644 app/Core/Repository/PropertyWebLog/PropertyWebLogRepository.php create mode 100644 app/Core/Repository/PropertyWebMenu/PropertyWebMenuRepository.php create mode 100644 app/Core/Repository/PropertyWebMenuMapping/PropertyWebMenuMappingRepository.php create mode 100644 app/Core/Repository/PropertyWebPhotoMapping/PropertyWebPhotoMappingRepository.php create mode 100644 app/Core/Repository/PropertyWebPlaceMapping/PropertyWebPlaceMappingRepository.php create mode 100644 app/Core/Repository/PropertyWebPopup/PropertyWebPopupRepository.php create mode 100644 app/Core/Repository/PropertyWebRoomMapping/PropertyWebRoomMappingRepository.php create mode 100644 app/Core/Repository/PropertyWebSetup/PropertyWebSetupRepository.php create mode 100644 app/Core/Repository/PropertyWebTemplate/PropertyWebTemplateRepository.php create mode 100644 app/Core/Repository/ReputationManagement/PropertyReviewCategoryRepository.php create mode 100644 app/Core/Repository/ReputationManagement/PropertyReviewChannelMappingRepository.php create mode 100644 app/Core/Repository/ReputationManagement/PropertyReviewChannelRepository.php create mode 100644 app/Core/Repository/ReputationManagement/PropertyReviewRepository.php create mode 100644 app/Core/Repository/ServiceLog/ServiceLogRepository.php create mode 100644 app/Core/Repository/SiteConfig/SiteConfigRepository.php create mode 100644 app/Core/Repository/TempProperty/TempPropertyRepository.php create mode 100644 app/Core/Repository/User/UserRepository.php create mode 100644 app/Core/Repository/UserPropertyMapping/UserPropertyMappingRepository.php create mode 100644 app/Core/Repository/VWDestination/VWDestinationRepository.php create mode 100644 app/Core/Service/ApiAccessTokenService.php create mode 100644 app/Core/Service/ApplicationCacheService.php create mode 100644 app/Core/Service/BookingAddonService.php create mode 100644 app/Core/Service/BookingContactService.php create mode 100644 app/Core/Service/BookingPaymentService.php create mode 100644 app/Core/Service/BookingRoomPaxService.php create mode 100644 app/Core/Service/BookingRoomService.php create mode 100644 app/Core/Service/BookingService.php create mode 100644 app/Core/Service/BookingTicketService.php create mode 100644 app/Core/Service/ChannelManager/Athena.php create mode 100644 app/Core/Service/ChannelManager/Channex.php create mode 100644 app/Core/Service/ChannelManager/ElektraWeb.php create mode 100644 app/Core/Service/ChannelManager/Fina.php create mode 100644 app/Core/Service/ChannelManager/HotelRunner.php create mode 100644 app/Core/Service/ChannelManager/HyperGuest.php create mode 100644 app/Core/Service/ChannelManager/Mirai.php create mode 100644 app/Core/Service/ChannelManager/Reseliva.php create mode 100644 app/Core/Service/ChannelManager/SistemOtel.php create mode 100644 app/Core/Service/ChannelManager/Trivago.php create mode 100644 app/Core/Service/ChannelManager/_Mirai.php create mode 100644 app/Core/Service/ChannelManagerBookingService.php create mode 100644 app/Core/Service/ChannelManagerLogService.php create mode 100644 app/Core/Service/ChannelManagerMappingService.php create mode 100644 app/Core/Service/ChannelManagerPropertyMappingService.php create mode 100644 app/Core/Service/ChannelManagerPropertyRateMappingService.php create mode 100644 app/Core/Service/ChannelManagerService.php create mode 100644 app/Core/Service/CompetitorPriceAnalysisService.php create mode 100644 app/Core/Service/CountryService.php create mode 100644 app/Core/Service/CurrencyService.php create mode 100644 app/Core/Service/DashboardPlusService.php create mode 100644 app/Core/Service/DestinationService.php create mode 100644 app/Core/Service/EnwContactFormService.php create mode 100644 app/Core/Service/FindCountryCodeService.php create mode 100644 app/Core/Service/GeneralTimezoneService.php create mode 100644 app/Core/Service/Google/GoogleVisionLabelService.php create mode 100644 app/Core/Service/JobsService.php create mode 100644 app/Core/Service/JwtService.php create mode 100644 app/Core/Service/LanguageBaseService.php create mode 100644 app/Core/Service/LanguageService.php create mode 100644 app/Core/Service/ManualPaymentMailService.php create mode 100644 app/Core/Service/MyWebContentService.php create mode 100644 app/Core/Service/NewBookingMailService.php create mode 100644 app/Core/Service/NotificationService.php create mode 100644 app/Core/Service/OfferAccommodationMappingService.php create mode 100644 app/Core/Service/OfferCancellationPolicyService.php create mode 100644 app/Core/Service/OfferContactMappingService.php create mode 100644 app/Core/Service/OfferFactMappingService.php create mode 100644 app/Core/Service/OfferImportantNotesService.php create mode 100644 app/Core/Service/OfferPaymentTypeService.php create mode 100644 app/Core/Service/OfferPhotoMappingService.php create mode 100644 app/Core/Service/OfferPriceService.php create mode 100644 app/Core/Service/OfferRoomMappingService.php create mode 100644 app/Core/Service/OfferService.php create mode 100644 app/Core/Service/OneSignalService.php create mode 100644 app/Core/Service/PdfContentService.php create mode 100644 app/Core/Service/PermissionGroupService.php create mode 100644 app/Core/Service/PermissionGroupUserMappingService.php create mode 100644 app/Core/Service/PermissionService.php create mode 100644 app/Core/Service/ProductParameterMappingService.php create mode 100644 app/Core/Service/ProductService.php create mode 100644 app/Core/Service/PropertyAdditionalInfo/PropertyAdditionalInfoService.php create mode 100644 app/Core/Service/PropertyAddonService.php create mode 100644 app/Core/Service/PropertyAwardsCertificateService.php create mode 100644 app/Core/Service/PropertyBookingEngineSearchService.php create mode 100644 app/Core/Service/PropertyBookingEngineService.php create mode 100644 app/Core/Service/PropertyBookingPaymentTypeService.php create mode 100644 app/Core/Service/PropertyBrandService.php create mode 100644 app/Core/Service/PropertyCancellationPolicyService.php create mode 100644 app/Core/Service/PropertyChainService.php create mode 100644 app/Core/Service/PropertyChannelBookingPaymentSetupService.php create mode 100644 app/Core/Service/PropertyChannelCategoryService.php create mode 100644 app/Core/Service/PropertyChannelContactService.php create mode 100644 app/Core/Service/PropertyChannelCouponService.php create mode 100644 app/Core/Service/PropertyChannelGroupChannelMappingService.php create mode 100644 app/Core/Service/PropertyChannelGroupService.php create mode 100644 app/Core/Service/PropertyChannelMappingService.php create mode 100644 app/Core/Service/PropertyChannelService.php create mode 100644 app/Core/Service/PropertyCompetitorGroupService.php create mode 100644 app/Core/Service/PropertyConfigService.php create mode 100644 app/Core/Service/PropertyContactService.php create mode 100644 app/Core/Service/PropertyContentService.php create mode 100644 app/Core/Service/PropertyExecutiveService.php create mode 100644 app/Core/Service/PropertyExecutiveTypeService.php create mode 100644 app/Core/Service/PropertyFactMappingService.php create mode 100644 app/Core/Service/PropertyFactService.php create mode 100644 app/Core/Service/PropertyGroupMappingService.php create mode 100644 app/Core/Service/PropertyGroupService.php create mode 100644 app/Core/Service/PropertyInvoiceService.php create mode 100644 app/Core/Service/PropertyModuleMappingService.php create mode 100644 app/Core/Service/PropertyNetworkService.php create mode 100644 app/Core/Service/PropertyNonrefundableService.php create mode 100644 app/Core/Service/PropertyPaymentService.php create mode 100644 app/Core/Service/PropertyPersonPricingPolicyService.php create mode 100644 app/Core/Service/PropertyPhotoService/PropertyPhotoCategoryService.php create mode 100644 app/Core/Service/PropertyPhotoService/PropertyPhotoService.php create mode 100644 app/Core/Service/PropertyPlaceService.php create mode 100644 app/Core/Service/PropertyProductMappingService.php create mode 100644 app/Core/Service/PropertyPromotionService.php create mode 100644 app/Core/Service/PropertyQuickPricingService.php create mode 100644 app/Core/Service/PropertyRoomAvailabilityQueueService.php create mode 100644 app/Core/Service/PropertyRoomAvailabilityService.php create mode 100644 app/Core/Service/PropertyRoomBedService.php create mode 100644 app/Core/Service/PropertyRoomBedTypeService.php create mode 100644 app/Core/Service/PropertyRoomFactMappingService.php create mode 100644 app/Core/Service/PropertyRoomPhotoMappingService.php create mode 100644 app/Core/Service/PropertyRoomRateChannelMappingService.php create mode 100644 app/Core/Service/PropertyRoomRateInclusionMappingService.php create mode 100644 app/Core/Service/PropertyRoomRateMappingService.php create mode 100644 app/Core/Service/PropertyRoomRateMappingSetupService.php create mode 100644 app/Core/Service/PropertyRoomRatePriceQueueService.php create mode 100644 app/Core/Service/PropertyRoomRatePriceService.php create mode 100644 app/Core/Service/PropertyRoomRateService.php create mode 100644 app/Core/Service/PropertyRoomService.php create mode 100644 app/Core/Service/PropertyRoomSizeTypeService.php create mode 100644 app/Core/Service/PropertyRoomTypeService.php create mode 100644 app/Core/Service/PropertyRoomViewTypeService.php create mode 100644 app/Core/Service/PropertyService.php create mode 100644 app/Core/Service/PropertySummaryService.php create mode 100644 app/Core/Service/PropertyTypeService.php create mode 100644 app/Core/Service/PropertyWebAboutUsService.php create mode 100644 app/Core/Service/PropertyWebColorMappingService.php create mode 100644 app/Core/Service/PropertyWebContentService.php create mode 100644 app/Core/Service/PropertyWebLanguageMappingService.php create mode 100644 app/Core/Service/PropertyWebLogService.php create mode 100644 app/Core/Service/PropertyWebMenuMappingService.php create mode 100644 app/Core/Service/PropertyWebMenuService.php create mode 100644 app/Core/Service/PropertyWebMetaService.php create mode 100644 app/Core/Service/PropertyWebPhotoMappingService.php create mode 100644 app/Core/Service/PropertyWebPlaceMappingService.php create mode 100644 app/Core/Service/PropertyWebPopupService.php create mode 100644 app/Core/Service/PropertyWebRoomMappingService.php create mode 100644 app/Core/Service/PropertyWebService.php create mode 100644 app/Core/Service/PropertyWebSetupService.php create mode 100644 app/Core/Service/PropertyWebTemplateService.php create mode 100644 app/Core/Service/QRCodeService.php create mode 100644 app/Core/Service/ReputationManagementService.php create mode 100644 app/Core/Service/ServiceLogService.php create mode 100644 app/Core/Service/SiteConfigService.php create mode 100644 app/Core/Service/TempPropertyService.php create mode 100644 app/Core/Service/ThirdPartyServices/MondayService.php create mode 100644 app/Core/Service/UserPropertyMappingService.php create mode 100644 app/Core/Service/UserService.php create mode 100644 app/Core/Validator/BaseValidator.php create mode 100644 app/Core/Validator/Booking/BookingUpdateValidator.php create mode 100644 app/Core/Validator/BookingEngine/ContractUploadValidator.php create mode 100644 app/Core/Validator/Destination/DestinationSearchValidator.php create mode 100644 app/Core/Validator/EnwContactForm/EnwContactFormCreateValidator.php create mode 100644 app/Core/Validator/ExampleValidator.php create mode 100644 app/Core/Validator/Offer/OfferCreateValidator.php create mode 100644 app/Core/Validator/Offer/OfferStatusValidator.php create mode 100644 app/Core/Validator/Offer/OfferUpdateValidator.php create mode 100644 app/Core/Validator/OfferAccommodationMapping/OfferAccommodationMappingAddValidator.php create mode 100644 app/Core/Validator/OfferAccommodationMapping/OfferAccommodationMappingCreateValidator.php create mode 100644 app/Core/Validator/OfferCancellationPolicy/OfferCancellationPolicyCreateValidator.php create mode 100644 app/Core/Validator/OfferContactMapping/OfferContactMappingAddValidator.php create mode 100644 app/Core/Validator/OfferContactMapping/OfferContactMappingCreateValidator.php create mode 100644 app/Core/Validator/OfferFactMapping/OfferFactMappingAddValidator.php create mode 100644 app/Core/Validator/OfferFactMapping/OfferFactMappingCreateValidator.php create mode 100644 app/Core/Validator/OfferImportantNotes/OfferImportantNotesCreateValidator.php create mode 100644 app/Core/Validator/OfferPaymentType/OfferPaymentTypeCreateValidator.php create mode 100644 app/Core/Validator/OfferPhotoMapping/OfferCoverPhotoAddValidator.php create mode 100644 app/Core/Validator/OfferPhotoMapping/OfferPhotoMappingAddValidator.php create mode 100644 app/Core/Validator/OfferPhotoMapping/OfferPhotoMappingCreateValidator.php create mode 100644 app/Core/Validator/OfferPrice/OfferPriceCreateValidator.php create mode 100644 app/Core/Validator/OfferRoomMapping/OfferRoomMappingAddValidator.php create mode 100644 app/Core/Validator/OfferRoomMapping/OfferRoomMappingCreateValidator.php create mode 100644 app/Core/Validator/Property/PropertyBrandAddValidator.php create mode 100644 app/Core/Validator/Property/PropertyBrandPhotoValidator.php create mode 100644 app/Core/Validator/Property/PropertyCreateValidator.php create mode 100644 app/Core/Validator/Property/PropertyLocationUpdateValidator.php create mode 100644 app/Core/Validator/Property/PropertyPhotoValidator.php create mode 100644 app/Core/Validator/Property/PropertySearchValidator.php create mode 100644 app/Core/Validator/Property/PropertyUpdateValidator.php create mode 100644 app/Core/Validator/PropertyAdditionalInfo/PropertyAdditionalInfoAddValidator.php create mode 100644 app/Core/Validator/PropertyAddon/PropertyAddonValidator.php create mode 100644 app/Core/Validator/PropertyAwardsCertificate/PropertyAwardCertificateUploadValidator.php create mode 100644 app/Core/Validator/PropertyAwardsCertificate/PropertyAwardsCertificateCreateValidator.php create mode 100644 app/Core/Validator/PropertyAwardsCertificate/PropertyAwardsCertificateUpdateValidator.php create mode 100644 app/Core/Validator/PropertyBookingTicket/PropertyTicketCreateValidator.php create mode 100644 app/Core/Validator/PropertyBookingTicket/PropertyTicketGetValidator.php create mode 100644 app/Core/Validator/PropertyCancellationPolicy/PropertyCancellationPolicyAddValidator.php create mode 100644 app/Core/Validator/PropertyCancellationPolicy/PropertyCancellationPolicyUpdateValidator.php create mode 100644 app/Core/Validator/PropertyCancellationPolicy/UpdateRoomRateChannelCancellationPolicyValidator.php create mode 100644 app/Core/Validator/PropertyChannel/PropertyChannelAddValidator.php create mode 100644 app/Core/Validator/PropertyChannel/PropertyChannelDeleteValidator.php create mode 100644 app/Core/Validator/PropertyChannel/PropertyChannelUpdateValidator.php create mode 100644 app/Core/Validator/PropertyChannelBookingPaymentSetup/PropertyChannelBookingPaymentSetupAddValidator.php create mode 100644 app/Core/Validator/PropertyChannelCoupon/PropertyChannelCouponValidator.php create mode 100644 app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingAddValidator.php create mode 100644 app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingRemoveValidator.php create mode 100644 app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingUpdateValidator.php create mode 100644 app/Core/Validator/PropertyChannelMapping/PropertyChannelSetupValidator.php create mode 100644 app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupAddValidator.php create mode 100644 app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupUpdateValidator.php create mode 100644 app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupWithMissingParametersAddValidator.php create mode 100644 app/Core/Validator/PropertyConfig/PropertyConfigCreateValidator.php create mode 100644 app/Core/Validator/PropertyContact/PropertyContactCreateValidator.php create mode 100644 app/Core/Validator/PropertyContact/PropertyContactUpdateValidator.php create mode 100644 app/Core/Validator/PropertyContent/PropertyContentCreateValidator.php create mode 100644 app/Core/Validator/PropertyExecutive/PropertyExecutiveCreateValidator.php create mode 100644 app/Core/Validator/PropertyExecutive/PropertyExecutiveUpdateValidator.php create mode 100644 app/Core/Validator/PropertyFact/PropertyFactCheckboxValidator.php create mode 100644 app/Core/Validator/PropertyFact/PropertyFactSearchValidator.php create mode 100644 app/Core/Validator/PropertyNonrefundable/PropertyNonrefundableAddValidator.php create mode 100644 app/Core/Validator/PropertyPayment/PropertyPaymentTransactionCreateValidator.php create mode 100644 app/Core/Validator/PropertyPayment/PropertyPaymentTransactionUpdateValidator.php create mode 100644 app/Core/Validator/PropertyPaymentMapping/PropertyPaymentInstallmentsUpdateValidator.php create mode 100644 app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingCreateValidator.php create mode 100644 app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingSetDefaultValidator.php create mode 100644 app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingStatusUpdateValidator.php create mode 100644 app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingUpdateValidator.php create mode 100644 app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyAdultAddValidator.php create mode 100644 app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyAdultUpdateValidator.php create mode 100644 app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyChildAddValidator.php create mode 100644 app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyChildUpdateValidator.php create mode 100644 app/Core/Validator/PropertyPersonPricingPolicy/UpdateRoomRateChannelPersonPricingAdultPolicyValidator.php create mode 100644 app/Core/Validator/PropertyPersonPricingPolicy/UpdateRoomRateChannelPersonPricingChildPolicyValidator.php create mode 100644 app/Core/Validator/PropertyPlace/PropertyPlaceCreateValidator.php create mode 100644 app/Core/Validator/PropertyPlace/PropertyPlaceFactMappingUpdateValidator.php create mode 100644 app/Core/Validator/PropertyPlace/PropertyPlacePhotoMappingAddValidator.php create mode 100644 app/Core/Validator/PropertyPlace/PropertyPlaceUpdateValidator.php create mode 100644 app/Core/Validator/PropertyPromotion/PropertyPromotionAddValidator.php create mode 100644 app/Core/Validator/PropertyPromotion/PropertyPromotionUpdateValidator.php create mode 100644 app/Core/Validator/PropertyPromotion/UpdateRoomRateChannelPromotionValidator.php create mode 100644 app/Core/Validator/PropertyQuickPricing/PropertyQuickPricingCreateValidator.php create mode 100644 app/Core/Validator/PropertyRoom/PropertyRoomAddValidator.php create mode 100644 app/Core/Validator/PropertyRoom/PropertyRoomAndBedAddValidator.php create mode 100644 app/Core/Validator/PropertyRoom/PropertyRoomAndBedUpdateValidator.php create mode 100644 app/Core/Validator/PropertyRoom/PropertyRoomDeleteValidator.php create mode 100644 app/Core/Validator/PropertyRoom/PropertyRoomFactMappingAddValidator.php create mode 100644 app/Core/Validator/PropertyRoom/PropertyRoomInventoryValidator.php create mode 100644 app/Core/Validator/PropertyRoom/PropertyRoomPhotoMappingAddValidator.php create mode 100644 app/Core/Validator/PropertyRoom/PropertyRoomUpdateValidator.php create mode 100644 app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityAddValidator.php create mode 100644 app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityBulkInsertValidator.php create mode 100644 app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityBulkUpdateValidator.php create mode 100644 app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityDeleteValidator.php create mode 100644 app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityUpdateValidator.php create mode 100644 app/Core/Validator/PropertyRoomBed/PropertyRoomBedAddValidator.php create mode 100644 app/Core/Validator/PropertyRoomRate/PropertyRoomRateAddValidator.php create mode 100644 app/Core/Validator/PropertyRoomRate/PropertyRoomRateAddWithInclusionValidator.php create mode 100644 app/Core/Validator/PropertyRoomRate/PropertyRoomRateDeleteValidator.php create mode 100644 app/Core/Validator/PropertyRoomRate/PropertyRoomRateUpdateValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingAddValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingUpdateValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateInclusionMapping/PropertyRoomRateInclusionMappingAddValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingAddValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingAddWithSetupValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingConnectedValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingDeleteValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingSetStatusValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingUpdateValidator.php create mode 100644 app/Core/Validator/PropertyRoomRateMappingSetup/PropertyRoomRateMappingSetupAddValidator.php create mode 100644 app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceAddValidator.php create mode 100644 app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceBulkInsertValidator.php create mode 100644 app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceBulkUpdateValidator.php create mode 100644 app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceDeleteValidator.php create mode 100644 app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceUpdateValidator.php create mode 100644 app/Core/Validator/PropertyWeb/PropertyWebContactFormValidator.php create mode 100644 app/Core/Validator/PropertyWeb/PropertyWebCreateValidator.php create mode 100644 app/Core/Validator/PropertyWeb/PropertyWebPublishValidator.php create mode 100644 app/Core/Validator/PropertyWeb/PropertyWebUpdateContentValidator.php create mode 100644 app/Core/Validator/PropertyWeb/PropertyWebUpdateValidator.php create mode 100644 app/Core/Validator/PropertyWebContent/PropertyWebContentCreateValidator.php create mode 100644 app/Core/Validator/PropertyWebTemplate/PropertyWebTemplateCreateValidator.php create mode 100644 app/Core/Validator/TempProperty/TempPropertySearchValidator.php create mode 100644 app/Core/Validator/User/ChangePasswordValidator.php create mode 100644 app/Core/Validator/User/ResetPasswordValidator.php create mode 100644 app/Core/Validator/User/UserCreateValidator.php create mode 100644 app/Core/Validator/User/UserLoginValidator.php create mode 100644 app/Core/Validator/User/UserNewPasswordValidator.php create mode 100644 app/Core/Validator/User/UserProfileUpdateValidator.php create mode 100644 app/Core/Validator/UserPropertyMapping/UserPropertyMappingAddValidator.php create mode 100644 app/Core/Validator/UserPropertyMapping/UserPropertyMappingRemoveValidator.php create mode 100644 app/Events/Event.php create mode 100644 app/Events/ExampleEvent.php create mode 100644 app/Exceptions/ApiErrorException.php create mode 100644 app/Exceptions/Handler.php create mode 100644 app/Export/PropertyBookingListExport.php create mode 100644 app/Export/PropertyCompetitorExport.php create mode 100644 app/Http/Controllers/AuthController.php create mode 100644 app/Http/Controllers/BookingEngine/V1/BookingController.php create mode 100644 app/Http/Controllers/BookingEngine/V1/BookingEngineBaseController.php create mode 100644 app/Http/Controllers/BookingEngine/V1/PaymentLinkController.php create mode 100644 app/Http/Controllers/BookingEngine/V1/PropertyBookingEngineController.php create mode 100644 app/Http/Controllers/BookingEngine/V1/SearchController.php create mode 100644 app/Http/Controllers/ChannelManager/Athena/v1/AthenaController.php create mode 100644 app/Http/Controllers/ChannelManager/Channex/v1/ChannexController.php create mode 100644 app/Http/Controllers/ChannelManager/ElektraWeb/v1/ElektraWebController.php create mode 100644 app/Http/Controllers/ChannelManager/ElektraWeb/v1/_ElektraWebController.php create mode 100644 app/Http/Controllers/ChannelManager/Fina/v1/FinaController.php create mode 100644 app/Http/Controllers/ChannelManager/HotelRunner/v1/HotelRunnerController.php create mode 100644 app/Http/Controllers/ChannelManager/HyperGuest/v1/HyperGuestController.php create mode 100644 app/Http/Controllers/ChannelManager/OneC/v1/OneCController.php create mode 100644 app/Http/Controllers/ChannelManager/Reseliva/v1/ReselivaController.php create mode 100644 app/Http/Controllers/ChannelManager/SistemOtel/v1/SistemOtelController.php create mode 100644 app/Http/Controllers/Controller.php create mode 100644 app/Http/Controllers/MetaSearch/Google/v1/GoogleController.php create mode 100644 app/Http/Controllers/MetaSearch/Trivago/v1/TrivagoController.php create mode 100644 app/Http/Controllers/MetaSearch/Trivago/v1/_TrivagoController.php create mode 100644 app/Http/Controllers/MetaSearch/Yandex/v1/YandexController.php create mode 100644 app/Http/Controllers/PaymentController.php create mode 100644 app/Http/Controllers/TestController.php create mode 100644 app/Http/Controllers/UserController.php create mode 100644 app/Http/Controllers/V1/AIController.php create mode 100644 app/Http/Controllers/V1/CompetitorPriceAnalysisController.php create mode 100644 app/Http/Controllers/V1/CurrencyController.php create mode 100644 app/Http/Controllers/V1/DestinationController.php create mode 100644 app/Http/Controllers/V1/EnwContactFormController.php create mode 100644 app/Http/Controllers/V1/ExportPdfController.php create mode 100644 app/Http/Controllers/V1/LanguageController.php create mode 100644 app/Http/Controllers/V1/MyWebContentController.php create mode 100644 app/Http/Controllers/V1/PaymentController.php create mode 100644 app/Http/Controllers/V1/ProductController.php create mode 100644 app/Http/Controllers/V1/PropertyAdditionalInfoController.php create mode 100644 app/Http/Controllers/V1/PropertyAddonController.php create mode 100644 app/Http/Controllers/V1/PropertyAwardCertificatesController.php create mode 100644 app/Http/Controllers/V1/PropertyBookingController.php create mode 100644 app/Http/Controllers/V1/PropertyBookingTicketController.php create mode 100644 app/Http/Controllers/V1/PropertyBrandController.php create mode 100644 app/Http/Controllers/V1/PropertyCancellationPolicyController.php create mode 100644 app/Http/Controllers/V1/PropertyChainController.php create mode 100644 app/Http/Controllers/V1/PropertyChannelBookingPaymentSetupController.php create mode 100644 app/Http/Controllers/V1/PropertyChannelCategoryController.php create mode 100644 app/Http/Controllers/V1/PropertyChannelContactController.php create mode 100644 app/Http/Controllers/V1/PropertyChannelController.php create mode 100644 app/Http/Controllers/V1/PropertyChannelGroupController.php create mode 100644 app/Http/Controllers/V1/PropertyChannelMappingController.php create mode 100644 app/Http/Controllers/V1/PropertyCompetitorGroupController.php create mode 100644 app/Http/Controllers/V1/PropertyConfigController.php create mode 100644 app/Http/Controllers/V1/PropertyContactController.php create mode 100644 app/Http/Controllers/V1/PropertyContentController.php create mode 100644 app/Http/Controllers/V1/PropertyController.php create mode 100644 app/Http/Controllers/V1/PropertyCouponController.php create mode 100644 app/Http/Controllers/V1/PropertyExecutiveController.php create mode 100644 app/Http/Controllers/V1/PropertyFactController.php create mode 100644 app/Http/Controllers/V1/PropertyFactMappingController.php create mode 100644 app/Http/Controllers/V1/PropertyNonrefundableController.php create mode 100644 app/Http/Controllers/V1/PropertyOfferController.php create mode 100644 app/Http/Controllers/V1/PropertyPersonPricingPolicyController.php create mode 100644 app/Http/Controllers/V1/PropertyPhotoCategoryController.php create mode 100644 app/Http/Controllers/V1/PropertyPhotoController.php create mode 100644 app/Http/Controllers/V1/PropertyPlaceController.php create mode 100644 app/Http/Controllers/V1/PropertyPromotionController.php create mode 100644 app/Http/Controllers/V1/PropertyQuickPricingController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomBedController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomBedTypeController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomFactMappingController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomPhotoMappingController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomRateChannelMappingController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomRateController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomRateInclusionMappingController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomRateMappingController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomRateMappingSetupController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomRatePriceController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomSizeTypeController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomTypeController.php create mode 100644 app/Http/Controllers/V1/PropertyRoomViewTypeController.php create mode 100644 app/Http/Controllers/V1/PropertyTypeController.php create mode 100644 app/Http/Controllers/V1/PropertyWebComponentController.php create mode 100644 app/Http/Controllers/V1/PropertyWebContentController.php create mode 100644 app/Http/Controllers/V1/PropertyWebController.php create mode 100644 app/Http/Controllers/V1/PropertyWebPopupController.php create mode 100644 app/Http/Controllers/V1/ReputationManagementController.php create mode 100644 app/Http/Controllers/V1/TempPropertyController.php create mode 100644 app/Http/Controllers/bulutController.backup.php create mode 100644 app/Http/Controllers/bulutController.php create mode 100644 app/Http/Controllers/propertyInsertController.php create mode 100644 app/Http/Middleware/Authenticate.php create mode 100644 app/Http/Middleware/BookingEngineTokenMiddleware.php create mode 100644 app/Http/Middleware/CheckPropertyChannelConnectionMiddleware.php create mode 100644 app/Http/Middleware/ContentWizardMiddleware.php create mode 100644 app/Http/Middleware/CorsMiddleware.php create mode 100644 app/Http/Middleware/ExampleMiddleware.php create mode 100644 app/Http/Middleware/JwtMiddleware.php create mode 100644 app/Http/Middleware/LanguageSettingMiddleware.php create mode 100644 app/Http/Middleware/MyWebTokenMiddleware.php create mode 100644 app/Http/Middleware/PropertyMiddleware.php create mode 100644 app/Http/Middleware/UserRoutePermissionAuthorize.php create mode 100644 app/Jobs/ExampleJob.php create mode 100644 app/Jobs/Job.php create mode 100644 app/Jobs/PropertyCatalogServiceJob.php create mode 100644 app/Jobs/PropertyReviewAnalyzeServiceJob.php create mode 100644 app/Jobs/PropertyReviewServiceJob.php create mode 100644 app/Jobs/SlackLogJob.php create mode 100644 app/Listeners/ExampleListener.php create mode 100644 app/Models/ApiAccessToken.php create mode 100644 app/Models/AwardsCertificateCategory.php create mode 100644 app/Models/BaseModel.php create mode 100644 app/Models/Booking.php create mode 100644 app/Models/BookingAddon.php create mode 100644 app/Models/BookingContact.php create mode 100644 app/Models/BookingPayment.php create mode 100644 app/Models/BookingPaymentData.php create mode 100644 app/Models/BookingPaymentDataCheck.php create mode 100644 app/Models/BookingRoom.php create mode 100644 app/Models/BookingRoomPax.php create mode 100644 app/Models/BookingStatus.php create mode 100644 app/Models/BookingTicket.php create mode 100644 app/Models/ChannelManager.php create mode 100644 app/Models/ChannelManagerBooking.php create mode 100644 app/Models/ChannelManagerLog.php create mode 100644 app/Models/ChannelManagerMapping.php create mode 100644 app/Models/ChannelManagerPropertyMapping.php create mode 100644 app/Models/ChannelManagerPropertyRateMapping.php create mode 100644 app/Models/Country.php create mode 100644 app/Models/Currency.php create mode 100644 app/Models/CurrencyRates.php create mode 100644 app/Models/Destination.php create mode 100644 app/Models/FailedJobs.php create mode 100644 app/Models/GeneralTimezone.php create mode 100644 app/Models/IpNation.php create mode 100644 app/Models/IpNationCountries.php create mode 100644 app/Models/Jobs.php create mode 100644 app/Models/Language.php create mode 100644 app/Models/LanguageBase.php create mode 100644 app/Models/LanguageTranslate.php create mode 100644 app/Models/Offer.php create mode 100644 app/Models/OfferAcceptStatus.php create mode 100644 app/Models/OfferAccommodationMapping.php create mode 100644 app/Models/OfferCancellationPolicy.php create mode 100644 app/Models/OfferConfirmType.php create mode 100644 app/Models/OfferContactMapping.php create mode 100644 app/Models/OfferFactMapping.php create mode 100644 app/Models/OfferImportantNotes.php create mode 100644 app/Models/OfferPaymentType.php create mode 100644 app/Models/OfferPhotoMapping.php create mode 100644 app/Models/OfferPrice.php create mode 100644 app/Models/OfferRoomMapping.php create mode 100644 app/Models/PaymentBinNumber.php create mode 100644 app/Models/PaymentTransaction.php create mode 100644 app/Models/PaymentTransactionStatus.php create mode 100644 app/Models/PaymentType.php create mode 100644 app/Models/Permission.php create mode 100644 app/Models/PermissionGroup.php create mode 100644 app/Models/PermissionGroupMapping.php create mode 100644 app/Models/PermissionGroupUserMapping.php create mode 100644 app/Models/PermissionMenuModel.php create mode 100644 app/Models/PhotoGoogleLabel.php create mode 100644 app/Models/PlaceCategoryField.php create mode 100644 app/Models/PlaceCategoryFieldMapping.php create mode 100644 app/Models/PlaceCategoryFieldOption.php create mode 100644 app/Models/PlaceCategoryFieldOptionFieldMapping.php create mode 100644 app/Models/Product.php create mode 100644 app/Models/ProductParameter.php create mode 100644 app/Models/ProductParameterMapping.php create mode 100644 app/Models/PromotionType.php create mode 100644 app/Models/Property.php create mode 100644 app/Models/PropertyAdditionalInfo.php create mode 100644 app/Models/PropertyAdditionalInfoKey.php create mode 100644 app/Models/PropertyAddon.php create mode 100644 app/Models/PropertyAvailabilityType.php create mode 100644 app/Models/PropertyAwardsCertificate.php create mode 100644 app/Models/PropertyBookingEngine.php create mode 100644 app/Models/PropertyBookingEngineSearch.php create mode 100644 app/Models/PropertyBookingPaymentType.php create mode 100644 app/Models/PropertyBookingType.php create mode 100644 app/Models/PropertyBrand.php create mode 100644 app/Models/PropertyCancellationPolicy.php create mode 100644 app/Models/PropertyChain.php create mode 100644 app/Models/PropertyChannel.php create mode 100644 app/Models/PropertyChannelAddon.php create mode 100644 app/Models/PropertyChannelBookingPaymentSetup.php create mode 100644 app/Models/PropertyChannelCategory.php create mode 100644 app/Models/PropertyChannelContact.php create mode 100644 app/Models/PropertyChannelCoupon.php create mode 100644 app/Models/PropertyChannelGroup.php create mode 100644 app/Models/PropertyChannelGroupChannelMapping.php create mode 100644 app/Models/PropertyChannelMapping.php create mode 100644 app/Models/PropertyChannelRoomRateCancellationPolicyMapping.php create mode 100644 app/Models/PropertyChannelRoomRatePricingPolicyAdultMapping.php create mode 100644 app/Models/PropertyChannelRoomRatePricingPolicyChildMapping.php create mode 100644 app/Models/PropertyChannelTax.php create mode 100644 app/Models/PropertyCompetitorGroup.php create mode 100644 app/Models/PropertyCompetitorGroupMapping.php create mode 100644 app/Models/PropertyCompetitorMapping.php create mode 100644 app/Models/PropertyConfig.php create mode 100644 app/Models/PropertyContact.php create mode 100644 app/Models/PropertyContent.php create mode 100644 app/Models/PropertyContentCategory.php create mode 100644 app/Models/PropertyExecutive.php create mode 100644 app/Models/PropertyExecutiveType.php create mode 100644 app/Models/PropertyFact.php create mode 100644 app/Models/PropertyFactAttribute.php create mode 100644 app/Models/PropertyFactMapping.php create mode 100644 app/Models/PropertyGroup.php create mode 100644 app/Models/PropertyGroupMapping.php create mode 100644 app/Models/PropertyInvoice.php create mode 100644 app/Models/PropertyLanguageSpoken.php create mode 100644 app/Models/PropertyMeta.php create mode 100644 app/Models/PropertyMetaRoomRate.php create mode 100644 app/Models/PropertyMetaRoomRateMapping.php create mode 100644 app/Models/PropertyMetaRoomRatePrice.php create mode 100644 app/Models/PropertyModule.php create mode 100644 app/Models/PropertyModuleMapping.php create mode 100644 app/Models/PropertyNonrefundable.php create mode 100644 app/Models/PropertyPaymentInstallment.php create mode 100644 app/Models/PropertyPaymentMapping.php create mode 100644 app/Models/PropertyPhoto.php create mode 100644 app/Models/PropertyPhotoCategory.php create mode 100644 app/Models/PropertyPhotoCategoryLabelMapping.php create mode 100644 app/Models/PropertyPlace.php create mode 100644 app/Models/PropertyPlaceCategory.php create mode 100644 app/Models/PropertyPlaceCategoryFieldValue.php create mode 100644 app/Models/PropertyPlaceFact.php create mode 100644 app/Models/PropertyPlaceFactMapping.php create mode 100644 app/Models/PropertyPlaceFactTitle.php create mode 100644 app/Models/PropertyPlaceFactTitleFactMapping.php create mode 100644 app/Models/PropertyPlacePhotoMapping.php create mode 100644 app/Models/PropertyPlaceWorkingHour.php create mode 100644 app/Models/PropertyPriceComparison.php create mode 100644 app/Models/PropertyPricingPolicyAdult.php create mode 100644 app/Models/PropertyPricingPolicyChild.php create mode 100644 app/Models/PropertyProductMapping.php create mode 100644 app/Models/PropertyProductOffer.php create mode 100644 app/Models/PropertyPromotion.php create mode 100644 app/Models/PropertyPromotionMapping.php create mode 100644 app/Models/PropertyQuickPricingMapping.php create mode 100644 app/Models/PropertyReview.php create mode 100644 app/Models/PropertyReviewCategory.php create mode 100644 app/Models/PropertyReviewCategoryMapping.php create mode 100644 app/Models/PropertyReviewChannel.php create mode 100644 app/Models/PropertyReviewChannelMapping.php create mode 100644 app/Models/PropertyReviewFetchStatus.php create mode 100644 app/Models/PropertyReviewKeywordMapping.php create mode 100644 app/Models/PropertyRoom.php create mode 100644 app/Models/PropertyRoomAvailability.php create mode 100644 app/Models/PropertyRoomAvailabilityQueue.php create mode 100644 app/Models/PropertyRoomBed.php create mode 100644 app/Models/PropertyRoomBedType.php create mode 100644 app/Models/PropertyRoomConnected.php create mode 100644 app/Models/PropertyRoomFactMapping.php create mode 100644 app/Models/PropertyRoomPhotoMapping.php create mode 100644 app/Models/PropertyRoomPricingType.php create mode 100644 app/Models/PropertyRoomRate.php create mode 100644 app/Models/PropertyRoomRateChannelMapping.php create mode 100644 app/Models/PropertyRoomRateInclusionMapping.php create mode 100644 app/Models/PropertyRoomRateMapping.php create mode 100644 app/Models/PropertyRoomRateMappingSetup.php create mode 100644 app/Models/PropertyRoomRatePrice.php create mode 100644 app/Models/PropertyRoomRatePriceQueue.php create mode 100644 app/Models/PropertyRoomSizeType.php create mode 100644 app/Models/PropertyRoomType.php create mode 100644 app/Models/PropertyRoomViewMapping.php create mode 100644 app/Models/PropertyRoomViewType.php create mode 100644 app/Models/PropertyStatus.php create mode 100644 app/Models/PropertySummary.php create mode 100644 app/Models/PropertyType.php create mode 100644 app/Models/PropertyUnit.php create mode 100644 app/Models/PropertyWeb.php create mode 100644 app/Models/PropertyWebAboutUs.php create mode 100644 app/Models/PropertyWebColorMapping.php create mode 100644 app/Models/PropertyWebComponent.php create mode 100644 app/Models/PropertyWebComponentMapping.php create mode 100644 app/Models/PropertyWebContent.php create mode 100644 app/Models/PropertyWebContentCategory.php create mode 100644 app/Models/PropertyWebGroup.php create mode 100644 app/Models/PropertyWebLanguageMapping.php create mode 100644 app/Models/PropertyWebLog.php create mode 100644 app/Models/PropertyWebMenu.php create mode 100644 app/Models/PropertyWebMenuMapping.php create mode 100644 app/Models/PropertyWebMeta.php create mode 100644 app/Models/PropertyWebMetaMapping.php create mode 100644 app/Models/PropertyWebMetaTag.php create mode 100644 app/Models/PropertyWebPhotoMapping.php create mode 100644 app/Models/PropertyWebPlaceMapping.php create mode 100644 app/Models/PropertyWebPopup.php create mode 100644 app/Models/PropertyWebReview.php create mode 100644 app/Models/PropertyWebRoomMapping.php create mode 100644 app/Models/PropertyWebSetup.php create mode 100644 app/Models/PropertyWebTemplate.php create mode 100644 app/Models/PropertyWebWeather.php create mode 100644 app/Models/ServiceLog.php create mode 100644 app/Models/SiteConfig.php create mode 100644 app/Models/TempProperty.php create mode 100644 app/Models/User.php create mode 100644 app/Models/UserPropertyMapping.php create mode 100644 app/Models/VWDestination.php create mode 100644 app/Models/vwActiveProperty.php create mode 100644 app/Models/vwBookingEngineSearch.php create mode 100644 app/Models/vwBookingSummary.php create mode 100644 app/Models/vwBookingSummaryAll.php create mode 100644 app/Notifications/DirectPushNotificationUser.php create mode 100644 app/Notifications/NewUserNotification.php create mode 100644 app/Notifications/PushNotificationPropertyUser.php create mode 100644 app/Providers/AppServiceProvider.php create mode 100644 app/Providers/AuthServiceProvider.php create mode 100644 app/Providers/EventServiceProvider.php create mode 100644 artisan create mode 100644 bootstrap/app.php create mode 100644 composer.json create mode 100644 config/app.php create mode 100644 config/cache.php create mode 100644 config/database.php create mode 100644 config/filesystems.php create mode 100644 config/language-pack-de.php create mode 100644 config/language-pack-en.php create mode 100644 config/language-pack-tr.php create mode 100644 config/languageSupport.php create mode 100644 config/mail.php create mode 100644 config/queue.php create mode 100644 database/factories/ModelFactory.php create mode 100644 database/migrations/.gitkeep create mode 100644 database/migrations/2019_09_17_103201_create_user_table.php create mode 100644 database/migrations/2019_09_17_103202_create_language_table.php create mode 100644 database/migrations/2019_09_17_103203_create_property_chain_table.php create mode 100644 database/migrations/2019_09_17_103204_create_currency_table.php create mode 100644 database/migrations/2019_09_17_103205_create_property_type_table.php create mode 100644 database/migrations/2019_09_17_103610_create_property_table.php create mode 100644 database/migrations/2019_09_17_110258_create_temp_property_table.php create mode 100644 database/migrations/2019_09_17_121431_create_user_property_mapping_table.php create mode 100644 database/migrations/2019_09_25_112056_create_property_content_category_table.php create mode 100644 database/migrations/2019_09_26_123443_create_property_content_table.php create mode 100644 database/migrations/2019_09_30_130301_create_property_fact_table.php create mode 100644 database/migrations/2019_09_30_130303_create_property_unit_table.php create mode 100644 database/migrations/2019_09_30_130304_create_property_fact_attribute_table.php create mode 100644 database/migrations/2019_09_30_131650_create_property_fact_attribute_mapping_table.php create mode 100644 database/migrations/2019_09_30_132133_create_property_fact_mapping_table.php create mode 100644 database/migrations/2019_10_02_111700_create_property_contact_table.php create mode 100644 database/migrations/2019_10_02_120109_create_property_executive_type_table.php create mode 100644 database/migrations/2019_10_02_120724_create_property_executive_table.php create mode 100644 database/migrations/2019_10_02_133201_create_property_additional_info_key_table.php create mode 100644 database/migrations/2019_10_02_134621_create_property_additional_info_table.php create mode 100644 database/migrations/2019_10_07_141400_create_property_photo_table.php create mode 100644 database/migrations/2019_10_11_121418_create_property_config_table.php create mode 100644 database/migrations/2019_10_14_111402_create_property_module_table.php create mode 100644 database/migrations/2019_10_18_102754_create_permission_table.php create mode 100644 database/migrations/2019_11_06_103124_create_api_access_token_table.php create mode 100644 database/migrations/2019_11_14_174036_add_hash_key_to_user.php create mode 100644 database/migrations/2019_11_15_151514_create_jobs_table.php create mode 100644 database/migrations/2019_12_18_104857_create_site_config_table.php create mode 100644 database/migrations/2019_12_24_101312_create_property_room_type_table.php create mode 100644 database/migrations/2019_12_24_162318_create_property_room_table.php create mode 100644 database/migrations/2019_12_25_173749_create_property_room_bed_type_table.php create mode 100644 database/migrations/2019_12_26_094857_create_property_room_bed_table.php create mode 100644 database/migrations/2019_12_27_111441_create_property_channel_category_table.php create mode 100644 database/migrations/2019_12_27_135558_create_property_channel_table.php create mode 100644 database/migrations/2019_12_27_171704_create_property_channel_mapping_table.php create mode 100644 database/migrations/2019_12_30_160355_create_property_room_rate_table.php create mode 100644 database/migrations/2019_12_31_104604_create_property_room_rate_mapping_table.php create mode 100644 database/migrations/2019_12_31_134435_create_property_room_rate_inclusion_mapping_table.php create mode 100644 database/migrations/2020_01_02_110521_create_property_room_rate_channel_mapping_table.php create mode 100644 database/migrations/2020_01_02_140905_create_property_room_rate_mapping_setup_table.php create mode 100644 database/migrations/2020_01_02_170939_create_property_room_rate_price_table.php create mode 100644 database/migrations/2020_01_03_124014_create_property_room_availability_table.php create mode 100644 database/migrations/2020_01_07_122454_add_max_stay_to_property_room_rate.php create mode 100644 database/migrations/2020_01_15_165436_add_image_to_property_channel.php create mode 100644 database/migrations/2020_02_13_145243_drop_unique_to_property_executive.php create mode 100644 database/migrations/2020_02_18_113758_add_mobile2_to_property_contact.php create mode 100644 database/migrations/2020_02_18_153248_add_social_media_addresses_to_property_contact.php create mode 100644 database/migrations/2020_05_18_154634_create_offer_table.php create mode 100644 database/migrations/2020_05_27_174534_create_property_room_fact_mapping_table.php create mode 100644 database/migrations/2020_05_28_123105_create_property_room_photo_mapping_table.php create mode 100644 database/migrations/2020_05_28_203313_create_property_brand_table.php create mode 100644 database/migrations/2020_06_01_201830_change_offer_accommodation_mapping_date_format.php create mode 100644 database/migrations/2020_06_05_114115_add_column_offer_table_offer_code.php create mode 100644 database/migrations/2020_06_05_162856_add_column_offer_table_accept_status_column.php create mode 100644 database/migrations/2020_06_09_095128_add_is_cover_photo_to_offer_photo_mapping.php create mode 100644 database/migrations/2020_06_09_135659_change_offer_table_status_and_accept_status_fields.php create mode 100644 database/migrations/2020_06_10_142445_change_property_table_property_type_id_name_official_name_fields.php create mode 100644 database/migrations/2020_06_12_213428_update_property_fact_table_icons.php create mode 100644 database/migrations/2020_06_16_093408_drop_column_from_offer_cancellation_policy.php create mode 100644 database/migrations/2020_06_16_171417_update_fact_order.php create mode 100644 database/migrations/2020_06_17_145049_update_fact_parent.php create mode 100644 database/migrations/2020_06_17_150913_create_country_table.php create mode 100644 database/migrations/2020_06_18_103740_update_property_fact_data.php create mode 100644 database/migrations/2020_06_18_132804_country_table_add_data.php create mode 100644 database/migrations/2020_06_18_162035_drop_column_checkin_from_property_table.php create mode 100644 database/migrations/2020_06_18_162612_add_column_property_table_country_field.php create mode 100644 database/migrations/2020_06_18_173416_add_column_country_table_status_field.php create mode 100644 database/migrations/2020_06_19_093503_change_columns_property_table.php create mode 100644 database/migrations/2020_06_19_105117_add_column_extension_field_property_executive_table.php create mode 100644 database/migrations/2020_06_19_105603_add_columns_to_user_table.php create mode 100644 database/migrations/2020_06_22_143629_drop_table_destination_table.php create mode 100644 database/migrations/2020_06_25_134810_add_column_property_room_exclude_occupancy_field.php create mode 100644 database/migrations/2020_06_25_162442_change_columns_property_executive_table.php create mode 100644 database/migrations/2020_06_26_092410_change_columns_property_brand_table.php create mode 100644 database/migrations/2020_06_26_142234_change_table_language_table_create_values.php create mode 100644 database/migrations/2020_06_29_111217_add_data_to_property_additional_info_key.php create mode 100644 database/migrations/2020_06_30_171839_add_column_room_bed_type.php create mode 100644 database/migrations/2020_06_30_174837_change_columns_executive_contact_name_and_property_id_fields.php create mode 100644 database/migrations/2020_06_30_182422_update_data_to_site_config.php create mode 100644 database/migrations/2020_07_02_092052_drop_locale_tables.php create mode 100644 database/migrations/2020_07_03_143603_add_language_key_to_tables.php create mode 100644 database/migrations/2020_07_07_093527_create_table_property_room_view_table.php create mode 100644 database/migrations/2020_07_07_094356_create_property_room_view_mapping_table.php create mode 100644 database/migrations/2020_07_07_134958_add_data_property_type_table.php create mode 100644 database/migrations/2020_07_07_143007_add_data_property_room_type_table.php create mode 100644 database/migrations/2020_07_07_144833_add_data_property_room_bed_type_table.php create mode 100644 database/migrations/2020_07_07_145651_create_property_web_table.php create mode 100644 database/migrations/2020_07_07_152015_add_data_property_executive_type_table.php create mode 100644 database/migrations/2020_07_08_102336_clear_unique_property_executive_table.php create mode 100644 database/migrations/2020_07_08_114601_change_property_chain_table.php create mode 100644 database/migrations/2020_07_08_141717_change_column_language_base_and_language_translate_clear_foreingkeys.php create mode 100644 database/migrations/2020_07_13_104422_add_column_and_create_data_property_room_view_type_table.php create mode 100644 database/migrations/2020_07_14_181858_add_title_language_key_to_property_fact.php create mode 100644 database/migrations/2020_07_22_095749_truncate_related_property_tables.php create mode 100644 database/migrations/2020_07_22_135248_add_icon_to_property_executive_type.php create mode 100644 database/migrations/2020_07_24_143309_add_language_key_to_tables.php create mode 100644 database/migrations/2020_08_10_112926_add_accommodation_type_to_property_room_rate.php create mode 100644 database/migrations/2020_08_12_111815_add_room_size_to_property_room.php create mode 100644 database/migrations/2020_08_13_154333_create_property_web_photo_mapping.php create mode 100644 database/migrations/2020_08_17_103358_drop_destination_id_column_from_property_contact.php create mode 100644 database/migrations/2020_08_17_174028_add_published_dns_checked_to_property_web.php create mode 100644 database/migrations/2020_08_18_132146_drop_and_add_columns_property_brand_table.php create mode 100644 database/migrations/2020_08_20_102828_create_table_room_size_type.php create mode 100644 database/migrations/2020_08_20_105403_add_column_room_size_type_on_property_room_table.php create mode 100644 database/migrations/2020_08_21_113444_create_property_booking_payment_type.php create mode 100644 database/migrations/2020_08_25_101449_add_same_colmuns_to_property_channel.php create mode 100644 database/migrations/2020_08_25_144805_independent_data_update_property_chain_table.php create mode 100644 database/migrations/2020_08_26_174248_update_siteconfig_20200826.php create mode 100644 database/migrations/2020_08_27_173249_update_site_config_20200827.php create mode 100644 database/migrations/2020_08_28_144129_edit_property_channel_mapping_setup_20200828.php create mode 100644 database/migrations/2020_08_28_165030_create_property_nonrefundable_table.php create mode 100644 database/migrations/2020_08_31_133321_add_property_id_to_property_room_rate_channel_mapping.php create mode 100644 database/migrations/2020_09_01_161230_delete_descption_from_property_room.php create mode 100644 database/migrations/2020_09_02_160026_create_property_availability_type.php create mode 100644 database/migrations/2020_09_03_104816_edit_property_channel.php create mode 100644 database/migrations/2020_09_07_103627_add_is_selected_to_property_channel_mapping_setup.php create mode 100644 database/migrations/2020_09_07_162450_change_title_column_property_brand.php create mode 100644 database/migrations/2020_09_08_165655_update_property_executive_type_data.php create mode 100644 database/migrations/2020_09_09_110916_add_ticked_code_to_offer.php create mode 100644 database/migrations/2020_09_11_145607_edit_property_channel_currency_column.php create mode 100644 database/migrations/2020_09_11_165326_add_currency_code_to_property_channel_mapping.php create mode 100644 database/migrations/2020_09_14_112835_inventroy_columns_design.php create mode 100644 database/migrations/2020_09_14_145608_add_description_to_property_room.php create mode 100644 database/migrations/2020_09_15_092617_redesign_property_content.php create mode 100644 database/migrations/2020_09_15_150003_add_file_ext_to_property_brand.php create mode 100644 database/migrations/2020_09_17_112959_add_column_property_channel.php create mode 100644 database/migrations/2020_09_18_171723_add_column_checkin_checkout_offer.php create mode 100644 database/migrations/2020_09_21_105331_drop_is_manually_input.php create mode 100644 database/migrations/2020_09_23_111912_create_property_cancellation_policy_table.php create mode 100644 database/migrations/2020_09_23_113433_add_date_to_property_room_rate_channel_mapping.php create mode 100644 database/migrations/2020_09_23_170133_create_property_cancellation_policy_channel_mapping.php create mode 100644 database/migrations/2020_09_25_095301_create_property_room_pricing_type_table.php create mode 100644 database/migrations/2020_09_25_095528_add_columns_property_channel_mapping.php create mode 100644 database/migrations/2020_09_25_105949_create_property_cancellation_policy_setup.php create mode 100644 database/migrations/2020_09_25_173707_create_property_channel_booking_payment_setup_table.php create mode 100644 database/migrations/2020_09_28_132300_add_is_nonrefundable_to_property_cancellation_policy_setup.php create mode 100644 database/migrations/2020_09_29_094141_drop_property_channel_mapping_setup_table.php create mode 100644 database/migrations/2020_09_29_121744_update_property_room_pricing_type.php create mode 100644 database/migrations/2020_09_29_150218_drop_default_sell_to_room_rate_mapping.php create mode 100644 database/migrations/2020_09_29_161816_add_column_and_data_icon_channel_setup.php create mode 100644 database/migrations/2020_09_30_142848_change_rank_rate_column_property_room_rate_mapping_table.php create mode 100644 database/migrations/2020_09_30_151054_property_room_availability_channel_nullable.php create mode 100644 database/migrations/2020_09_30_152633_change_columns_min_stay_max_stay_property_room_rate_table.php create mode 100644 database/migrations/2020_10_02_100536_person_pricing_module.php create mode 100644 database/migrations/2020_10_05_113528_update_column_data_property_fact_table.php create mode 100644 database/migrations/2020_10_06_135908_set_property_room_pricing_type_code.php create mode 100644 database/migrations/2020_10_09_105034_property_cancellation_policy_setup_merge.php create mode 100644 database/migrations/2020_10_14_175802_property_cancellation_policy_effect_price.php create mode 100644 database/migrations/2020_10_15_112112_property_pricing_policy_adult_refactoring.php create mode 100644 database/migrations/2020_10_15_142300_property_channel_booking_payment_setup_add_channel_mapping_id.php create mode 100644 database/migrations/2020_10_16_154201_update_data_channel_booking_payment_setup_table_and_cancellation_policy_table.php create mode 100644 database/migrations/2020_10_16_161510_booking_engine_migration.php create mode 100644 database/migrations/2020_10_20_113714_update_fact_atrribute_and_unit_tables.php create mode 100644 database/migrations/2020_10_21_162319_change_column_name_cancellation_policy_table.php create mode 100644 database/migrations/2020_10_22_151625_property_room_add_room_count.php create mode 100644 database/migrations/2020_10_27_101720_create_booking_tables.php create mode 100644 database/migrations/2020_11_02_101613_booking_relationships.php create mode 100644 database/migrations/2020_11_02_133050_update_property_fact_table.php create mode 100644 database/migrations/2020_11_03_154256_change_column_room_size_type_on_property_room_table.php create mode 100644 database/migrations/2020_11_04_160316_add_column_property_room_fact_mapping.php create mode 100644 database/migrations/2020_11_09_114658_add_column_is_compatible_with_myweb_slider_property_photos_table.php create mode 100644 database/migrations/2020_11_09_154030_add_column_languages_property_web_table.php create mode 100644 database/migrations/2020_11_10_102122_create_table_property_web_menu.php create mode 100644 database/migrations/2020_11_10_132302_add_column_and_data_property_web_menu_table.php create mode 100644 database/migrations/2020_11_12_131542_create_table_property_web_setup.php create mode 100644 database/migrations/2020_11_13_095302_add_column_and_data_property_web_menu_table.php create mode 100644 database/migrations/2020_11_13_144520_propoerty_room_add_room_type_count_column.php create mode 100644 database/migrations/2020_11_16_141738_property_web_menu_add_alias_column.php create mode 100644 database/migrations/2020_11_18_174057_create_table_web_language_mapping_web_color_mapping_menu_sorting.php create mode 100644 database/migrations/2020_11_26_101121_drop_table_property_web_setup_table.php create mode 100644 database/migrations/2020_11_26_144853_create_table_property_group_and_property_group_mapping_table.php create mode 100644 database/migrations/2020_12_01_101035_add_adult_typeto_property_pricing_policy_adult.php create mode 100644 database/migrations/2020_12_01_105321_create_table_property_web_log_table.php create mode 100644 database/migrations/2020_12_04_143734_change_column_property_web_log_table_ip_field.php create mode 100644 database/migrations/2020_12_08_151737_add_installment_to_payment_type.php create mode 100644 database/migrations/2020_12_11_103651_change_column_pricing_policy_tables_name_field.php create mode 100644 database/migrations/2020_12_14_130930_add_column_property_web_table.php create mode 100644 database/migrations/2020_12_15_134317_create_table_places.php create mode 100644 database/migrations/2020_12_16_113544_change_column_property_room_rate_table_description_field.php create mode 100644 database/migrations/2020_12_16_133400_change_column_property_room_rate_mapping_table_description_field.php create mode 100644 database/migrations/2020_12_19_203908_create_property_place_photo_mapping.php create mode 100644 database/migrations/2020_12_21_103049_delete_property_place_uniqiue_fields.php create mode 100644 database/migrations/2020_12_22_171855_update_site_config_gor_places.php create mode 100644 database/migrations/2020_12_22_175505_payment_type_change_name_data.php create mode 100644 database/migrations/2020_12_26_171403_add_language_code_to_booking_contact.php create mode 100644 database/migrations/2020_12_30_123025_create_table_property_web_about_us_table.php create mode 100644 database/migrations/2020_12_30_130747_create_property_place_fact_title.php create mode 100644 database/migrations/2021_01_04_104332_create_property_place_fact_mapping.php create mode 100644 database/migrations/2021_01_05_174138_create_place_category_field.php create mode 100644 database/migrations/2021_01_06_102008_property_fact_place_title_fact_mapping_data_create.php create mode 100644 database/migrations/2021_01_06_113020_update_data_place_fact_icon_field.php create mode 100644 database/migrations/2021_01_11_105931_modify_column_property_web_about_us_value.php create mode 100644 database/migrations/2021_01_12_115030_property_addtional_info_add_key01.php create mode 100644 database/migrations/2021_01_12_145625_fill_place_category_fields.php create mode 100644 database/migrations/2021_01_13_102953_place_category_field_update_data01.php create mode 100644 database/migrations/2021_01_13_135853_place_category_field_mapping_001.php create mode 100644 database/migrations/2021_01_15_210010_property_web_migrations.php create mode 100644 database/migrations/2021_01_19_102919_add_additioal_info_key_02.php create mode 100644 database/migrations/2021_01_20_141411_add_data_web_menu_table.php create mode 100644 database/migrations/2021_01_23_125159_create_table_general_timezone.php create mode 100644 database/migrations/2021_01_23_132652_add_additional_info_key_03.php create mode 100644 database/migrations/2021_01_23_165950_update_data_property_web_menu.php create mode 100644 database/migrations/2021_01_25_101757_property_unit_fixess.php create mode 100644 database/migrations/2021_01_26_141158_update_data_property_fact_table.php create mode 100644 database/migrations/2021_01_26_164106_property_place_add_fields.php create mode 100644 database/migrations/2021_01_27_090330_add_additional_info_key_04.php create mode 100644 database/migrations/2021_01_29_120411_awards_certificate_tables.php create mode 100644 database/migrations/2021_01_30_143616_add_max_child_number_to_property_room.php create mode 100644 database/migrations/2021_02_01_102416_add_fields_to_payment_transaction.php create mode 100644 database/migrations/2021_02_01_144004_update_data_general_timezone.php create mode 100644 database/migrations/2021_02_02_171050_payment_transaction_type_colum_set_not_null.php create mode 100644 database/migrations/2021_02_03_144403_update_date_property_fact.php create mode 100644 database/migrations/2021_02_03_184742_update_data_property_place_fact.php create mode 100644 database/migrations/2021_02_08_130551_create_data_certificate_and_awards.php create mode 100644 database/migrations/2021_02_10_095958_update_data_site_config.php create mode 100644 database/migrations/2021_02_10_133328_property_payment_transaction_status.php create mode 100644 database/migrations/2021_02_12_094207_property_promotion_tables.php create mode 100644 database/migrations/2021_02_16_130830_update_data_promotion_type.php create mode 100644 database/migrations/2021_02_19_161855_add_created_by_to_payment_transactions.php create mode 100644 database/migrations/2021_02_20_125220_add_column_user_table.php create mode 100644 database/migrations/2021_03_01_125017_add_data_promotion_type_table.php create mode 100644 database/migrations/2021_03_03_163031_add_data_currency_table.php create mode 100644 database/migrations/2021_03_10_154716_change_columns_property_channel_mapping_table.php create mode 100644 database/migrations/2021_03_11_154806_create_property_room_rate_price_queue_table.php create mode 100644 database/migrations/2021_03_15_114954_create_booking_status_table.php create mode 100644 database/migrations/2021_03_17_125514_add_key_property_addtional_info_key_table.php create mode 100644 database/migrations/2021_03_18_172703_update_icon_property_fact_table.php create mode 100644 database/migrations/2021_03_31_134507_add_kvkk_keys.php create mode 100644 database/migrations/archive.zip create mode 100644 database/seeds/DatabaseSeeder.php create mode 100644 database/seeds/DestinationTableSeeder.php create mode 100644 database/seeds/FirstRunSeeder.php create mode 100644 database/seeds/PropertyAdditionalInfoKeyLocaleSeeder.php create mode 100644 database/seeds/PropertyAdditionalInfoKeySeeder.php create mode 100644 database/seeds/PropertyChainSeeder.php create mode 100644 database/seeds/PropertyExecuteSeeder.php create mode 100644 database/seeds/PropertyExecuteTypeLocaleSeeder.php create mode 100644 database/seeds/PropertyExecuteTypeSeeder.php create mode 100644 database/seeds/PropertyFactSeeder.php create mode 100644 database/seeds/PropertyGoogleLabelSeeder.php create mode 100644 database/seeds/PropertyPhotoSeeder.php create mode 100644 database/seeds/PropertyTypeSeeder.php create mode 100644 database/seeds/UserTableSeeder.php create mode 100644 docker-compose.yml create mode 100644 php create mode 100644 phpunit.xml create mode 100644 public/.htaccess create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058432_BU4ZCOX3BL.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058432_BU4ZCOX3BL_medium.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058432_BU4ZCOX3BL_thumbnail.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058498_KXQFEAR72N.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058498_KXQFEAR72N_medium.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058498_KXQFEAR72N_thumbnail.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058692_2PTNCQ6D0Y.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058692_2PTNCQ6D0Y_medium.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058692_2PTNCQ6D0Y_thumbnail.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058726_N4JQ1CR3DD.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058726_N4JQ1CR3DD_medium.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058726_N4JQ1CR3DD_thumbnail.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058727_PC7D0HUQU7.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058727_PC7D0HUQU7_medium.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058727_PC7D0HUQU7_thumbnail.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058728_HPCDZ6OGKO.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058728_HPCDZ6OGKO_medium.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058728_HPCDZ6OGKO_thumbnail.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_8Z63Y323LE.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_8Z63Y323LE_medium.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_8Z63Y323LE_thumbnail.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_U1CXBVYIWS.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_U1CXBVYIWS_medium.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_U1CXBVYIWS_thumbnail.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058964_HVHBI7CM27.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058964_HVHBI7CM27_medium.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058964_HVHBI7CM27_thumbnail.jpg" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/awards-certificates/1778057851_IMD365C7C8.pdf" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/logo/1778057791_250x250.png" create mode 100644 "public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/logo/1778057791_750x750.png" create mode 100644 public/assets/css/bootstrap.css create mode 100644 public/assets/css/load.css create mode 100644 public/assets/fav.ico create mode 100644 public/assets/favicon.ico create mode 100644 public/assets/img/banner/shape/shape-1.png create mode 100644 public/assets/img/banner/shape/shape-2.png create mode 100644 public/assets/img/banner/shape/shape-3.png create mode 100644 public/assets/img/banner/shape/shape-4.png create mode 100644 public/assets/img/banner/shape/shape-4_tmp19510 create mode 100644 public/assets/img/logo-white.png create mode 100644 public/assets/img/logo.svg create mode 100644 public/assets/img/main-image.png create mode 100644 public/assets/js/config.js create mode 100644 public/assets/js/jquery-2.2.4.min.js create mode 100644 public/assets/manifest.json create mode 100644 public/docs.html create mode 100644 public/excel/.gitignore create mode 100644 public/img/background.jpg create mode 100644 public/img/channel-icons/agodacom.png create mode 100644 public/img/channel-icons/algotels.png create mode 100644 public/img/channel-icons/bookingcom.png create mode 100644 public/img/channel-icons/default.psd create mode 100644 public/img/channel-icons/edreams.png create mode 100644 public/img/channel-icons/expediacom.png create mode 100644 public/img/channel-icons/google.png create mode 100644 public/img/channel-icons/holidaycheck.png create mode 100644 public/img/channel-icons/hopper.png create mode 100644 public/img/channel-icons/hotelscom.png create mode 100644 public/img/channel-icons/prestigiacom.png create mode 100644 public/img/channel-icons/priceline.png create mode 100644 public/img/channel-icons/stayforlong.png create mode 100644 public/img/channel-icons/travelupcom.png create mode 100644 public/img/channel-icons/tripadvisor.png create mode 100644 public/img/channel-icons/tripcom.png create mode 100644 public/img/channel-icons/trivago.png create mode 100644 public/img/channel-icons/viocom.png create mode 100644 public/img/channel-icons/zenhotelscom.png create mode 100644 public/img/component-icons/googleads.png create mode 100644 public/img/component-icons/googleanalytics.png create mode 100644 public/img/component-icons/googlereview.png create mode 100644 public/img/component-icons/googlesiteverification.png create mode 100644 public/img/component-icons/googletagmanager.png create mode 100644 public/img/component-icons/hotjar.png create mode 100644 public/img/component-icons/jotform.png create mode 100644 public/img/component-icons/metapixel.png create mode 100644 public/img/component-icons/microsoftclarity.png create mode 100644 public/img/component-icons/sojern.png create mode 100644 public/img/component-icons/tawkto.png create mode 100644 public/img/component-icons/template.psd create mode 100644 public/img/logo.png create mode 100644 public/img/logo_small.png create mode 100644 public/img/noise.gif create mode 100644 public/img/redirect-bank.png create mode 100644 public/img/redirecting.gif create mode 100644 public/img/weather-icons-monochrome/clear-day.png create mode 100644 public/img/weather-icons-monochrome/clear-night.png create mode 100644 public/img/weather-icons-monochrome/cloudy.png create mode 100644 public/img/weather-icons-monochrome/fog.png create mode 100644 public/img/weather-icons-monochrome/hail.png create mode 100644 public/img/weather-icons-monochrome/partly-cloudy-day.png create mode 100644 public/img/weather-icons-monochrome/partly-cloudy-night.png create mode 100644 public/img/weather-icons-monochrome/rain-snow-showers-day.png create mode 100644 public/img/weather-icons-monochrome/rain-snow-showers-night.png create mode 100644 public/img/weather-icons-monochrome/rain-snow.png create mode 100644 public/img/weather-icons-monochrome/rain.png create mode 100644 public/img/weather-icons-monochrome/showers-day.png create mode 100644 public/img/weather-icons-monochrome/showers-night.png create mode 100644 public/img/weather-icons-monochrome/sleet.png create mode 100644 public/img/weather-icons-monochrome/snow-showers-day.png create mode 100644 public/img/weather-icons-monochrome/snow-showers-night.png create mode 100644 public/img/weather-icons-monochrome/snow.png create mode 100644 public/img/weather-icons-monochrome/thunder-rain.png create mode 100644 public/img/weather-icons-monochrome/thunder-showers-day.png create mode 100644 public/img/weather-icons-monochrome/thunder-showers-night.png create mode 100644 public/img/weather-icons-monochrome/thunder.png create mode 100644 public/img/weather-icons-monochrome/wind.png create mode 100644 public/img/weather-icons/clear-day.png create mode 100644 public/img/weather-icons/clear-night.png create mode 100644 public/img/weather-icons/cloudy.png create mode 100644 public/img/weather-icons/fog.png create mode 100644 public/img/weather-icons/hail.png create mode 100644 public/img/weather-icons/partly-cloudy-day.png create mode 100644 public/img/weather-icons/partly-cloudy-night.png create mode 100644 public/img/weather-icons/rain-snow-showers-day.png create mode 100644 public/img/weather-icons/rain-snow-showers-night.png create mode 100644 public/img/weather-icons/rain-snow.png create mode 100644 public/img/weather-icons/rain.png create mode 100644 public/img/weather-icons/showers-day.png create mode 100644 public/img/weather-icons/showers-night.png create mode 100644 public/img/weather-icons/sleet.png create mode 100644 public/img/weather-icons/snow-showers-day.png create mode 100644 public/img/weather-icons/snow-showers-night.png create mode 100644 public/img/weather-icons/snow.png create mode 100644 public/img/weather-icons/thunder-rain.png create mode 100644 public/img/weather-icons/thunder-showers-day.png create mode 100644 public/img/weather-icons/thunder-showers-night.png create mode 100644 public/img/weather-icons/thunder.png create mode 100644 public/img/weather-icons/wind.png create mode 100644 public/index.php create mode 100644 public/openapi.yaml create mode 100644 resources/data/AE.csv create mode 100644 resources/data/AT.csv create mode 100644 resources/data/BE.csv create mode 100644 resources/data/BG.csv create mode 100644 resources/data/BR.csv create mode 100644 resources/data/CA.csv create mode 100644 resources/data/CH.csv create mode 100644 resources/data/DE.csv create mode 100644 resources/data/DK.csv create mode 100644 resources/data/EG.csv create mode 100644 resources/data/ES.csv create mode 100644 resources/data/FI.csv create mode 100644 resources/data/FR.csv create mode 100644 resources/data/IT.csv create mode 100644 resources/data/LU.csv create mode 100644 resources/data/MV.csv create mode 100644 resources/data/MX.csv create mode 100644 resources/data/NL.csv create mode 100644 resources/data/PR.csv create mode 100644 resources/data/SE.csv create mode 100644 resources/data/TR-AYT.csv create mode 100644 resources/data/TR.csv create mode 100644 resources/data/UK.csv create mode 100644 resources/data/US.csv create mode 100644 resources/data/data.json create mode 100644 resources/data/geography.xml create mode 100644 resources/data/google-vision-authentication.json create mode 100644 resources/postman/api-enw-collection.json create mode 100644 resources/postman/auth-login.postman_collection.json create mode 100644 resources/postman/auth-user.postman_collection.json create mode 100644 resources/views/.gitkeep create mode 100644 resources/views/emails/_propertyProductOfferMail.blade.php create mode 100644 resources/views/emails/affiliateRequestMail.blade.php create mode 100644 resources/views/emails/bookingCancellationConfirmCode.blade.php create mode 100644 resources/views/emails/bookingCancellationRequest.blade.php create mode 100644 resources/views/emails/bookingEngineSearchReportMail.blade.php create mode 100644 resources/views/emails/bookingInvoiceUpdateMail.blade.php create mode 100644 resources/views/emails/bookingPaymentDataCode.blade.php create mode 100644 resources/views/emails/bookingPropertyAddonUpdateMail.blade.php create mode 100644 resources/views/emails/bookingTicketMail.blade.php create mode 100644 resources/views/emails/cancelBookingMail.blade.php create mode 100644 resources/views/emails/channelManagerNotificationMail.blade.php create mode 100644 resources/views/emails/contactFormMail.blade.php create mode 100644 resources/views/emails/createUserMail.blade.php create mode 100644 resources/views/emails/dailyReportMail.blade.php create mode 100644 resources/views/emails/dailyReportMailSales.blade.php create mode 100644 resources/views/emails/enwContactFormMail.blade.php create mode 100644 resources/views/emails/inventoryActionMail.blade.php create mode 100644 resources/views/emails/inventoryPdfLinkMail.blade.php create mode 100644 resources/views/emails/logMail.blade.php create mode 100644 resources/views/emails/manualPaymentMail.blade.php create mode 100644 resources/views/emails/modifiedBookingMail.blade.php create mode 100644 resources/views/emails/newBookingMail.blade.php create mode 100644 resources/views/emails/offerAcceptMail.blade.php create mode 100644 resources/views/emails/offerMail.blade.php create mode 100644 resources/views/emails/offerPreConfirmCustomerMail.blade.php create mode 100644 resources/views/emails/offerPreConfirmPropertyMail.blade.php create mode 100644 resources/views/emails/propertyCatalogMail.blade.php create mode 100644 resources/views/emails/propertyProductOfferMail.blade.php create mode 100644 resources/views/emails/reminder/trialFirstMail.blade.php create mode 100644 resources/views/emails/reminder/trialSecondMail.blade.php create mode 100644 resources/views/emails/userForgotPassword.blade.php create mode 100644 resources/views/emails/wellCome.blade.php create mode 100644 resources/views/errors/404.blade.php create mode 100644 resources/views/index.blade.php create mode 100644 resources/views/pdf/PropertyProductOffer.blade.php create mode 100644 resources/views/pdf/hotelContent.blade.php create mode 100644 resources/views/pdf/inventory.blade.php create mode 100644 resources/views/pdf/priceComparison.blade.php create mode 100644 resources/views/pdf/propertyCatalog.blade.php create mode 100644 resources/views/threeDSecureForm.blade.php create mode 100644 routes/web.php create mode 100644 storage/Tripadvisor.json create mode 100644 storage/agoda.json create mode 100644 storage/app/.gitignore create mode 100644 storage/check_rooms.php create mode 100644 storage/fact.log create mode 100644 storage/framework/cache/.gitignore create mode 100644 storage/google.json create mode 100644 storage/logs/.gitignore create mode 100644 storage/off_fact.log create mode 100644 storage/response.json create mode 100644 storage/tmp.csv create mode 100644 tests/ExampleTest.php create mode 100644 tests/TestCase.php create mode 100644 text.txt.php create mode 100644 todo.md diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..8e86a02 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,102 @@ +kind: pipeline +type: docker +name: api-extranetwork + +# Sadece main branch'te tetiklenir +trigger: + branch: + - main + event: + - push + +steps: + # ------------------------------------------------------- + # 1. Bağımlılıkları kur + # ------------------------------------------------------- + - name: composer-install + image: composer:2.2 + volumes: + - name: composer-cache + path: /root/.composer/cache + commands: + - composer install --no-dev --optimize-autoloader --no-interaction --prefer-dist + + # ------------------------------------------------------- + # 2. PHP Unit testlerini çalıştır + # ------------------------------------------------------- + - name: tests + image: php:7.3-cli + commands: + - apt-get update -qq + - apt-get install -y --no-install-recommends libzip-dev libpng-dev libxml2-dev libonig-dev > /dev/null 2>&1 + - docker-php-ext-install pdo pdo_mysql zip mbstring > /dev/null 2>&1 + - cp .env.example .env + - php artisan key:generate + - vendor/bin/phpunit --no-coverage --stop-on-failure + environment: + DB_CONNECTION: sqlite + DB_DATABASE: ":memory:" + depends_on: + - composer-install + + # ------------------------------------------------------- + # 3. Docker image build & Gitea Container Registry'e push + # ------------------------------------------------------- + - name: docker-build-push + image: plugins/docker + settings: + registry: git.extranetwork.net + repo: git.extranetwork.net/gitea/api-extranetwork + username: + from_secret: GITEA_USER + password: + from_secret: GITEA_TOKEN + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + build_args: + - APP_ENV=production + depends_on: + - tests + when: + branch: + - main + event: + - push + + # ------------------------------------------------------- + # 4. SSH ile sunucuya deploy et + # ------------------------------------------------------- + - name: deploy + image: appleboy/drone-ssh + settings: + host: + from_secret: DEPLOY_HOST + user: + from_secret: DEPLOY_USER + key: + from_secret: DEPLOY_SSH_KEY + port: 22 + script: + - cd /opt/api.extranetwork.com + - docker compose pull + - docker compose up -d --remove-orphans + - docker compose exec -T app php artisan migrate --force + - docker compose exec -T app php artisan config:cache + - docker compose exec -T app php artisan route:cache + - docker compose exec -T app php artisan view:cache + - echo "Deploy tamamlandı - $(date)" + depends_on: + - docker-build-push + when: + branch: + - main + event: + - push + +# ------------------------------------------------------- +# Uçucu volume (composer cache pipeline içinde paylaşılır) +# ------------------------------------------------------- +volumes: + - name: composer-cache + temp: {} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a7f46ec --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.yml] +indent_size = 2 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c9a76b4 --- /dev/null +++ b/.env.example @@ -0,0 +1,54 @@ +APP_NAME=Lumen +APP_ENV=local +APP_KEY=bae48aba23b3e4395b7f1b484dd25192 +APP_DEBUG=true +APP_URL=http://api.extranetwork.local +APP_TIMEZONE=Europe/Istanbul +CLIENT_SERVER=http://www.extranetwork.com + +LOG_CHANNEL=stack +LOG_SLACK_WEBHOOK_URL= + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=enw_20201106 +DB_USERNAME=root +DB_PASSWORD=1722 + +MAIL_DRIVER=log +MAIL_HOST=smtp.yandex.com.tr +MAIL_PORT=465 +MAIL_USERNAME=noreply@extranetwork.com +MAIL_PASSWORD=8KSLQzxuGg4vEfSFkaA4 +MAIL_ENCRYPTION=ssl + + +JWT_SECRET=JhbGciOiJIUzI1N0eXAiOiJKV1QiLC + + +CACHE_DRIVER=redis +SESSION_DRIVER=redis +QUEUE_DRIVER=sync +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + + +FILESYSTEM_DRIVER=C:\www\api.extranetwork.com\public +IMAGE_URL=http://api.extranetwork.local +GOOGLE_APPLICATION_CREDENTIALS='' + +QUEUE_CONNECTION=database +APP_LANGUAGE_JSON_STORED_FOLDER=C:\www\www.extranetwork.com\public +MAIN_HOST_ADDRESS=http://myweb.extranetwork.local + +MYWEB_LANGUAGE_JSON_STORED_FOLDER=C:\www\www.myweb.com\resources\lang\ +BOOKING_ENGINE_LANGUAGE_JSON_STORED_FOLDER=C:\www\be.extranetwork.com\resources\lang\ +BOOKING_ENGINE_URL=http://be.extranetwork.local +ZIP_FILES_URL=http://api.extranetwork.local/property-zip-files/ +PAYMENT_FORM_LINK=http://be.extranetwork.local/link/ +ENW_CONTACT_FORM_MAILTO= + +STRIPE_ENWKEY=sk_test_51HuYHvEa9cmPdLq3ANG7ZYaGB9zMuhQZlwH19axJRauZsMnnpnuGBN1h8iAfr9kNVWe4FWcEcvZiMjn3hhBELHHx00hiBgjO41 +PROPERTY_FILES_URL=http://local-api.extranetwork.com/property-files/ \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1eeeaaf --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +/vendor +/.idea +/.vscode +Homestead.json +Homestead.yaml +.env +composer.lock + +# Büyük SQL dosyaları +*.sql + +# Public upload klasörleri +/public/property-photos/ +/public/property-files/ +/public/property-zip-files/ +/public/property-map/ +/public/property-group/ +/public/property-catalog/ +/public/property-web-content/ +/public/property-web-popup/ +/public/blog/ + +# Storage (log, cache vs.) +/storage/logs/* +!/storage/logs/.gitignore +/storage/framework/cache/data/ +/storage/framework/sessions/ +/storage/framework/views/ + +/resources/lang/ + +# OS +.DS_Store +Thumbs.db + +# Node +node_modules/ +npm-debug.log diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 0000000..fcd4cf0 --- /dev/null +++ b/.styleci.yml @@ -0,0 +1,6 @@ +php: + preset: laravel + disabled: + - unused_use +js: true +css: true diff --git a/API_FLOW.md b/API_FLOW.md new file mode 100644 index 0000000..4414c7d --- /dev/null +++ b/API_FLOW.md @@ -0,0 +1,391 @@ +# API Akış Şeması — ExtraNetWork + +300+ endpoint, 60+ controller, 9 channel manager entegrasyonu, 3 metasearch entegrasyonu. + +--- + +## 1. Middleware Zinciri + +```mermaid +graph LR + A[Client] --> B[cors] + B --> C[LanguageSetting] + C --> D{Public?} + D -->|Evet| Z[Controller] + D -->|Hayır| E[jwt.auth] + E --> F[userRoutePermissionAuthorize] + F --> G[property] + G --> H{Wizard gerekli mi?} + H -->|Evet| I[contentWizard] + H -->|Hayır| J{Channel sync?} + I --> J + J -->|Evet| K[checkPropertyChannelConnection] + J -->|Hayır| Z + K --> Z + Z --> S[Service] + S --> R[Repository] + R --> DB[(MySQL)] +``` + +| Middleware | Amaç | +| -------------------------------- | ---------------------------------------------- | +| `cors` | CORS başlıkları | +| `LanguageSetting` | TR/EN/DE dil seçimi | +| `jwt.auth` | JWT + `api_access_token` doğrulama | +| `userRoutePermissionAuthorize` | RBAC permission kontrolü | +| `property` | `user_property_mapping` ile property ownership | +| `contentWizard` | Onboarding tamamlanma kontrolü | +| `checkPropertyChannelConnection` | Channel bağlantı durumu | +| `bookingEngineToken` | BookingEngine widget token | +| `myWebToken` | MyWeb template token | + +--- + +## 2. Genel Request Lifecycle + +```mermaid +sequenceDiagram + participant C as Client + participant MW as Middleware Stack + participant Ctrl as Controller + participant Svc as Service + participant Repo as Repository + participant DB as MySQL + + C->>MW: HTTP + authToken + MW->>MW: cors → lang → jwt → permission → property → wizard + MW->>Ctrl: Request + credentials + property + Ctrl->>Ctrl: Validator->validate(params) + Ctrl->>Svc: business call + Svc->>Repo: findByCriteria / create / update + Repo->>DB: SQL + DB-->>Repo: rows + Repo-->>Svc: data + Svc-->>Ctrl: ['status','data','message'] + Ctrl-->>C: apiResponse(status, message, data, code) +``` + +--- + +## 3. Auth Akışı (Login / Refresh / Logout) + +```mermaid +sequenceDiagram + participant U as User + participant A as AuthController + participant J as JwtService + participant T as ApiAccessTokenService + participant M as UserPropertyMappingService + participant DB + + U->>A: POST auth/login {email, password, remember_me} + A->>DB: user where email & status=1 + A->>A: Hash::check + A->>J: jwtCreate(user_id, remember_me, day_counter=5) + J-->>A: {token, exp} + A->>T: create({token: md5(jwt), expire_date, user_id, invalidate=0}) + A->>M: select(user_property_mapping where user_id, status=1) + M-->>A: property_list + A-->>U: {token, expire_time, locale, property_list, user} + + Note over U,A: Refresh + U->>A: GET auth/refresh-token (authToken header) + A->>T: token bul (md5, expire>now, invalidate=0) + A->>J: jwtCreate(day_counter=0.5) + A->>T: update aynı token row + A-->>U: {new token, expire_time} + + Note over U,A: Logout + U->>A: POST logout (authToken header) + A->>T: update invalidate=1 + A-->>U: 200 Logged out +``` + +--- + +## 4. Property Erişim Kontrolü + +```mermaid +graph TD + A[Request + property_id] --> B[jwt.auth ✓] + B --> C[userRoutePermissionAuthorize] + C -->|permission yok| X[403] + C --> D[PropertyMiddleware] + D --> E{user_property_mapping
user_id+property_id+status=1} + E -->|yok| X + E --> F{Wizard route?} + F -->|Evet| G{property.wizard_status
= complete?} + G -->|Hayır| Y[422 Wizard incomplete] + G --> H[Controller] + F -->|Hayır| H + H --> I[Service: property_id ile scope] + I --> J[(DB)] +``` + +--- + +## 5. Booking Lifecycle (BookingEngine) + +```mermaid +sequenceDiagram + participant G as Guest + participant BE as BookingEngine Widget + participant API as BookingEngine\BookingController + participant Inv as Inventory/Rate + participant Pay as PaymentLinkController + participant DB + + G->>BE: arama (checkin/checkout, pax) + BE->>API: POST /v1/search + API->>Inv: availability + rates + Inv->>DB: property_room_rate_mapping + availability + API-->>BE: rate listesi + + G->>BE: oda seç + BE->>API: POST /v1/booking + API->>DB: insert booking + booking_contact + booking_room + booking_room_pax + API-->>BE: booking_code (status=2 Pending) + + G->>BE: ödemeyi onayla + BE->>API: POST /v1/bookingConfirm + API->>Pay: paymentLinkInitialize + Pay->>DB: insert payment_transaction (status=2 Start) + Pay-->>BE: redirect URL + + G->>Pay: 3D Secure / POS + Pay->>API: callback /paymentRedirect/{code} + API->>DB: payment_transaction.status=1, booking.status=1 + API->>API: send confirmation email + API-->>G: confirmation page +``` + +**Status kodları:** + +- `booking.status`: 0=İptal/Refund, 1=Confirmed, 2=Pending +- `booking_payment.status`: 0=İptal, 1=Confirmed, 2=Pending +- `payment_transaction.status`: 0=Error, 1=Success, 2=Start, 3=Pending, 4=Cancel/Refund, 5=Manual + +--- + +## 6. Channel Sync Akışı + +```mermaid +graph TD + A[Property Manager] -->|property/property-channel-mapping/add| B[PropertyChannelMappingController] + B --> C[(property_channel_mapping)] + C --> D[Job: PropertyCatalogServiceJob] + D --> E{Channel?} + E -->|Reseliva| R1[ChannelManager/Reseliva] + E -->|Channex| R2[ChannelManager/Channex] + E -->|HotelRunner| R3[ChannelManager/HotelRunner] + E -->|ElektraWeb| R4[ChannelManager/ElektraWeb] + E -->|Athena/Fina/SistemOtel/1C/HyperGuest| R5[Diğer Adapters] + R1 & R2 & R3 & R4 & R5 --> X[Remote Channel API] + X --> L[Sync log + status update] + L --> N[Dashboard'da görünür] +``` + +--- + +## 7. Inventory & Rate Update (Channel-Connection-Protected) + +```mermaid +sequenceDiagram + participant M as Manager + participant API as PropertyRoomRateMappingController + participant CC as checkPropertyChannelConnection + participant DB + participant Ch as Remote Channel + + M->>API: POST property/room-rate-mapping/bulk-update + API->>CC: Channel bağlı mı? + CC->>DB: property_channel_mapping.status + alt Bağlı değil + CC-->>M: 403 Channel disconnected + else Bağlı + API->>DB: update room_rate_mapping (price/availability) + API->>Ch: push update + Ch-->>API: ack + API-->>M: success + end +``` + +--- + +## 8. Wizard Onboarding + +```mermaid +graph TD + A[property/create] --> B[(property: wizard incomplete)] + B --> C1[contact/update] + B --> C2[room/add-room-bed] + B --> C3[room-fact-mapping/update] + B --> C4[room-photo-mapping/update] + B --> C5[awards-certificates/list] + B --> C6[fact/get-subcategory-facts] + B --> C7[executive/list] + C1 & C2 & C3 & C4 & C5 & C6 & C7 --> D[property/update/content-code] + D --> E[(wizard_status = complete)] + E --> F[Diğer property endpoint'leri açılır] +``` + +--- + +## 9. Web Site Builder (MyWeb) + +```mermaid +sequenceDiagram + participant M as Manager + participant API as PropertyWebController + participant DB + participant FS as FileStorage/CDN + participant V as Visitor + participant MyW as MyWebContentController + + M->>API: web/create → property_web + M->>API: web/update-content → component & content + M->>API: web/meta-tag/sync + M->>API: web/popup/create + M->>API: web/publish → status=PUBLISHED + API->>FS: assets + + V->>MyW: web/home (myWebToken) + MyW->>DB: property_web_content + components + MyW-->>V: render data (frontend tarafı şablonlar) +``` + +--- + +## 10. Reputation Management + +```mermaid +graph TD + A[reputation-management/channel/get] --> B[(reputation_channels)] + A --> C[reputation-management/channel/sync] + C --> J[Job: PropertyReviewServiceJob] + J --> D{Source} + D -->|TripAdvisor| T[TripAdvisor API] + D -->|Google| G[Google Places API] + D -->|Booking| BK[Booking API] + T & G & BK --> R[(reviews)] + R --> AN[Job: PropertyReviewAnalyzeServiceJob
NLP/sentiment] + AN --> S[(review_statistics)] + S --> RS[reputation-management/review/statistics] +``` + +--- + +## 11. CPA (Competitor Price Analysis) + +```mermaid +graph TD + A[cpa/property/competitor/create] --> B[(property_competitors)] + B --> C[cpa/property/competitor/sync] + C --> D[Crawler/OTA API] + D --> E[(competitor_prices günlük)] + E --> F[cpa/property/competitor/analysis] + F --> G[cpa/property/best-available-price] + F --> H[cpa/property/promotion-available] + F --> I[cpa/property/quick-pricing/rate] + G & H & I --> J[Manager dashboard önerileri] +``` + +--- + +## 12. Endpoint Domain Özet Tablosu + +| # | Domain | Controller(lar) | Endpoint | Açıklama | +| --: | ------------------- | ----------------------------------------------------- | -------: | ------------------------------------------------------------------------------------ | +| 1 | Auth | AuthController | 3 | login / refresh / logout | +| 2 | User | UserController | 17 | register, profile, password, mapping | +| 3 | Property | PropertyController | 12 | CRUD + dashboard + raporlar | +| 4 | Property Info | Contact, Brand, Config, Executive | 14 | Detay bilgi yönetimi | +| 5 | Content | Content, Fact, FactMapping, AdditionalInfo | 11 | Property metadata | +| 6 | Photo | PropertyPhoto + Category + Mapping | 11 | Foto yönetimi & CDN | +| 7 | Place | PropertyPlaceController | 15 | Tesis içi yer/alan | +| 8 | Awards | PropertyAwardCertificates | 5 | Sertifikalar | +| 9 | Room | PropertyRoom + Type/View/Bed/Size | 20+ | Oda yapısı | +| 10 | Rate | PropertyRoomRate + Mapping/Channel/Setup/Inclusion | 27 | Rate & inventory | +| 11 | Channel | PropertyChannel + Mapping/Group/Contact/Category | 23 | OTA dağıtım | +| 12 | Cancellation Policy | PropertyCancellationPolicy | 5 | İptal kuralları | +| 13 | Pricing Policy | PropertyPersonPricingPolicy | 7 | Yetişkin/çocuk fiyat | +| 14 | Booking | PropertyBookingController + Ticket | 11 | Rezervasyon ops | +| 15 | Payment | PaymentController + PaymentLink | 16 | Ödeme + manual link + taksit | +| 16 | Offer | PropertyOfferController | 11 | Teklif yönetimi | +| 17 | Promotion | PropertyPromotionController | 7 | Promosyon & kampanya | +| 18 | Coupon/Addon | PropertyCoupon + Addon | 4 | Ek ürün/kupon | +| 19 | CPA | CompetitorPriceAnalysis + Group + QuickPricing | 15+ | Rakip analizi | +| 20 | Web Builder | PropertyWebController + Content/Popup/Component | 30+ | Website yönetimi | +| 21 | MyWeb | MyWebContentController | 22+ | Public site render | +| 22 | Booking Engine | BookingEngine\BookingController + Search | 17 | Embedded widget | +| 23 | Channel Manager | 9 entegrasyon adapter'ı | 21 | Reseliva, Channex, HotelRunner, ElektraWeb, Athena, Fina, SistemOtel, 1C, HyperGuest | +| 24 | MetaSearch | Trivago, Yandex, Google | 8 | Meta arama | +| 25 | Reputation | ReputationManagementController | 4 | Yorum & istatistik | +| 26 | AI | AIController | 1 | OpenAI | +| 27 | Export | ExportPdfController | 5 | PDF/Excel | +| 28 | Utility | Language, Currency, Destination, Chain, Test, Contact | 11 | Referans veri | + +**Toplam:** ~300+ endpoint, ~60 controller dosyası. + +--- + +## 13. Auth Header Konvansiyonu + +| Tip | Header | Doğrulayan | +| --------------- | ----------------------------- | ------------------------------ | +| App API | `authToken: ` | `JwtMiddleware` | +| BookingEngine | `bookingEngineToken: ` | `BookingEngineTokenMiddleware` | +| MyWeb | `myWebToken: ` | `MyWebTokenMiddleware` | +| Channel Manager | partner-specific header | İlgili adapter | + +--- + +## 14. Body Konvansiyonu + +| Endpoint Tipi | Body Şeması | +| ------------------ | -------------------------------------------------------------------------- | +| Login | `{ "email", "password", "remember_me", "locale", "onesignal_key" }` (flat) | +| Tüm App API (POST) | `{ "params": { ... } }` (controller'lar `$this->request->params` okur) | +| BookingEngine | `{ "params": { ... } }` veya path param | +| ChannelManager | adapter'a özel JSON şeması | + +--- + +## 15. Asenkron Olaylar (Queue Driver: database) + +| Job | Tetikleyici | Görev | +| --------------------------------- | ----------------------- | ---------------- | +| `PropertyCatalogServiceJob` | property/channel update | Catalog senkronu | +| `PropertyReviewServiceJob` | reputation sync | Review fetch | +| `PropertyReviewAnalyzeServiceJob` | review save | NLP/sentiment | +| `SlackLogJob` | sistem logu | Slack bildirim | + +Mail kuyrukları: `userCreateMail`, `UserForgotPassword`. + +--- + +## 16. Status Kodları Özeti + +| Tablo | Alan | Değerler | +| ------------------- | ---------- | --------------------------------------------------------------- | +| user | status | 0 inaktif / 1 aktif | +| user | user_type | 0 normal / 1 admin | +| api_access_token | invalidate | 0 geçerli / 1 iptal | +| booking | status | 0 İptal / 1 Onaylı / 2 Pending | +| booking_payment | status | 0 İptal / 1 Onaylı / 2 Pending | +| payment_transaction | status | 0 Error / 1 Success / 2 Start / 3 Pending / 4 Cancel / 5 Manual | +| property | status | 0 inaktif / 1 aktif | + +--- + +## 17. Hızlı Bakış: Tipik İstek Hayatı + +``` +1. Client → POST /app/v1/property/info/get (Header: authToken) +2. cors → LanguageSetting → jwt.auth → userRoutePermissionAuthorize → property +3. PropertyController@getProperty +4. PropertyService->select(criteria scoped to property_id) +5. PropertyRepository->findByCriteria +6. MySQL property + relations (with: 'propertyContact', 'propertyType', ...) +7. apiResponse(1, null, $data, 200) +``` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5c24dd4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,82 @@ +FROM php:7.3-apache-buster + +# Debian Buster EOL olduğu için repo adreslerini archive'a taşıyoruz +RUN sed -i 's|http://deb.debian.org/debian|http://archive.debian.org/debian|g' /etc/apt/sources.list \ + && sed -i 's|http://security.debian.org/debian-security|http://archive.debian.org/debian-security|g' /etc/apt/sources.list \ + && sed -i '/buster-updates/d' /etc/apt/sources.list \ + && apt-get update + +# Sistem paketleri + PHP extension kurulumu +RUN apt-get install -y --no-install-recommends \ + git \ + unzip \ + zip \ + nano \ + curl \ + libzip-dev \ + libpng-dev \ + libjpeg-dev \ + libfreetype6-dev \ + libxml2-dev \ + zlib1g-dev \ + libonig-dev \ + && docker-php-ext-configure gd \ + --with-freetype-dir=/usr/include/ \ + --with-jpeg-dir=/usr/include/ \ + && docker-php-ext-install \ + pdo \ + pdo_mysql \ + gd \ + zip \ + mbstring \ + exif \ + && rm -rf /var/lib/apt/lists/* + +# Composer 2.2 kurulumu - PHP 7.3 için güvenli sürüm +RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ + && php composer-setup.php --2.2 --install-dir=/usr/local/bin --filename=composer \ + && php -r "unlink('composer-setup.php');" + +# Apache modları +RUN a2enmod rewrite + +# Apache VirtualHost ayarı +RUN cat > /etc/apache2/sites-available/000-default.conf <<'EOF' + + ServerName localhost + ServerAdmin webmaster@localhost + + DocumentRoot /var/www/html/public + + + Options FollowSymLinks + AllowOverride All + Require all granted + + + + AllowOverride All + Require all granted + + + Alias /uploads /home/uploads + + + Options FollowSymLinks + AllowOverride None + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + +EOF + +# Upload klasörü +RUN mkdir -p /home/uploads \ + && chown -R www-data:www-data /home/uploads \ + && chmod -R 775 /home/uploads + +WORKDIR /var/www/html + +CMD ["apache2-foreground"] diff --git a/PROJECT_SUMMARY.md b/PROJECT_SUMMARY.md new file mode 100644 index 0000000..22efe74 --- /dev/null +++ b/PROJECT_SUMMARY.md @@ -0,0 +1,233 @@ +# ExtraNetWork API — Teknik Özet + +## Genel Bilgiler + +| Özellik | Değer | +| ------------- | ----------------------------------------- | +| Framework | Laravel Lumen 5.8 | +| PHP | >= 7.1.3 | +| App Versiyonu | 1.5.22 | +| Veritabanı | MySQL (`utf8mb4`, host: `mysql-master`) | +| Queue Driver | Database (jobs tablosu) | +| Mail Driver | SMTP — Gmail (`noreply@extranetwork.com`) | + +--- + +## Mimari + +``` +app/ +├── Http/Controllers/ → 86 Controller +├── Core/ +│ ├── Service/ → 142 Service +│ ├── Repository/ → 176 Repository +│ └── Validator/ → 132 Validator +├── Models/ → 196 Model +├── Jobs/ → 6 Job +├── Events/ → 2 Event +├── Listeners/ → 1 Listener +├── Channels/ → OneSignal push +└── Http/Middleware/ → 11 Middleware +``` + +**Katmanlı mimari:** Controller → Service → Repository → Model + +--- + +## Servisler & URL'ler + +| Servis | URL | +| -------------- | ------------------------------ | +| API | `https://api.extranetwork.com` | +| Web | `https://www.extranetwork.com` | +| Client App | `https://app.extranetwork.com` | +| Booking Engine | `https://be.extranetwork.com` | +| Image CDN | `https://cdn.extranetwork.com` | + +--- + +## Veritabanı (100+ tablo) + +### Ana Tablolar + +| Grup | Tablolar | +| --------------- | --------------------------------------------------------------------------------------------------------- | +| Kullanıcı | `user`, `permission`, `permission_group`, `user_property_mapping` | +| Mülk (Property) | `property`, `property_type`, `property_chain`, `property_brand`, `property_contact`, `property_executive` | +| Oda | `property_room`, `property_room_type`, `property_room_bed`, `property_room_view_type` | +| Fotoğraf | `property_photo`, `property_photo_category`, `property_room_photo_mapping` | +| İçerik | `property_content`, `property_content_category`, `property_fact`, `property_fact_mapping` | +| Fiyat & Oran | `property_room_rate`, `property_room_rate_mapping`, `property_room_rate_channel_mapping` | +| Kanal | `property_channel`, `property_channel_mapping`, `property_channel_category` | +| Rezervasyon | `booking`, `booking_contact`, `booking_room_pax`, `booking_addon`, `booking_payment` | +| Politika | `property_cancellation_policy`, `property_pricing_policy_adult`, `property_pricing_policy_child` | +| Web | `property_web`, `property_web_content`, `property_web_menu`, `property_web_meta_tag` | +| Diğer | `property_promotion`, `property_coupon`, `property_addon`, `property_award_certificate`, `site_config` | + +--- + +## Modeller & İlişkiler + +### Property (Ana Model) + +``` +Property + ├── hasMany → PropertyPhoto, PropertyRoom, PropertyExecutive, PropertyUser + ├── hasOne → PropertyContact, PropertyWeb, PropertyBrand, PropertyChain + └── belongsTo → PropertyType, Destination +``` + +### Booking + +``` +Booking + ├── hasMany → BookingRoomPax, BookingAddon, BookingPayment + └── hasOne → BookingContact +``` + +### Offer + +``` +Offer + ├── hasMany → OfferContactMapping, OfferFactMapping + └── hasOne → OfferPaymentType +``` + +--- + +## Controller Grupları + +### Auth & Kullanıcı + +- `AuthController` — login, refresh-token, logout +- `UserController` — CRUD, şifre sıfırlama, rol yönetimi + +### Property Yönetimi + +- `PropertyController` — CRUD, dashboard, raporlar +- `PropertyPhotoController` — yükleme, sıralama, yayınlama +- `PropertyContactController`, `PropertyExecutiveController`, `PropertyBrandController` +- `PropertyRoomController`, `PropertyRoomBedController`, `PropertyRoomTypeController` + +### Fiyat & Oran + +- `PropertyRoomRateController`, `PropertyRoomRateMappingController` +- `PropertyRoomRateChannelMappingController` +- `PropertyQuickPricingController`, `PropertyPersonPricingPolicyController` +- `PropertyOfferController`, `PropertyNonrefundableController` +- `PropertyCancellationPolicyController` + +### Kanal Yönetimi + +- `PropertyChannelController`, `PropertyChannelMappingController` +- `PropertyChannelGroupController`, `PropertyChannelCategoryController` +- `CompetitorPriceAnalysisController` + +### İçerik & Web + +- `PropertyContentController`, `PropertyFactController` +- `PropertyWebController`, `PropertyWebContentController`, `PropertyWebMenuController` +- `PropertyPlaceController`, `PropertyAdditionalInfoController` + +### Rezervasyon & Ödeme + +- `PropertyBookingController` +- `PaymentController`, `PaymentLinkController` +- `PropertyBookingTicketController` + +### Özel + +- `AIController` — OpenAI entegrasyonu +- `ReputationManagementController` — Review/yorum yönetimi +- `PropertyAwardCertificatesController` +- `ExportPdfController`, `DashboardPlusService` controllers + +--- + +## Middleware + +| Middleware | Görev | +| ------------------------------------------ | ------------------------ | +| `JwtMiddleware` | JWT token doğrulama | +| `CorsMiddleware` | CORS başlıkları | +| `LanguageSettingMiddleware` | Dil seçimi | +| `PropertyMiddleware` | Property context | +| `UserRoutePermissionAuthorize` | RBAC yetki kontrolü | +| `BookingEngineTokenMiddleware` | Booking engine auth | +| `MyWebTokenMiddleware` | MyWeb auth | +| `CheckPropertyChannelConnectionMiddleware` | Kanal bağlantı doğrulama | +| `ContentWizardMiddleware` | İçerik sihirbazı akışı | + +--- + +## Jobs (Queue) + +| Job | Görev | +| --------------------------------- | ---------------------- | +| `PropertyReviewServiceJob` | Review senkronizasyonu | +| `PropertyReviewAnalyzeServiceJob` | Review analizi | +| `PropertyCatalogServiceJob` | Katalog güncellemeleri | +| `SlackLogJob` | Slack bildirim logları | + +--- + +## Route Yapısı + +``` +POST /app/v1/auth/login +POST /app/v1/auth/refresh-token +POST /app/v1/user/register +POST /app/v1/logout + +GET /property-comparison/{weekKey} +POST /web/check-domain + +[Protected - /app/v1] + ├── /property/** → Property CRUD & yönetim + ├── /property-room/** → Oda yönetimi + ├── /property-rate/** → Fiyat & oran + ├── /property-channel/** → Kanal yönetimi + ├── /booking/** → Rezervasyon + ├── /payment/** → Ödeme + ├── /property-web/** → Web içerik + └── /user/** → Kullanıcı yönetimi +``` + +--- + +## Temel Paketler + +| Paket | Sürüm | Kullanım | +| ------------------------- | ------ | -------------- | +| `laravel/lumen-framework` | 5.8 | Framework | +| `firebase/php-jwt` | ^5.0 | JWT auth | +| `stripe/stripe-php` | ^7.66 | Ödeme | +| `google/cloud-vision` | ^0.24 | Görsel analiz | +| `maatwebsite/excel` | ^3.1 | Excel export | +| `barryvdh/laravel-dompdf` | ^0.8.7 | PDF üretimi | +| `intervention/image` | ^2.5 | Görsel işleme | +| `predis/predis` | ^1.1 | Redis | +| `ramsey/uuid` | ^4.1 | UUID üretimi | +| `symfony/translation` | 4.4.1 | Çeviri desteği | + +--- + +## Dil Desteği + +`config/` altında hazır dil paketi dosyaları: + +- `language-pack-en.php` — İngilizce +- `language-pack-de.php` — Almanca +- `language-pack-tr.php` — Türkçe + +--- + +## Entegrasyonlar + +- **Stripe** — Ödeme işlemleri +- **OpenAI** — AI özellikler (`AIController`) +- **Google Cloud Vision** — Görsel analiz +- **OneSignal** — Push bildirim (`OneSignalChannel`) +- **Slack** — Log bildirimleri (`SlackLogJob`) +- **Redis** — Cache & queue +- **Gmail SMTP** — Mail gönderimi diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee4410f --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# api.extranetwork.com + +Laravel 5.x tabanlı ExtraNetwork API backend. + +## Gereksinimler + +- PHP 7.3 +- MySQL 8.0 +- Docker & Docker Compose +- Composer 2.2 + +## Kurulum + +```bash +# Bağımlılıkları kur +composer install + +# .env dosyasını oluştur +cp .env.example .env +php artisan key:generate + +# Veritabanı migrate +php artisan migrate +``` + +## Docker ile Çalıştırma + +```bash +docker compose up -d +``` + +Uygulama `http://localhost:8073` adresinde çalışır. + +## CI/CD + +Drone CI pipeline otomatik olarak: + +1. `composer install` çalıştırır +2. PHPUnit testlerini çalıştırır +3. Docker image build edip Gitea registry'e push eder +4. SSH ile sunucuya deploy eder + +### Drone Secrets (Ayarlanması gerekenler) + +| Secret | Açıklama | +| ---------------- | --------------------------- | +| `GITEA_USER` | Gitea kullanıcı adı | +| `GITEA_TOKEN` | Gitea access token | +| `DEPLOY_HOST` | Deploy sunucusu IP/hostname | +| `DEPLOY_USER` | SSH kullanıcısı | +| `DEPLOY_SSH_KEY` | SSH private key | diff --git a/app/Channels/OneSignalChannel.php b/app/Channels/OneSignalChannel.php new file mode 100644 index 0000000..1135004 --- /dev/null +++ b/app/Channels/OneSignalChannel.php @@ -0,0 +1,22 @@ +toOneSignal($notifiable); + + // Send notification to the $notifiable instance... + } +} diff --git a/app/Console/Commands/.gitkeep b/app/Console/Commands/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/Console/Commands/ApplicationLanguageFiles/ApplicationFillTableLanguageKey.php b/app/Console/Commands/ApplicationLanguageFiles/ApplicationFillTableLanguageKey.php new file mode 100644 index 0000000..4743459 --- /dev/null +++ b/app/Console/Commands/ApplicationLanguageFiles/ApplicationFillTableLanguageKey.php @@ -0,0 +1,48 @@ +languageBaseService = $languageBaseService; + parent::__construct(); + } + + public function handle() + { + try { + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' LANGUAGE BASE START'); + $process = $this->languageBaseService->fillApplicationLanguageKeys(); + if($process['status'] != 'success'){ + throw new Exception($process['message']) ; + + } + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' LANGUAGE BASE FINISHED'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/ApplicationLanguageFiles/ApplicationLanguageBaseData.php b/app/Console/Commands/ApplicationLanguageFiles/ApplicationLanguageBaseData.php new file mode 100644 index 0000000..9da00e8 --- /dev/null +++ b/app/Console/Commands/ApplicationLanguageFiles/ApplicationLanguageBaseData.php @@ -0,0 +1,48 @@ +languageBaseService = $languageBaseService; + parent::__construct(); + } + + public function handle() + { + try { + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' LANGUAGE BASE START'); + $process = $this->languageBaseService->createApplicationLanguageBaseData(); + if($process['status'] != 'success'){ + throw new Exception($process['message']) ; + + } + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' LANGUAGE BASE FINISHED'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/ApplicationLanguageFiles/ApplicationLanguageFiles.php b/app/Console/Commands/ApplicationLanguageFiles/ApplicationLanguageFiles.php new file mode 100644 index 0000000..13a4022 --- /dev/null +++ b/app/Console/Commands/ApplicationLanguageFiles/ApplicationLanguageFiles.php @@ -0,0 +1,48 @@ +languageBaseService = $languageBaseService; + parent::__construct(); + } + + public function handle() + { + try { + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' LANGUAGE CREATE JSON START'); + $process = $this->languageBaseService->createApplicationLanguageFiles(); + if($process['status'] != 'success'){ + throw new Exception($process['message']) ; + + } + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' LANGUAGE CREATE JSON FINISHED'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/ChannelManager/BestAvailableRateSyncService.php b/app/Console/Commands/ChannelManager/BestAvailableRateSyncService.php new file mode 100644 index 0000000..e383bbc --- /dev/null +++ b/app/Console/Commands/ChannelManager/BestAvailableRateSyncService.php @@ -0,0 +1,356 @@ +mailer = $mailer; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyBookingEngineService = $propertyBookingEngineService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => 2],//Channex + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['property'] + ]; + + $howManyDays = 90; + if (!is_null($this->option('property_id'))) { + $requestParam['criteria'][] = ['field' => 'property_id', 'condition' => '=', 'value' => $this->option('property_id')]; + $howManyDays = 6 * 30; + } + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + $channelManagerPropertyMapping = $channelManagerPropertyMapping['status'] == 'success' ? $channelManagerPropertyMapping['data'] : []; + + foreach ($channelManagerPropertyMapping as $propertyMapping) { + + if ($propertyMapping['property']['status'] != 1) { + $this->error(date('Y-m-d H:i:s') . ' : ' . $propertyMapping['property']['name']); + continue; + } + + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyRoomRateChannelMappingParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => 5],//Kanal Yöentimi + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyMapping['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingService->select($propertyRoomRateChannelMappingParam); + $propertyRoomRateChannelMapping = $propertyRoomRateChannelMapping['status'] == 'success' ? $propertyRoomRateChannelMapping['data'] : []; + $propertyRoomRateChannelMappingCollect = collect($propertyRoomRateChannelMapping); + + $propertyBookingEngineParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyMapping['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel', 'property.propertyBookingEngineToken'], + 'firstRow' => 1 + ]; + + $propertyBookingEngine = $this->propertyBookingEngineService->select($propertyBookingEngineParam, ['id', 'property_id', 'channel_id', 'token']); + $propertyBookingEngine = $propertyBookingEngine['status'] == 'success' ? $propertyBookingEngine['data'] : []; + + if (empty($propertyBookingEngine)) { + $this->info(date('Y-m-d H:i:s') . ' : None Booking Engine!'); + continue; + } + + + $searchController = App::make("App\Http\Controllers\BookingEngine\V1\SearchController"); + + + $roomRateFormatted = []; + + + $today = Carbon::now()->startOfDay()->toDateTimeString(); + if (!is_null($this->option('property_id'))) { + $today = Carbon::now()->startOfDay()->toDateTimeString(); + } else { + + //00:00 04:00 08:00 12:00 16:00 20:00 + $processHour = (integer)Carbon::now()->format('H'); + + switch ($processHour) { + + case ($processHour >= 0 && $processHour <= 2) : + case ($processHour > 8 && $processHour <= 10): + case ($processHour > 16 && $processHour <= 18): + $today = Carbon::now()->startOfDay()->toDateTimeString(); + break; + case ($processHour > 2 && $processHour <= 4) : + case ($processHour > 10 && $processHour <= 12): + case ($processHour > 18 && $processHour <= 20) : + $today = Carbon::now()->startOfDay()->addDays($howManyDays)->toDateTimeString(); + break; + case ($processHour > 4 && $processHour <= 6) : + case ($processHour > 12 && $processHour <= 14): + case ($processHour > 20 && $processHour <= 22) : + $today = Carbon::now()->startOfDay()->addDays($howManyDays * 2)->toDateTimeString(); + break; + case ($processHour > 6 && $processHour <= 8) : + case ($processHour > 14 && $processHour <= 16): + case ($processHour > 22 && $processHour < 24) : + $today = Carbon::now()->startOfDay()->addDays($howManyDays * 3)->toDateTimeString(); + break; + + } + + } + + + for ($i = 0; $i < $howManyDays; $i++) { + + $checkIn = Carbon::parse($today)->addDays($i)->toDateString(); + $checkOut = Carbon::parse($checkIn)->addDay()->toDateString(); + + $searchRequestJson = [ + 'date' => [ + 'checkIn' => $checkIn, + 'checkOut' => $checkOut, + ], + 'rooms' => [ + [ + 'adults' => 2, + 'children' => 0, + 'age' => [], + ] + ], + 'property' => [], + 'ipAddress' => '185.137.215.118', + 'isMobile' => null, + 'noneCacheSearch' => true, + 'min_stay_disabled' => true + ]; + + + $requestCreate = Request::create(null, null, [], [], [], [], json_encode($searchRequestJson)); + $requestCreate->headers->set('channelId', '1'); + $requestCreate->headers->set('bookingEnginePropertyId', $propertyMapping['property_id']); + $requestCreate->headers->set('channelToken', $propertyBookingEngine['channel']['token']); + $requestCreate->headers->set('bookingEngineToken', $propertyBookingEngine['property']['property_booking_engine_token']['token']); + + $search = $searchController->search($requestCreate); + $search = json_decode(json_encode($search), 1); + + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn); + + if ($search['original']['status'] == 200) { + + + if (empty($search['original']['data']['properties'])) { + + //Rate Stop or Price 0 Case + foreach ($propertyRoomRateChannelMapping as $roomRateChannelMapping) { + + $referenceRoomRateMapping = $propertyRoomRateChannelMappingCollect + ->where('property_room_rate_mapping.room_id', $roomRateChannelMapping['property_room_rate_mapping']['room_id']) + ->where('property_room_rate_mapping.property_room_rate.name', 'Best Available Rate')->first(); + + if (!empty($referenceRoomRateMapping)) { + $roomRateKey = '1|' . $referenceRoomRateMapping['property_room_rate_mapping']['room_id'] . '|' . $referenceRoomRateMapping['property_room_rate_mapping']['id'] . '|' . $checkIn; + $roomRateFormatted[$roomRateKey] = [ + 'setup_type_id' => '1', + 'room_id' => $referenceRoomRateMapping['property_room_rate_mapping']['room_id'], + 'room_rate_mapping_id' => $referenceRoomRateMapping['property_room_rate_mapping']['id'], + 'date' => $checkIn, + 'amount' => 0 + ]; + } + } + //Rate Stop or Price 0 Case + + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - NONE'); + continue; + } + + $property = reset($search['original']['data']['properties']); + if (empty($property)) { + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - NONE'); + continue; + } + + if (!isset($property['availabilities'])) { + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - NONE'); + continue; + } + + $propertyAvailability = reset($property['availabilities']); + if (empty($propertyAvailability)) { + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - NONE'); + continue; + } + + /*dd($propertyAvailability['rooms']); + + $propertyAvailabilityRoom = reset($propertyAvailability['rooms']); + if (empty($propertyAvailability)) { + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - NONE'); + continue; + } + + $propertyAvailabilityRoom['rates'] = collect($propertyAvailabilityRoom['rates'])->sortBy('total')->toArray(); +*/ + + foreach ($propertyAvailability['rooms'] as $roomId => $room) { + foreach ($room['rates'] as $roomRateInnerKey => $roomRate) { + + /*$propertyAvailabilityRoomRate = reset($propertyAvailabilityRoom['rates']); + if (empty($propertyAvailabilityRoomRate)) { + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - NONE'); + continue; + }*/ + + $propertyAvailabilityRoomRatePrices = reset($roomRate['requestedRoomPrice']); + if (empty($propertyAvailabilityRoomRatePrices)) { + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - NONE'); + continue; + } + + $propertyAvailabilityRoomRatePrice = reset($propertyAvailabilityRoomRatePrices['prices']); + if (empty($propertyAvailabilityRoomRatePrice)) { + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - NONE'); + continue; + } + + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['id'] . ' - ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - ' . $propertyAvailabilityRoomRatePrice['total']); + + + $referenceRoomRateMapping = $propertyRoomRateChannelMappingCollect + ->where('property_room_rate_mapping.room_id', $roomId) + ->where('property_room_rate_mapping.property_room_rate.name', 'Best Available Rate')->first(); + + if (empty($referenceRoomRateMapping)) { + $this->info(date('Y-m-d H:i:s') . ' : None Best Available Rate! PropertyId: ' . $propertyMapping['property_id'] . ' Room: ' . $room['name']); + continue; + } + + /*if($referenceRoomRateMapping['property_room_rate_mapping']['id'] != 6003) { + continue; + }*/ + + + $roomRateKey = '1|' . $referenceRoomRateMapping['property_room_rate_mapping']['room_id'] . '|' . $referenceRoomRateMapping['property_room_rate_mapping']['id'] . '|' . $checkIn; + + if (!isset($roomRateFormatted[$roomRateKey])) { + $roomRateFormatted[$roomRateKey] = [ + 'setup_type_id' => '1', + 'room_id' => $referenceRoomRateMapping['property_room_rate_mapping']['room_id'], + 'room_rate_mapping_id' => $referenceRoomRateMapping['property_room_rate_mapping']['id'], + 'date' => $checkIn, + 'amount' => $propertyAvailabilityRoomRatePrice['total'] + ]; + + } + + if ($propertyAvailabilityRoomRatePrice['total'] < $roomRateFormatted[$roomRateKey]['amount']) { + $roomRateFormatted[$roomRateKey]['amount'] = $propertyAvailabilityRoomRatePrice['total']; + } + + } + + } + + } else { + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['name'] . ' - ' . $checkIn . ' - NONE'); + } + + } + + $requestParams = [ + 'property_id' => $propertyMapping['property_id'], + 'channel_id' => 5, + 'availability' => [], + 'user_id' => 1, + 'rates' => $roomRateFormatted + ]; + + $roomRateUpdate = $this->propertyRoomRatePriceService->roomRateUpdate($requestParams); + + if ($roomRateUpdate['status'] != 'success') { + throw new ApiErrorException($roomRateUpdate['message']); + } + + $response['status'] = true; + + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyBookingEngine['property']['id'] . ' : ' . $propertyBookingEngine['property']['name'] . ' - Today: ' . $today . ' - OK'); + + Log::debug($propertyBookingEngine['property']['id'] . ' : ' . $propertyBookingEngine['property']['name'] . ' - Today: ' . $today . ' - OK'); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $this->error(date('Y-m-d H:i:s') . ' : ' . $response['message']); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + + } +} diff --git a/app/Console/Commands/ChannelManager/MetaCancellationService.php b/app/Console/Commands/ChannelManager/MetaCancellationService.php new file mode 100644 index 0000000..5a8aedc --- /dev/null +++ b/app/Console/Commands/ChannelManager/MetaCancellationService.php @@ -0,0 +1,130 @@ +mailer = $mailer; + $this->channelManagerBookingService = $channelManagerBookingService; + + } + + public function handle() + { + + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + $today = Carbon::now()->startOfDay()->toDateString(); + + + if(!Carbon::parse($today)->isLastOfMonth()) { + $this->alert(date('Y-m-d H:i:s') . ' : Not Today!'); + return false; + } + + $fistDayOfMonth = Carbon::parse($today)->startOfMonth()->toDateString(); + $lastDayOfMonth = Carbon::parse($today)->addMonth()->startOfMonth()->toDateString(); + + $cancellationBooking = Booking::where('channel_id', 1) + ->where('status', 0) + ->where('checkout_date', '>', $fistDayOfMonth) + ->where('checkout_date', '<', $lastDayOfMonth) + //->where('id', 68690) + ->with('channelManagerBooking') + ->with('bookingRoom') + ->get(); + + //11 - Trivago + + $cancellationBooking = $cancellationBooking ? $cancellationBooking->toArray() : []; + + + foreach ($cancellationBooking as $booking) { + + try { + + + if (empty($booking['channel_manager_booking'])) { + $this->line(date('Y-m-d H:i:s') . ' : Booking ID: ' . $booking['id']); + continue; + } + + $channelBookingCheck = collect($booking['channel_manager_booking']) + ->where('channel_manager_id', 11) + ->where('type', 'Booking') + ->where('is_pushed', 1) + ->isEmpty(); + + if ($channelBookingCheck) { + $this->line(date('Y-m-d H:i:s') . ' : Booking ID: ' . $booking['id']); + continue; + } + + $channelCancelCheck = collect($booking['channel_manager_booking']) + ->where('channel_manager_id', 11) + ->where('type', 'Cancel') + ->where('is_pushed', 1) + ->isEmpty(); + + if (!$channelCancelCheck) { + $this->line(date('Y-m-d H:i:s') . ' : Booking ID: ' . $booking['id']); + continue; + } + + $channelManagerBookingCreateParam = [ + 'property_id' => $booking['property_id'], + 'booking_id' => $booking['id'], + 'channel_manager_id' => 11, + 'type' => 'Cancel', + ]; + + $this->channelManagerBookingService->create($channelManagerBookingCreateParam); + + $this->info(date('Y-m-d H:i:s') . ' : Booking ID: ' . $booking['id']); + + + } catch (ApiErrorException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + + } + +} diff --git a/app/Console/Commands/ChannelManager/PropertyBookingSyncService.php b/app/Console/Commands/ChannelManager/PropertyBookingSyncService.php new file mode 100644 index 0000000..d009a79 --- /dev/null +++ b/app/Console/Commands/ChannelManager/PropertyBookingSyncService.php @@ -0,0 +1,72 @@ +mailer = $mailer; + $this->propertyRoomService = $propertyRoomService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + } + + public function handle() + { + + $sourcePath = 'C:\www\api.extranetwork.com\app\Console\Commands\ChannelManager\akgun.xlsx'; + + + $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); + $spreadsheet = $reader->load($sourcePath); + $sheet = $spreadsheet->getSheet($spreadsheet->getFirstSheetIndex()); + $rows = $sheet->toArray(); + + $rowKey = 0; + foreach ($rows as $row) { + + $rowKey++; + + if($rowKey < 3) { + continue; + } + + dd($row); + } + + //dd(file_exists($sourcePath)); + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + + } +} diff --git a/app/Console/Commands/ChannelManager/PropertyChannelSyncService.php b/app/Console/Commands/ChannelManager/PropertyChannelSyncService.php new file mode 100644 index 0000000..2f309e8 --- /dev/null +++ b/app/Console/Commands/ChannelManager/PropertyChannelSyncService.php @@ -0,0 +1,222 @@ +mailer = $mailer; + $this->propertyRoomService = $propertyRoomService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + } + + public function handle() + { + + $sourceChannelId = 1; //Booking Engine + $targetChannelId = 5; //Channel Manager + + $propertyIdList = [1098]; + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + + foreach ($propertyIdList as $propertyId) { + + + $this->info(date('Y-m-d H:i:s') . ' - AVAILABILITY START : Property: ' . $propertyId . ' : SourceChannelId: ' . $sourceChannelId . ' : TargetChannelId: ' . $targetChannelId); + + $isPropertyRoomAvailabilityUpdate = false; + + DB::beginTransaction(); + + try { + + $propertyRoomAvailabilityUpdateQuery = <<= curdate() AND channel_id IS NULL AND status = 1; +BUR; + + $propertyRoomAvailabilityUpdate = DB::select(DB::raw($propertyRoomAvailabilityUpdateQuery)); + + $isPropertyRoomAvailabilityUpdate = true; + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (\Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + + if ($isPropertyRoomAvailabilityUpdate) { + DB::commit(); + $this->info(date('Y-m-d H:i:s') . ' - AVAILABILITY SUCCESS : Property: ' . $propertyId . ' : SourceChannelId: ' . $sourceChannelId . ' : TargetChannelId: ' . $targetChannelId); + } else { + DB::rollBack(); + $this->info(date('Y-m-d H:i:s') . ' - AVAILABILITY ERROR : Property: ' . $propertyId . ' : SourceChannelId: ' . $sourceChannelId . ' : TargetChannelId: ' . $targetChannelId); + } + + $this->info(date('Y-m-d H:i:s') . ' - AVAILABILITY FINISHED : Property: ' . $propertyId . ' : SourceChannelId: ' . $sourceChannelId . ' : TargetChannelId: ' . $targetChannelId); + + + $this->info(date('Y-m-d H:i:s') . ' - RATE START : Property: ' . $propertyId . ' : SourceChannelId: ' . $sourceChannelId . ' : TargetChannelId: ' . $targetChannelId); + + $requestParams = [ + 'property_id' => $propertyId, + 'channel_id' => $sourceChannelId, + 'start_date' => Carbon::now()->toDateString(), + 'end_date' => Carbon::now()->addMonths(6)->subDay()->toDateString(), + //'start_date' => '2023-03-01', + //'end_date' => '2024-01-01', + ]; + + $propertyRoomType = $this->propertyRoomService->getPropertyRoomInventory($requestParams); + + if ($propertyRoomType['status'] != 'success') { + throw new ApiErrorException($propertyRoomType['message']); + } + + $propertyRoomType = $propertyRoomType['data']; + $propertyRoomRateAvailability = collect($propertyRoomType); + + $roomRates = []; + foreach ($propertyRoomRateAvailability as $room) { + + foreach ($room['property_room_rate_mapping'] as $roomRateMapping) { + + + foreach ($room['room_availability'] as $date => $roomAvailability) { + $roomRates[$room['id']]['availability'][$date] = $roomAvailability['value']; + } + + $roomRates[$room['id']]['rate'][$roomRateMapping['id']] = [ + 'roomName' => $room['name'], + 'roomRateName' => $roomRateMapping['name'], + 'currencyCode' => $roomRateMapping['currency_code'], + ]; + + + $roomRatePrices = reset($roomRateMapping['prices']); + foreach ($roomRatePrices['price'] as $date => $roomRatePrice) { + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['price'][$date] = $roomRatePrice['value']; + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['stopSell'][$date] = $roomRatePrice['stop_sell']; + } + + foreach ($roomRatePrices['stop_sell'] as $date => $stopSell) { + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['stopSell'][$date] = $stopSell['value']; + } + + foreach ($roomRatePrices['min_stay'] as $date => $minStay) { + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['minStay'][$date] = $minStay['value']; + } + + } + } + + + $roomRateFormatted = []; + foreach ($roomRates as $roomId => $roomRateMapping) { + foreach ($roomRateMapping['rate'] as $roomRateMappingId => $roomRate) { + + foreach ($roomRate['price'] as $date => $price) { + + $roomRateKey = '1|' . $roomId . '|' . $roomRateMappingId . '|' . $date; + + $roomRateFormatted[$roomRateKey] = [ + 'setup_type_id' => '1', + 'room_id' => $roomId, + 'room_rate_mapping_id' => $roomRateMappingId, + 'date' => $date, + 'amount' => is_null($price) ? 0 : $price, + 'min_stay' => $roomRate['minStay'][$date], + 'stop_sell' => $roomRate['stopSell'][$date], + ]; + + } + + } + + } + + $isPropertyRoomRateUpdate = false; + + DB::beginTransaction(); + + try { + + $roomRateFormattedChunked = array_chunk($roomRateFormatted, 1000); + + foreach ($roomRateFormattedChunked as $roomRateFormattedParsed) { + + $requestParams = [ + 'property_id' => $propertyId, + 'channel_id' => $targetChannelId, + 'rates' => $roomRateFormattedParsed + ]; + + $roomRateUpdate = $this->propertyRoomRatePriceService->roomRateUpdate($requestParams); + + if ($roomRateUpdate['status'] != 'success') { + throw new ApiErrorException($roomRateUpdate['message']); + } + + } + + $isPropertyRoomRateUpdate = true; + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (\Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + + if ($isPropertyRoomRateUpdate) { + DB::commit(); + $this->info(date('Y-m-d H:i:s') . ' - RATE SUCCESS : Property: ' . $propertyId . ' : SourceChannelId: ' . $sourceChannelId . ' : TargetChannelId: ' . $targetChannelId); + } else { + DB::rollBack(); + $this->info(date('Y-m-d H:i:s') . ' - RATE ERROR : Property: ' . $propertyId . ' : SourceChannelId: ' . $sourceChannelId . ' : TargetChannelId: ' . $targetChannelId); + } + + $this->info(date('Y-m-d H:i:s') . ' - RATE FINISHED : Property: ' . $propertyId . ' : SourceChannelId: ' . $sourceChannelId . ' : TargetChannelId: ' . $targetChannelId); + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + + } +} diff --git a/app/Console/Commands/ChannelManager/PropertyMetaRoomRatePriceService.php b/app/Console/Commands/ChannelManager/PropertyMetaRoomRatePriceService.php new file mode 100644 index 0000000..27cf166 --- /dev/null +++ b/app/Console/Commands/ChannelManager/PropertyMetaRoomRatePriceService.php @@ -0,0 +1,344 @@ +mailer = $mailer; + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + + if (!is_null($this->option('property_id'))) { + $propertyMeta = PropertyMeta::where('status', 1)->where('property_id', $this->option('property_id'))->with('property')->orderBy('id', 'ASC')->get(['id', 'property_id', 'cancellation_policy', 'adult_policy', 'child_policy', 'promotion', 'status'])->toArray(); + } else { + $propertyMeta = PropertyMeta::where('status', 1)->with('property')->orderBy('id', 'ASC')->get(['id', 'property_id', 'cancellation_policy', 'adult_policy', 'child_policy', 'promotion', 'status'])->toArray(); + } + + foreach ($propertyMeta as $property) { + + + $this->info(date('Y-m-d H:i:s') . ' : Property Room Rate Price Start: ' . $property['property']['name']); + + + $roomRateOccupancyPriceParam = []; + + $propertyId = $property['property_id']; + $startDate = Carbon::now()->toDateString(); + $finishDate = Carbon::parse($startDate)->addDays(360)->toDateString(); + + $propertyCancellationPolicy = json_decode($property['cancellation_policy'], 1); + $propertyAdultPolicy = json_decode($property['adult_policy'], 1); + $propertyChildPolicy = json_decode($property['child_policy'], 1); + $propertyPromotion = json_decode($property['promotion'], 1); + + $propertyRoomRatePrice = PropertyRoomRatePrice::where('property_id', $propertyId) + ->where('date', '>=', $startDate) + ->where('date', '<=', $finishDate) + ->where('channel_id', 1) + //->where('room_rate_mapping_id', 79)//TODO: Delete + //->where('date', '2024-09-01')//TODO: Delete + ->where('availability_type_id', 1) + ->where('status', 1) + ->get(['id', 'property_id', 'property_room_id', 'room_rate_mapping_id', 'date', 'amount', 'currency', 'min_stay', 'stop_sell']); + + $propertyRoomRatePrice = $propertyRoomRatePrice->toArray(); + + $roomRateOccupancy = PropertyMetaRoomRate::where('property_id', $propertyId)->get()->toArray(); + + //TODO: DEL + //$roomRateOccupancy = collect($roomRateOccupancy)->where('occupancy_code', 'A')->toArray(); + + $propertyAllPriceCounter = 0; + + $roomRateOccupancyPrice = []; + foreach ($propertyRoomRatePrice as $perPropertyRoomRatePrice) { + + $roomRateOccupancySelected = collect($roomRateOccupancy)->where('room_rate_mapping_id', $perPropertyRoomRatePrice['room_rate_mapping_id'])->toArray(); + + foreach ($roomRateOccupancySelected as $roomRateOccupancyKey => $perRoomRateOccupancy) { + + $baseAmount = $perPropertyRoomRatePrice['amount']; + + $roomRateOccupancyKey = $perRoomRateOccupancy['code']; + + $affectedAmountArray = []; + + $roomRateMappingId = $perPropertyRoomRatePrice['room_rate_mapping_id']; + $cancellationPolicy = isset($propertyCancellationPolicy[$roomRateMappingId]) ? $propertyCancellationPolicy[$roomRateMappingId] : []; + $adultPolicy = isset($propertyAdultPolicy[$roomRateMappingId]) ? $propertyAdultPolicy[$roomRateMappingId] : []; + $childPolicy = isset($propertyChildPolicy[$roomRateMappingId]) ? $propertyChildPolicy[$roomRateMappingId] : []; + $promotions = isset($propertyPromotion[$roomRateMappingId]) ? $propertyPromotion[$roomRateMappingId] : []; + + //dd($promotions,$perPropertyRoomRatePrice,$baseAmount); + + + $perPersonAmount = $baseAmount / $perRoomRateOccupancy['included_occupancy']; + + //Kişi bazlı fiyat hesaplamada baz fiyat dikkate alınır, daha sonra $perPersonAmount baz fiyat üzerinden alınıp çocuk hespaplanır. + + + //Adult Policy + $affectedAmount = 0; + if ($perRoomRateOccupancy['included_occupancy'] != strlen($perRoomRateOccupancy['occupancy_code'])) { + if (!empty($adultPolicy)) { + + if ($perRoomRateOccupancy['included_occupancy'] > strlen($perRoomRateOccupancy['occupancy_code'])) { + + $occupancyDifference = $perRoomRateOccupancy['included_occupancy'] - strlen($perRoomRateOccupancy['occupancy_code']); + $adultPolicySelected = collect($adultPolicy) + ->where('adult', $occupancyDifference) + ->where('adult_action_type', 'DEC') + ->first(); + + if (!empty($adultPolicySelected)) { + if ($adultPolicySelected['type'] == 'PER') { + $affectedAmount = ($baseAmount * $adultPolicySelected['value'] / 100); + } elseif ($adultPolicySelected['type'] == 'FIX') { + $affectedAmount = $adultPolicySelected['value']; + } + + $affectedAmountArray[] = $adultPolicySelected['action_type'] == 'DEC' ? ($affectedAmount * -1) : $affectedAmount; + + } + + } + + if (strlen($perRoomRateOccupancy['occupancy_code']) > $perRoomRateOccupancy['included_occupancy']) { + + $occupancyDifference = strlen($perRoomRateOccupancy['occupancy_code']) - $perRoomRateOccupancy['included_occupancy']; + $adultPolicySelected = collect($adultPolicy) + ->where('adult', $occupancyDifference) + ->where('adult_action_type', 'INC') + ->first(); + + + if (!empty($adultPolicySelected)) { + if ($adultPolicySelected['type'] == 'PER') { + $affectedAmount = ($baseAmount * $adultPolicySelected['value'] / 100); + } elseif ($adultPolicySelected['type'] == 'FIX') { + $affectedAmount = $adultPolicySelected['value']; + } + + $affectedAmountArray[] = $adultPolicySelected['action_type'] == 'DEC' ? ($affectedAmount * -1) : $affectedAmount; + + } + + + } + + } + + } + //Adult Policy + + //Önce Oda Fiyatı Hesaplanır, Yetişkine Göre + $roomAmount = $baseAmount + array_sum($affectedAmountArray); + + + //Oluşan fiyata göre de iptal politikası uygulanır + //$cancellationPolicy + $affectedAmount = 0; + $isCancellationPolicyActive = false; + if (isset($cancellationPolicy[$perRoomRateOccupancy['cancellation_policy_id']])) { + $cancellationPolicySelected = $cancellationPolicy[$perRoomRateOccupancy['cancellation_policy_id']]; + + //dd($cancellationPolicy,$perRoomRateOccupancy,$cancellationPolicySelected,$perPropertyRoomRatePrice['date']); + + + //if ($cancellationPolicySelected['is_affected_price'] == 1) { + + $isDateRange = $cancellationPolicySelected['is_date_range']; + $isDateRangeDate = Carbon::parse($perPropertyRoomRatePrice['date'])->between($cancellationPolicySelected['start_date'], $cancellationPolicySelected['finish_date']); + + if (($isDateRange && $isDateRangeDate) || empty($isDateRange)) { + + if ($cancellationPolicySelected['affect_price_type'] == 'PER') { + $affectedAmount = ($roomAmount * $cancellationPolicySelected['affect_price_value'] / 100); + } elseif ($cancellationPolicySelected['affect_price_type'] == 'FIX') { + $affectedAmount = $cancellationPolicySelected['affect_price_value']; + } + + $affectedAmountArray[] = $cancellationPolicySelected['affect_price_action_type'] == 'DEC' ? ($affectedAmount * -1) : $affectedAmount; + + $isCancellationPolicyActive = true; + + } + + //} + + } + //$cancellationPolicy + + + //dd($perRoomRateOccupancy, $perPropertyRoomRatePrice, $cancellationPolicy); + + $isCancellationPolicyActive = true; + if ($isCancellationPolicyActive) { + + $calculatedAmount = $baseAmount + array_sum($affectedAmountArray); + + $promotionsGrouped = collect($promotions)->groupBy('type')->toArray(); + + /**** PROMOTION START ****/ + $totalPromotionDiscount = 0; + foreach ($promotionsGrouped as $promotionsGroupKey => $promotions) { + + $promotions = collect($promotions)->sortByDesc('amount')->toArray(); + + foreach ($promotions as $promotion) { + + if ($promotion['min_stay'] > 1) { + continue; + } + + if ($promotion['is_mobile']) { + continue; + } + + $daysArray = !empty($promotion['days']) ? json_decode($promotion['days'], 1) : []; + if (!in_array((Carbon::parse($perPropertyRoomRatePrice['date'])->dayOfWeek + 1), $daysArray)) { + continue; + } + + if ($promotion['type'] == 'PRD') { + + if (!Carbon::now()->startOfDay()->between(Carbon::parse($promotion['start_date'])->toDateString(), Carbon::parse($promotion['end_date'])->toDateString())) { + continue; + } + + if (!Carbon::parse($perPropertyRoomRatePrice['date'])->startOfDay()->between(Carbon::parse($promotion['reservation_start_date'])->toDateString(), Carbon::parse($promotion['reservation_end_date'])->toDateString())) { + continue; + } + + $totalPromotionDiscount += ($calculatedAmount * $promotion['amount']) / 100; + + break; + + } + + if ($promotion['type'] == 'BFD') { + + if (Carbon::parse($perPropertyRoomRatePrice['date'])->diffInDays(Carbon::now()->startOfDay()->toDateString()) < $promotion['day_before']) { + continue; + } + + $totalPromotionDiscount += ($calculatedAmount * $promotion['amount']) / 100; + + break; + } + + if ($promotion['type'] == 'DSC') { + $totalPromotionDiscount += ($calculatedAmount * $promotion['amount']) / 100; + + break; + } + + + } + } + + + $calculatedAmount = moneyDoubleFormatDecimal($calculatedAmount - $totalPromotionDiscount); + $calculatedAmount = $calculatedAmount > 0 ? $calculatedAmount : 0; + /**** PROMOTION FINISHED ****/ + + $roomRateOccupancyPriceParam[] = [ + 'property_id' => $property['property_id'], + 'room_id' => $perRoomRateOccupancy['room_id'], + 'room_rate_mapping_id' => $roomRateMappingId, + 'date' => $perPropertyRoomRatePrice['date'], + 'code' => $roomRateOccupancyKey, + //'title' => $perRoomRateOccupancy['title'], + //'baseAmount' => $perPropertyRoomRatePrice['amount'], + 'amount' => $calculatedAmount, + 'currency' => $perPropertyRoomRatePrice['currency'], + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => Carbon::now()->unix(), + 'updated_at' => Carbon::now()->unix(), + ]; + + $propertyAllPriceCounter++; + + } + + } + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Property Room Rate Price Finished: ' . $property['property']['name']); + + //dd(collect($roomRateOccupancyPriceParam)->sortBy('room_id')->sortBy('amount')->toArray()); + + + $insertRows = []; + $updateRows = []; + $rowChunk = 0; + foreach ($roomRateOccupancyPriceParam as $roomRateOccupancyPrice) { + + $insertRows[$rowChunk][] = $roomRateOccupancyPrice; + if (count($insertRows[$rowChunk]) > 1000) { + $rowChunk++; + } + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Property Room Rate Price to DB Start: ' . $property['property']['name']); + + DB::beginTransaction(); + + PropertyMetaRoomRatePrice::where('property_id', $propertyId)->delete(); + + if (!empty($insertRows)) { + foreach ($insertRows as $insertRow) { + PropertyMetaRoomRatePrice::insert($insertRow); + } + } + + DB::commit(); + + $this->info(date('Y-m-d H:i:s') . ' : Property Room Rate Price to DB Finished: ' . $property['property']['name']); + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + + } +} diff --git a/app/Console/Commands/ChannelManager/PropertyMetaRoomRatePushService.php b/app/Console/Commands/ChannelManager/PropertyMetaRoomRatePushService.php new file mode 100644 index 0000000..f50a1cd --- /dev/null +++ b/app/Console/Commands/ChannelManager/PropertyMetaRoomRatePushService.php @@ -0,0 +1,309 @@ +mailer = $mailer; + $this->miraiService = $miraiService; + } + + function getMonthlyPeriod($endDate) + { + $diffInMonths = Carbon::parse()->floorMonth()->diffInMonths(Carbon::parse($endDate)->floorMonth()); + $diffInMonths++; + + $monthlyPeriod = []; + $startDate = null; + $finishDate = null; + for ($i = 0; $i < $diffInMonths; $i++) { + + if (empty($startDate)) { + $startDate = Carbon::now()->toDateString(); + $finishDate = Carbon::parse($startDate)->endOfMonth()->toDateString(); + } else { + $startDate = Carbon::now()->addMonths($i)->startOfMonth()->toDateString(); + $finishDate = Carbon::parse($startDate)->endOfMonth()->toDateString(); + } + + if ($finishDate > $endDate) { + $finishDate = $endDate; + } + + $monthlyPeriod[] = [ + 'startDate' => $startDate, + 'finishDate' => $finishDate, + ]; + + } + + return $monthlyPeriod; + + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + + if (!is_null($this->option('property_id'))) { + $propertyMeta = PropertyMeta::where('status', 1)->where('property_id', $this->option('property_id'))->with('property')->orderBy('id', 'ASC')->get(['id', 'property_id', 'cancellation_policy', 'adult_policy', 'child_policy', 'param', 'status'])->toArray(); + } else { + $propertyMeta = PropertyMeta::where('status', 1)->with('property')->orderBy('id', 'ASC')->get(['id', 'property_id', 'cancellation_policy', 'adult_policy', 'child_policy', 'param', 'status'])->toArray(); + } + + + foreach ($propertyMeta as $property) { + + + $propertyId = $property['property_id']; + + //Mirai User Setup + $this->miraiService->setupUser($property['paramsArray']['login'], $property['paramsArray']['password']); + + $this->info(date('Y-m-d H:i:s') . ' : Property Room Rate Push Start: ' . $property['property']['name']); + + //$propertyRoomRatePriceEndDate = PropertyRoomRatePrice::where('property_id', $propertyId)->orderBy('date', 'DESC')->first(); + //$propertyRoomRatePriceEndDate = $propertyRoomRatePriceEndDate->toArray() ? $propertyRoomRatePriceEndDate['date'] : null; + //$propertyRoomRatePriceEndDate = '2023-10-31'; + + //$getMonthlyPeriod = $this->getMonthlyPeriod($propertyRoomRatePriceEndDate); + $getMonthlyPeriod = []; + $getMonthlyPeriod[] = [ + 'startDate' => Carbon::now()->toDateString(), + //'finishDate' => Carbon::now()->addYear()->toDateString(), + 'finishDate' => Carbon::now()->addDays(360)->toDateString(), + ]; + + $propertyMetaRoomRateMapping = PropertyMetaRoomRateMapping::where('property_id', $propertyId)->with('propertyMetaRoomRate')->get()->toArray(); + $propertyMetaRoomRateMappingCollect = collect($propertyMetaRoomRateMapping); + + $channelManagerPropertyMapping = ChannelManagerPropertyMapping::where('property_id', $propertyId)->where('status', 1)->where('channel_manager_id', 7)->first()->toArray(); + + if (empty($channelManagerPropertyMapping)) { + continue; + } + + $metaRoomRateMapping = []; + $propertyMetaRoomRateMappingGroup = $propertyMetaRoomRateMappingCollect->groupBy('property_meta_room_rate.room_id')->toArray(); + foreach ($propertyMetaRoomRateMappingGroup as $roomGroup) { + $roomGroup = reset($roomGroup); + $metaRoomRateMapping[$roomGroup['property_meta_room_rate']['room_id']]['meta_code'] = $roomGroup['meta_code']; + } + + + foreach ($getMonthlyPeriod as $period) { + + $this->info(date('Y-m-d H:i:s') . ' : Meta Availability Start: ' . $property['property']['name'] . ' - ' . $period['startDate'] . ' / ' . $period['finishDate']); + + try { + + + $propertyRoomAvailability = PropertyRoomAvailability::where('property_id', $propertyId) + ->where('status', 1)->where('channel_id', null) + ->whereBetween('date', [$period['startDate'], $period['finishDate']]) + ->get(['property_room_id', 'date', 'stop_sell', 'availability']) + ->toArray(); + $propertyRoomAvailabilityCollect = collect($propertyRoomAvailability); + + + foreach ($metaRoomRateMapping as $roomId => $metaRoom) { + $metaRoomRateMapping[$roomId]['data'] = $propertyRoomAvailabilityCollect->where('property_room_id', $roomId)->sortBy('date')->toArray(); + } + + + $xmlResponse = new \SimpleXMLElement(''); + + $xmlResponse->addAttribute('hotelId', $channelManagerPropertyMapping['channel_manager_property_id']); + + foreach ($metaRoomRateMapping as $roomId => $metaRoom) { + + $room = $xmlResponse->addChild('room'); + $room->addAttribute('id', $metaRoom['meta_code']); + + $roomInventory = $room->addChild('inventory'); + + foreach ($metaRoom['data'] as $roomRate) { + $roomInventoryAvailability = $roomInventory->addChild('availability'); + + $roomRate['availability'] = $roomRate['stop_sell'] == 1 ? 0 : $roomRate['availability']; + + $roomInventoryAvailability->addAttribute('date', $roomRate['date']); + $roomInventoryAvailability->addAttribute('quantity', $roomRate['availability']); + + } + + } + + + $request = $this->miraiService->inventoryRoomRateUpdate('webservice_updater.apro', $xmlResponse->asXML()); + + if (!$request['status']) { + throw new ApiErrorException('webservice_updater AVAILABILITY Error: ' . $request['message']); + } + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + + $this->info(date('Y-m-d H:i:s') . ' : Meta Price Start: ' . $property['property']['name'] . ' - ' . $period['startDate'] . ' / ' . $period['finishDate']); + + try { + + + $propertyMetaRoomRatePrice = PropertyMetaRoomRatePrice::where('property_id', $propertyId) + ->whereBetween('date', [$period['startDate'], $period['finishDate']])->where('status', 1) + ->whereIn('code', pickItemFromArray('code', $propertyMetaRoomRateMapping)) + ->get(['date', 'code', 'amount', 'currency']) + ->toArray(); + + $propertyMetaRoomRatePriceCollect = collect($propertyMetaRoomRatePrice); + + if (empty($propertyMetaRoomRatePrice)) { + $this->error(date('Y-m-d H:i:s') . ' : ' . $property['property']['name'] . ' : Price not found!'); + continue; + } + + + $metaRoomRatePrice = []; + $metaRoomPricesOrdered = []; + foreach ($propertyMetaRoomRateMapping as $metaRoom) { + + $metaRoomPrices = $propertyMetaRoomRatePriceCollect->where('code', $metaRoom['code'])->sortBy('date')->toArray(); + $metaRoomPricesCollect = collect($metaRoomPrices); + + $metaRoomPricesGroup = []; + foreach ($metaRoomPrices as $metaRoomPrice) { + $metaRoomPricesGroup[md5($metaRoomPrice['amount'])][$metaRoomPrice['date']] = $metaRoomPrice; + ksort($metaRoomPricesGroup[md5($metaRoomPrice['amount'])]); + } + + foreach ($metaRoomPricesGroup as $priceGroupKey => $priceGroup) { + + $priceDateGroup = 0; + foreach ($priceGroup as $priceGroupDate => $price) { + + if (!isset($metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['startDate'])) { + $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['startDate'] = $price['date']; + $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['finishDate'] = $price['date']; + } else { + + if (Carbon::parse($metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['finishDate'])->addDay()->toDateString() != $price['date']) { + + $priceDateGroup++; + $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['startDate'] = $price['date']; + + } else { + $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['finishDate'] = $price['date']; + } + + $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['finishDate'] = $price['date']; + + } + + $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['amount'] = $price['amount']; + $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['currency'] = $price['currency']; + //$metaRoomPricesOrdered[$priceGroupKey][$priceDateGroup]['metaCode'] = $metaRoom['meta_code']; + + $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['title'] = $metaRoom['property_meta_room_rate']['title']; + + } + } + + } + + $xmlResponse = new \SimpleXMLElement(''); + + $xmlResponse->addAttribute('hotelId', $channelManagerPropertyMapping['channel_manager_property_id']); + + foreach ($metaRoomPricesOrdered as $roomRateMetaCode => $metaRoom) { + + foreach ($metaRoom as $priceGroupKey => $metaRoomPriceGroup) { + + $room = $xmlResponse->addChild('room'); + + $room->addAttribute('id', $roomRateMetaCode); + + $currency = !empty($metaRoomPriceGroup) ? reset($metaRoomPriceGroup)['currency'] : 'EUR'; + + $rate = $room->addChild('rate'); + $rate->addAttribute('currency', $currency); + + foreach ($metaRoomPriceGroup as $metaRoomPrice) { + $planning = $rate->addChild('planning'); + + $planning->addAttribute('from', $metaRoomPrice['startDate']); + $planning->addAttribute('to', $metaRoomPrice['finishDate']); + $planning->addAttribute('unitPrice', $metaRoomPrice['amount']); + + } + } + + } + + $request = $this->miraiService->inventoryRoomRateUpdate('webservice_updater.apro', $xmlResponse->asXML()); + + if (!$request['status']) { + throw new ApiErrorException('webservice_updater PRICE Error: ' . $request['message']); + } + + //Remove all prices then push to meta + PropertyMetaRoomRatePrice::where('property_id', $propertyId)->delete(); + + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + + } + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + + } +} diff --git a/app/Console/Commands/ChannelManager/PropertyMetaRoomRateService.php b/app/Console/Commands/ChannelManager/PropertyMetaRoomRateService.php new file mode 100644 index 0000000..593dbcf --- /dev/null +++ b/app/Console/Commands/ChannelManager/PropertyMetaRoomRateService.php @@ -0,0 +1,288 @@ +mailer = $mailer; + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + + if (!is_null($this->option('property_id'))) { + $propertyMeta = PropertyMeta::where('status', 1)->where('property_id', $this->option('property_id'))->with('property')->orderBy('id', 'ASC')->get(['id', 'property_id', 'status'])->toArray(); + } else { + $propertyMeta = PropertyMeta::where('status', 1)->with('property')->orderBy('id', 'ASC')->get(['id', 'property_id', 'status'])->toArray(); + } + + foreach ($propertyMeta as $property) { + + $this->info(date('Y-m-d H:i:s') . ' : Property Policy Start: ' . $property['property']['name']); + + + //Property All Room Rate Price Policy + $propertyRoomRateChannelMapping = PropertyRoomRateChannelMapping::with('propertyRoomRateMapping') + ->with('propertyRoomRateMapping.propertyRoom') + ->with('propertyRoomRateMapping.propertyRoomRate') + ->with('propertyRoomRateChannelCancellationPolicy.propertyCancellationPolicy') + ->with('propertyRoomRateChannelPricingAdultPolicy.propertyPricingPolicyAdult') + ->with('propertyRoomRateChannelPricingChildPolicy.propertyPricingPolicyChild') + ->with('propertyRoomRateChannelPromotion.propertyPromotion.promotionType') + ->get() + ->where('property_id', $property['property_id'])->where('channel_id', 1) + ->where('status', 1); + + $propertyRoomRateChannelMapping = $propertyRoomRateChannelMapping->where('propertyRoomRateMapping', '!=', null)->toArray(); + + //dd($propertyRoomRateChannelMapping); + + $roomRateMappingPolicy = []; + foreach ($propertyRoomRateChannelMapping as $propertyRoom) { + + + $roomRateMappingPolicy['promotion'][$propertyRoom['room_rate_mapping_id']] = []; + foreach ($propertyRoom['property_room_rate_channel_promotion'] as $propertyRoomRateChannelPromotion) { + + if($propertyRoomRateChannelPromotion['status'] != 1) { + continue; + } + + $roomRateMappingPolicy['promotion'][$propertyRoom['room_rate_mapping_id']][$propertyRoomRateChannelPromotion['property_promotion_id']] = [ + 'type' => $propertyRoomRateChannelPromotion['property_promotion']['promotion_type']['type_code'], + 'start_date' => $propertyRoomRateChannelPromotion['property_promotion']['start_date'], + 'end_date' => $propertyRoomRateChannelPromotion['property_promotion']['end_date'], + 'reservation_start_date' => $propertyRoomRateChannelPromotion['property_promotion']['reservation_start_date'], + 'reservation_end_date' => $propertyRoomRateChannelPromotion['property_promotion']['reservation_end_date'], + 'day_before' => $propertyRoomRateChannelPromotion['property_promotion']['day_before'], + 'amount' => $propertyRoomRateChannelPromotion['property_promotion']['amount'], + 'min_stay' => $propertyRoomRateChannelPromotion['property_promotion']['min_stay'], + 'is_mobile' => $propertyRoomRateChannelPromotion['property_promotion']['is_mobile'], + 'days' => $propertyRoomRateChannelPromotion['property_promotion']['days'], + ]; + + } + + $roomRateMappingPolicy['cancellation_policy'][$propertyRoom['room_rate_mapping_id']] = []; + foreach ($propertyRoom['property_room_rate_channel_cancellation_policy'] as $propertyRoomRateCancellationPolicy) { + + $roomRateMappingPolicy['cancellation_policy'][$propertyRoom['room_rate_mapping_id']][$propertyRoomRateCancellationPolicy['cancellation_policy_id']] = [ + 'is_affected_price' => $propertyRoomRateCancellationPolicy['property_cancellation_policy']['is_affected_price'], + 'affect_price_action_type' => $propertyRoomRateCancellationPolicy['property_cancellation_policy']['affect_price_action_type'], + 'affect_price_type' => $propertyRoomRateCancellationPolicy['property_cancellation_policy']['affect_price_type'], + 'affect_price_value' => $propertyRoomRateCancellationPolicy['property_cancellation_policy']['affect_price_value'], + 'is_date_range' => $propertyRoomRateCancellationPolicy['property_cancellation_policy']['is_date_range'], + 'start_date' => $propertyRoomRateCancellationPolicy['property_cancellation_policy']['start_date'], + 'finish_date' => $propertyRoomRateCancellationPolicy['property_cancellation_policy']['finish_date'], + ]; + + } + + $roomRateMappingPolicy['adult_policy'][$propertyRoom['room_rate_mapping_id']] = []; + foreach ($propertyRoom['property_room_rate_channel_pricing_adult_policy'] as $propertyRoomRateAdultPolicy) { + + $roomRateMappingPolicy['adult_policy'][$propertyRoom['room_rate_mapping_id']][$propertyRoomRateAdultPolicy['pricing_policy_adult_id']] = [ + 'adult_action_type' => $propertyRoomRateAdultPolicy['property_pricing_policy_adult']['adult_action_type'], + 'adult' => $propertyRoomRateAdultPolicy['property_pricing_policy_adult']['adult'], + 'action_type' => $propertyRoomRateAdultPolicy['property_pricing_policy_adult']['action_type'], + 'type' => $propertyRoomRateAdultPolicy['property_pricing_policy_adult']['type'], + 'value' => $propertyRoomRateAdultPolicy['property_pricing_policy_adult']['value'], + ]; + + } + + $roomRateMappingPolicy['child_policy'][$propertyRoom['room_rate_mapping_id']] = []; + foreach ($propertyRoom['property_room_rate_channel_pricing_child_policy'] as $propertyRoomRateChildPolicy) { + + $roomRateMappingPolicy['child_policy'][$propertyRoom['room_rate_mapping_id']][$propertyRoomRateChildPolicy['pricing_policy_child_id']] = [ + 'adult' => $propertyRoomRateChildPolicy['property_pricing_policy_child']['adult'], + 'child_order' => $propertyRoomRateChildPolicy['property_pricing_policy_child']['child_order'], + 'child_age_start' => $propertyRoomRateChildPolicy['property_pricing_policy_child']['child_age_start'], + 'child_age_end' => $propertyRoomRateChildPolicy['property_pricing_policy_child']['child_age_end'], + 'type' => $propertyRoomRateChildPolicy['property_pricing_policy_child']['type'], + 'value' => $propertyRoomRateChildPolicy['property_pricing_policy_child']['value'], + ]; + + } + + } + //Property All Room Rate Price Policy + + $propertyMetaPolicyUpdate = [ + 'cancellation_policy' => json_encode($roomRateMappingPolicy['cancellation_policy']), + 'adult_policy' => json_encode($roomRateMappingPolicy['adult_policy']), + 'child_policy' => json_encode($roomRateMappingPolicy['child_policy']), + 'promotion' => json_encode($roomRateMappingPolicy['promotion']), + ]; + + PropertyMeta::where('id', $property['id'])->update($propertyMetaPolicyUpdate); + + $this->info(date('Y-m-d H:i:s') . ' : Property Policy Finished: ' . $property['property']['name']); + + + //$propertyMetaTemp + /*$propertyMetaTemp = PropertyMeta::where('property_id', $property['property_id'])->with('property')->get()->first(); + $propertyMetaTemp = $propertyMetaTemp->toArray(); + $propertyMetaPolicyUpdate = [ + 'cancellation_policy' => json_decode($propertyMetaTemp['cancellation_policy'], 1), + 'adult_policy' => json_decode($propertyMetaTemp['adult_policy'], 1), + 'child_policy' => json_decode($propertyMetaTemp['child_policy'], 1), + ];*/ + //$propertyMetaTemp + + + $this->info(date('Y-m-d H:i:s') . ' : Property Room Rate Occupancy Start: ' . $property['property']['name']); + + + //Property All Price Combination + /*$propertyRoomRateChannelMapping = PropertyRoomRateChannelMapping::with('propertyRoomRateMapping') + ->with('propertyRoomRateMapping.propertyRoom') + ->with('propertyRoomRateMapping.propertyRoomRate') + ->with('propertyRoomRateChannelCancellationPolicy.propertyCancellationPolicy') + ->with('propertyRoomRateChannelPricingAdultPolicy') + ->with('propertyRoomRateChannelPricingChildPolicy') + ->get() + ->where('property_id', $property['property_id'])->where('channel_id', 1) + ->where('status', 1); + + $propertyRoomRateChannelMapping = $propertyRoomRateChannelMapping->where('propertyRoomRateMapping', '!=', null)->toArray();*/ + + $roomRateOccupancy = []; + foreach ($propertyRoomRateChannelMapping as $propertyRoom) { + + $roomId = $propertyRoom['property_room_rate_mapping']['room_id']; + $propertyRoomRateMappingId = $propertyRoom['property_room_rate_mapping']['id']; + $occupancyGroup = occupancyGroup( + $propertyRoom['property_room_rate_mapping']['property_room']['max_adult'], + $propertyRoom['property_room_rate_mapping']['property_room']['max_child'], + $propertyRoom['property_room_rate_mapping']['property_room']['max_occupancy'] + ); + + //TODO: Burası incelenebilir, cancellation policy yok ise gelmiyor çünkü.... + /*if(empty($propertyRoom['property_room_rate_channel_cancellation_policy'])) { + $propertyRoom['property_room_rate_channel_cancellation_policy'][] = [ + 'cancellation_policy_id' => 0, + 'property_cancellation_policy' => [ + 'name' => 'Refundable', + 'is_nonrefundable' => 0, + ] + ]; + }*/ + + foreach ($propertyRoom['property_room_rate_channel_cancellation_policy'] as $propertyRoomRateCancellationPolicy) { + + + foreach ($occupancyGroup as $occupancyCode) { + + $roomRateOccupancyKey = $roomId . '-' . $propertyRoomRateMappingId . '-' . $propertyRoomRateCancellationPolicy['cancellation_policy_id'] . '-' . $occupancyCode; + $roomRateOccupancyKey = md5($roomRateOccupancyKey); + + $cancellationPolicyName = []; + $cancellationPolicyName[] = $propertyRoomRateCancellationPolicy['property_cancellation_policy']['is_nonrefundable'] ? 'NonRefundable' : 'Refundable'; + $cancellationPolicyName[] = !empty($propertyRoomRateCancellationPolicy['property_cancellation_policy']['name']) ? '(' . $propertyRoomRateCancellationPolicy['property_cancellation_policy']['name'] . ')' : null; + $cancellationPolicyName = implode(' ', $cancellationPolicyName); + + $roomRateOccupancyTitle = []; + $roomRateOccupancyTitle[] = $propertyRoom['property_room_rate_mapping']['property_room']['name']; + $roomRateOccupancyTitle[] = $propertyRoom['property_room_rate_mapping']['property_room_rate']['name']; + $roomRateOccupancyTitle[] = $cancellationPolicyName; + $roomRateOccupancyTitle[] = $occupancyCode; + + $roomRateOccupancyTitle = implode(' - ', $roomRateOccupancyTitle); + + $roomRateOccupancy[$roomRateOccupancyKey] = [ + 'propertyId' => $property['property_id'], + 'roomId' => $roomId, + 'roomName' => $propertyRoom['property_room_rate_mapping']['property_room']['name'], + 'roomRateMappingId' => $propertyRoomRateMappingId, + 'roomRateMappingName' => $propertyRoom['property_room_rate_mapping']['property_room_rate']['name'], + 'cancellationPolicyId' => $propertyRoomRateCancellationPolicy['cancellation_policy_id'], + 'cancellationPolicyName' => $propertyRoomRateCancellationPolicy['property_cancellation_policy']['name'], + 'occupancyCode' => $occupancyCode, + 'includedOccupancy' => $propertyRoom['property_room_rate_mapping']['included_occupancy'], + 'title' => $roomRateOccupancyTitle + ]; + + } + + } + + } + //Property All Price Combination + + + $propertyMetaRoomRateParam = []; + foreach ($roomRateOccupancy as $roomRateOccupancyKey => $roomRate) { + + //$propertyMetaRoomRateCheck = PropertyMetaRoomRate::where('code', $roomRateOccupancyKey)->count(); + + $propertyMetaRoomRateParam[] = [ + 'code' => $roomRateOccupancyKey, + 'title' => $roomRate['title'], + 'property_id' => $roomRate['propertyId'], + 'room_id' => $roomRate['roomId'], + 'room_rate_mapping_id' => $roomRate['roomRateMappingId'], + 'cancellation_policy_id' => $roomRate['cancellationPolicyId'], + 'included_occupancy' => $roomRate['includedOccupancy'], + 'occupancy_code' => $roomRate['occupancyCode'], + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => Carbon::now()->unix(), + 'updated_at' => Carbon::now()->unix(), + ]; + + /*if ($propertyMetaRoomRateCheck) { + $propertyMetaRoomRateUpdate = PropertyMetaRoomRate::where('code', $roomRateOccupancyKey)->update($propertyMetaRoomRateParam); + } else { + $propertyMetaRoomRateCreate = PropertyMetaRoomRate::create($propertyMetaRoomRateParam); + }*/ + + //$this->info(date('Y-m-d H:i:s') . ' : Property Room Rate Occupancy: ' . $roomRate['title']); + + } + + //Delete all codes + if (!empty($propertyMetaRoomRateParam)) { + PropertyMetaRoomRate::where('property_id', $property['property_id'])->delete(); + PropertyMetaRoomRate::insert($propertyMetaRoomRateParam); + } + + $this->info(date('Y-m-d H:i:s') . ' : Property Room Rate Occupancy Finished: ' . $property['property']['name']); + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + + } +} diff --git a/app/Console/Commands/ChannelManager/RemovePaymentTokenService.php b/app/Console/Commands/ChannelManager/RemovePaymentTokenService.php new file mode 100644 index 0000000..cbee425 --- /dev/null +++ b/app/Console/Commands/ChannelManager/RemovePaymentTokenService.php @@ -0,0 +1,116 @@ +mailer = $mailer; + $this->bookingPaymentService = $bookingPaymentService; + } + + public function handle() + { + + try { + + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + $bookingPaymentDataCriteria = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 2], + ['field' => 'type', 'condition' => '=', 'value' => 'ch'], + ], + 'with' => ['bookingDetail.channelManager'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $bookingPaymentData = $this->bookingPaymentService->selectPaymentData($bookingPaymentDataCriteria); + $bookingPaymentData = $bookingPaymentData['status'] == 'success' && !empty($bookingPaymentData['data']) ? $bookingPaymentData['data'] : []; + + if (!empty($bookingPaymentData)) { + $bookingPaymentDataCollect = collect($bookingPaymentData); + $bookingPaymentData = $bookingPaymentDataCollect->where('booking_detail.checkout_date', '<', Carbon::now()->subDays(7))->toArray(); + } + + //$bookingPaymentData + foreach ($bookingPaymentData as $bookingPaymentKey => $bookingPayment) { + + if (empty($bookingPayment['booking_detail']['channel_manager_id'])) { + continue; + } + + $channelManagerId = $bookingPayment['booking_detail']['channel_manager_id']; + + switch ($channelManagerId) { + case 2 : + + $channelService = App::make("App\Core\Service\ChannelManager\\{$bookingPayment['booking_detail']['channel_manager']['name']}"); + + $removeCreditCardToken = $channelService->removeCreditCardToken($bookingPayment['data']); + + if (!$removeCreditCardToken['status']) { + + //Hata maili atılsın + $this->error(date('Y-m-d H:i:s') . ' : Channel: ' . $bookingPayment['booking_detail']['channel_manager']['name'] . ' Token: ' . $bookingPayment['data']); + + } else { + + $this->bookingPaymentService->updatePaymentData($bookingPayment['id'], ['status' => 1]); + + $this->info(date('Y-m-d H:i:s') . ' : Channel: ' . $bookingPayment['booking_detail']['channel_manager']['name'] . ' Token: ' . $bookingPayment['data']); + + } + + break; + + default; + break; + + } + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/ChannelManager/ReservationPullService.php b/app/Console/Commands/ChannelManager/ReservationPullService.php new file mode 100644 index 0000000..b12be61 --- /dev/null +++ b/app/Console/Commands/ChannelManager/ReservationPullService.php @@ -0,0 +1,957 @@ +mailer = $mailer; + $this->channelManagerMappingService = $channelManagerMappingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->bookingService = $bookingService; + $this->bookingContactService = $bookingContactService; + $this->bookingRoomService = $bookingRoomService; + $this->bookingPaymentService = $bookingPaymentService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->newBookingMailService = $newBookingMailService; + $this->channelManagerService = $channelManagerService; + $this->channelManagerBookingService = $channelManagerBookingService; + } + + public function getDateByDay($dates = []) + { + + $dateByDay = []; + + $diffInDays = Carbon::parse($dates['checkIn'])->floatDiffInDays(Carbon::parse($dates['checkOut'])); + + for ($i = 0; $i < $diffInDays; $i++) { + $dateByDay[] = Carbon::parse($dates['checkIn'])->addDay($i)->format('Y-m-d'); + } + + return $dateByDay; + + } + + public function createBooking($channelCode, $param) + { + + $response = ['status' => false, 'message' => '']; + + DB::beginTransaction(); + + try { + + + $channelService = App::make("App\Core\Service\ChannelManager\\{$channelCode}"); + + $bookingCode = getCodeGenerate('BKG'); + + $param['booking']['booking_code'] = $bookingCode; + $param['booking']['extra_param'] = json_encode($param['channel_manager']); + $bookingCreate = $this->bookingService->create($param['booking']); + //$bookingCreate['status'] = 'success';//TODO: Delete + //$bookingCreate['data']['id'] = 1106; + + if ($bookingCreate['status'] != 'success') { + throw new ApiErrorException('Booking could not be made, Reservation: ' . $param['booking']['search_key']); + } + + $param['booking']['id'] = $bookingCreate['data']['id']; + + //INSERT CONTACT DATA + $bookingContactCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'name' => $param['contact']['name'], + 'surname' => $param['contact']['surname'], + 'phone_code' => $param['contact']['phone_code'], + 'phone_number' => $param['contact']['phone_number'], + 'email' => $param['contact']['email'], + 'country_code' => fillOnUndefined($param['contact'], 'country_code'), + 'note' => !empty($param['contact']['note']) ? $param['contact']['note'] : null, + 'language_code' => fillOnUndefined($param['contact'], 'language', 'en'), + 'extra_param' => fillOnUndefined($param['contact'], 'extra_param'), + 'status' => 1 + ]; + + $bookingContactCreate = $this->bookingContactService->create($bookingContactCreateParam); + //$bookingContactCreate['status'] = 'success'; + + if ($bookingContactCreate['status'] != 'success') { + throw new ApiErrorException('Booking Contact could not be made'); + } + + //INSERT ROOM DATA + foreach ($param['room'] as $roomOrder => $room) { + + $bookingRoomCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'room_order_number' => ($roomOrder + 1), + 'occupancy_code' => $room['occupancy_code'], + 'checkin_date' => $room['checkin_date'], //$room['checkin'], + 'checkout_date' => $room['checkout_date'],//$room['checkout'], + 'rate_key' => fillOnUndefined($room, 'rate_key'), + 'rate_key_code' => fillOnUndefined($room, 'rate_key_code'), + 'availability_id' => 1, + 'availability_code' => 'ROM', + 'room_id' => $room['room_id'], + 'room_name' => $room['room_name'], + 'room_rate_mapping_id' => $room['room_rate_mapping_id'], + 'room_rate_name' => $room['room_rate_name'], + 'cancellation_policy' => fillOnUndefined($room, 'cancellation_policy'), + 'payment_type_code' => fillOnUndefined($room, 'payment_type_code', 'CHN'), + 'daily_amount' => fillOnUndefined($room, 'daily_amount'), + 'extra_param' => fillOnUndefined($room, 'extra_param'), + 'rate_detail' => fillOnUndefined($room, 'rate_detail'), + 'total' => $room['total'], + 'currency_code' => $room['currency_code'], + 'status' => fillOnUndefined($room, 'status', 1), + ]; + + $bookingRoomCreate = $this->bookingRoomService->create($bookingRoomCreateParam); + //$bookingRoomCreate['status'] = 'success'; + + if ($bookingRoomCreate['status'] != 'success') { + throw new ApiErrorException('Booking Room could not be made'); + } + + + /* ROOM AVAILABILITY */ + $dateByDay = []; + $dateByDay = $this->getDateByDay(['checkIn' => $room['checkin_date'], 'checkOut' => $room['checkout_date']]); + foreach ($dateByDay as $day) { + + $requestParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['booking']['property_id']], + ['field' => 'property_room_id', 'condition' => '=', 'value' => $room['room_id']], + ['field' => 'availability_type_id', 'condition' => '=', 'value' => 1], + ['field' => 'date', 'condition' => '=', 'value' => $day] + ], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->propertyRoomAvailabilityService->select($requestParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success') { + throw new ApiErrorException('getPropertyRoomAndRoomRateAvailability Empty'); + } + + foreach ($getPropertyRoomAndRoomRateAvailability['data'] as $roomAvailability) { + $roomAvailabilityUpdated = $roomAvailability['availability'] <= 0 ? 0 : ($roomAvailability['availability'] - 1); + $this->propertyRoomAvailabilityService->update($roomAvailability['id'], ['availability' => $roomAvailabilityUpdated]); + } + + } + + /* ROOM AVAILABILITY */ + + } + + + //INSERT PAYMENT DATA + $bookingPaymentCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'payment_code' => fillOnUndefined($param['payment'], 'payment_code'), + 'payment_type_code' => fillOnUndefined($param['payment'], 'payment_type_code'),//Type: CHN + 'payment_source_code' => fillOnUndefined($param['payment'], 'payment_source_code'), + 'extra_param' => fillOnUndefined($param['payment'], 'extra_param'), + 'total' => fillOnUndefined($param['payment'], 'total'), + 'currency_code' => fillOnUndefined($param['payment'], 'currency_code'), + 'status' => fillOnUndefined($param['payment'], 'status'),//Type: 2 + ]; + + $bookingPaymentCreate = $this->bookingPaymentService->create($bookingPaymentCreateParam); + //$bookingPaymentCreate['status'] = 'success'; + + if ($bookingPaymentCreate['status'] != 'success') { + throw new ApiErrorException('Booking Payment could not be made'); + } + //INSERT PAYMENT DATA + + + //INSERT CHANNEL PAYMENT DATA + if (isset($param['payment_channel']) && !empty($param['payment_channel'])) { + + $bookingPaymentDataCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'type' => fillOnUndefined($param['payment_channel'], 'type'), + 'data' => fillOnUndefined($param['payment_channel'], 'data'), + 'status' => fillOnUndefined($param['payment_channel'], 'status', 1), + ]; + + $bookingPaymentDataCreate = $this->bookingPaymentService->createPaymentData($bookingPaymentDataCreateParam); + + if ($bookingPaymentDataCreate['status'] != 'success') { + throw new ApiErrorException('Booking Channel Payment could not be made'); + } + } + //INSERT CHANNEL PAYMENT DATA + + $reservationConfirmParam = $channelService->reservationConfirmParam($param['channel_manager']['property_id'], $param['channel_manager']['booking_id'], $bookingCode, $param); + $reservationConfirm = $channelService->reservationConfirm($reservationConfirmParam); + + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + + //PUSH CHANNEL MANAGER QUEUE + /* + 538 Euphoria Hotel Batumi + 541 Intourist Palace Hotel & SPA + 545 Metro Sky Tower Hotel + 546 Legend Hotel Batumi Convention Center & Spa + 548 Legend Business Hotel Batumi + 549 Euphoria Apartments + 550 City Hotel Batumi + */ + /*if (in_array($bookingCreate['data']['property_id'], [546])) { + + $channelManagerBookingParam = [ + 'property_id' => $bookingCreate['data']['property_id'], + 'booking_id' => $bookingCreate['data']['id'], + 'channel_manager_id' => $bookingCreate['data']['channel_manager_id'], + 'type' => 'Booking', + ]; + + $this->channelManagerBookingService->create($channelManagerBookingParam); + + }*/ + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingCreate['data']['property_id']], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $channelPropertyData) { + + if(in_array($channelPropertyData['channel_manager_id'],[11,13])) { + continue; + } + + $channelManagerBookingCreateParam = [ + 'property_id' => $bookingCreate['data']['property_id'], + 'booking_id' => $bookingCreate['data']['id'], + 'channel_manager_id' => $channelPropertyData['channel_manager_id'], + 'type' => 'Booking', + ]; + + $channelManagerBookingCreate = $this->channelManagerBookingService->create($channelManagerBookingCreateParam); + + } + + } + //PUSH CHANNEL MANAGER QUEUE + + DB::commit(); + + $mailParams = ['booking_id' => $bookingCreate['data']['id']]; + $this->newBookingMailService->process($mailParams); + + $response['status'] = true; + $response['data'] = $param; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + DB::rollBack(); + } + + return $response; + + } + + public function modifiedBooking($channelCode, $param) + { + + $response = ['status' => false, 'message' => '']; + + DB::beginTransaction(); + + try { + + $channelService = App::make("App\Core\Service\ChannelManager\\{$channelCode}"); + + $bookingCode = null; + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['booking']['property_id']], + ['field' => 'search_key', 'condition' => '=', 'value' => $param['booking']['search_key']], + //['field' => 'channel_manager_id', 'condition' => '=', 'value' => $param['booking']['channel_manager_id']], + ], + 'with' => ['bookingRoom', 'bookingContact', 'bookingChannel', 'bookingPayment'], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success') { + throw new ApiErrorException(lang('Booking could not be found, Reservation: ' . $param['booking']['search_key'])); + } + + if (!empty($bookingDetail['data'])) { + + $bookingCode = $bookingDetail['data']['booking_code']; + + $param['booking']['id'] = $bookingDetail['data']['id']; + $param['booking']['booking_code'] = $bookingDetail['data']['booking_code']; + + $bookingUpdateParam = [ + 'channel_booking_code' => fillOnUndefined($param['booking'], 'channel_booking_code'), + 'search_key' => $param['booking']['search_key'], + 'checkin_date' => $param['booking']['checkin_date'], + 'checkout_date' => $param['booking']['checkout_date'], + 'payment_type_code' => $param['booking']['payment_type_code'], + 'total' => $param['booking']['total'], + 'currency_code' => $param['booking']['currency_code'], + ]; + + $bookingUpdate = $this->bookingService->update($bookingDetail['data']['id'], $bookingUpdateParam); + + $bookingContactUpdateParam = [ + 'name' => $param['contact']['name'], + 'surname' => $param['contact']['surname'], + 'phone_code' => $param['contact']['phone_code'], + 'phone_number' => $param['contact']['phone_number'], + 'email' => $param['contact']['email'], + 'country_code' => !empty($param['contact']['country_code']) ? $param['contact']['country_code'] : null, + 'note' => !empty($param['contact']['note']) ? $param['contact']['note'] : null, + 'language_code' => fillOnUndefined($param['contact'], 'language_code', 'en') + ]; + + $bookingContactUpdate = $this->bookingContactService->update($bookingDetail['data']['booking_contact']['id'], $bookingContactUpdateParam); + + foreach ($bookingDetail['data']['booking_room'] as $roomOrder => $room) { + + foreach ($param['room'] as $roomOrderChannel => $roomChannel) { + + if ($roomOrder == $roomOrderChannel) { + + $bookingRoomUpdateParam = [ + 'occupancy_code' => $roomChannel['occupancy_code'], + 'checkin_date' => $roomChannel['checkin_date'], + 'checkout_date' => $roomChannel['checkout_date'], + 'room_id' => $roomChannel['room_id'], + 'room_name' => $roomChannel['room_name'], + 'room_rate_mapping_id' => $roomChannel['room_rate_mapping_id'], + 'room_rate_name' => $roomChannel['room_rate_name'], + 'rate_detail' => json_encode($roomChannel), + 'total' => $roomChannel['total'], + 'currency_code' => $roomChannel['currency_code'], + 'daily_amount' => fillOnUndefined($roomChannel, 'daily_amount'), + ]; + + $bookingRoomUpdate = $this->bookingRoomService->update($room['id'], $bookingRoomUpdateParam); + + /* ROOM AVAILABILITY */ + //Eğer odaya ait tarihler farklı ise buraya gelecek. + if (($room['checkin_date'] != $roomChannel['checkin_date']) || ($room['checkout_date'] != $roomChannel['checkout_date'])) { + + $dateByDay = []; + $dateByDay['update'] = []; + $dateByDay['current'] = $this->getDateByDay(['checkIn' => $room['checkin_date'], 'checkOut' => $room['checkout_date']]); + $dateByDay['modified'] = $this->getDateByDay(['checkIn' => $roomChannel['checkin_date'], 'checkOut' => $roomChannel['checkout_date']]); + + foreach ($dateByDay['current'] as $date) { + $dateByDay['update'][$date] = !isset($dateByDay['update'][$date]) ? 0 : $dateByDay['update'][$date]; + $dateByDay['update'][$date]++; + } + + foreach ($dateByDay['modified'] as $date) { + $dateByDay['update'][$date] = !isset($dateByDay['update'][$date]) ? 0 : $dateByDay['update'][$date]; + $dateByDay['update'][$date]--; + } + + ksort($dateByDay['update']); + foreach ($dateByDay['update'] as $day => $availabilityModified) { + + $requestParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['booking']['property_id']], + ['field' => 'property_room_id', 'condition' => '=', 'value' => $room['room_id']], + ['field' => 'availability_type_id', 'condition' => '=', 'value' => 1], + ['field' => 'date', 'condition' => '=', 'value' => $day] + ], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->propertyRoomAvailabilityService->select($requestParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success') { + throw new ApiErrorException('getPropertyRoomAndRoomRateAvailability Empty'); + } + + foreach ($getPropertyRoomAndRoomRateAvailability['data'] as $roomAvailability) { + + $roomAvailabilityUpdated = 0; + $roomAvailabilityUpdated = $roomAvailability['availability'] + $availabilityModified; + if($roomAvailabilityUpdated < 0) { + $roomAvailabilityUpdated = 0; + } + + $this->propertyRoomAvailabilityService->update($roomAvailability['id'], ['availability' => $roomAvailabilityUpdated]); + } + + } + } + /* ROOM AVAILABILITY */ + + } + } + + } + + //Added New Room Case + if (count($bookingDetail['data']['booking_room']) != count($param['room']) && count($param['room']) > count($bookingDetail['data']['booking_room']) ) { + + foreach ($param['room'] as $roomOrderChannel => $roomChannel) { + + foreach ($bookingDetail['data']['booking_room'] as $roomOrder => $room) { + + if ($roomOrder != $roomOrderChannel) { + + $bookingRoomCreateParam = [ + 'booking_id' => $bookingDetail['data']['id'], + 'room_order_number' => ($roomOrderChannel + 1), + 'occupancy_code' => $roomChannel['occupancy_code'], + 'checkin_date' => $roomChannel['checkin_date'], //$room['checkin'], + 'checkout_date' => $roomChannel['checkout_date'],//$room['checkout'], + 'rate_key' => fillOnUndefined($roomChannel, 'rate_key'), + 'rate_key_code' => fillOnUndefined($roomChannel, 'rate_key_code'), + 'availability_id' => 1, + 'availability_code' => 'ROM', + 'room_id' => $roomChannel['room_id'], + 'room_name' => $roomChannel['room_name'], + 'room_rate_mapping_id' => $roomChannel['room_rate_mapping_id'], + 'room_rate_name' => $roomChannel['room_rate_name'], + 'cancellation_policy' => fillOnUndefined($roomChannel, 'cancellation_policy'), + 'payment_type_code' => fillOnUndefined($roomChannel, 'payment_type_code', 'CHN'), + 'daily_amount' => fillOnUndefined($roomChannel, 'daily_amount'), + 'extra_param' => fillOnUndefined($roomChannel, 'extra_param'), + 'rate_detail' => fillOnUndefined($roomChannel, 'rate_detail'), + 'total' => $roomChannel['total'], + 'currency_code' => $roomChannel['currency_code'], + 'status' => fillOnUndefined($roomChannel, 'status', 1), + ]; + + $bookingRoomCreate = $this->bookingRoomService->create($bookingRoomCreateParam); + + } + } + + } + } + + //UPDATE PAYMENT DATA + $bookingPaymentUpdateParam = [ + 'payment_type_code' => fillOnUndefined($param['payment'], 'payment_type_code'), + 'payment_source_code' => fillOnUndefined($param['payment'], 'payment_source_code'), + 'extra_param' => fillOnUndefined($param['payment'], 'extra_param'), + 'total' => fillOnUndefined($param['payment'], 'total'), + 'currency_code' => fillOnUndefined($param['payment'], 'currency_code') + ]; + + $bookingPaymentUpdate = $this->bookingPaymentService->update($bookingDetail['data']['booking_payment']['id'], $bookingPaymentUpdateParam); + //UPDATE PAYMENT DATA + + + //INSERT CHANNEL PAYMENT DATA + if (isset($param['payment_channel']) && !empty($param['payment_channel'])) { + + $bookingPaymentDataCreateParam = [ + 'booking_id' => $bookingDetail['data']['id'], + 'type' => fillOnUndefined($param['payment_channel'], 'type'), + 'data' => fillOnUndefined($param['payment_channel'], 'data'), + 'status' => fillOnUndefined($param['payment_channel'], 'status', 1), + ]; + + $bookingPaymentDataCreate = $this->bookingPaymentService->createPaymentData($bookingPaymentDataCreateParam); + + if ($bookingPaymentDataCreate['status'] != 'success') { + throw new ApiErrorException('Booking Channel Payment could not be made'); + } + } + //INSERT CHANNEL PAYMENT DATA + + + } else { + return $this->createBooking($channelCode, $param); + } + + $bookingCode = is_null($bookingCode) ? 'ENW' . $param['booking']['search_key'] : $bookingCode; + + $reservationConfirmParam = $channelService->reservationConfirmParam($param['channel_manager']['property_id'], $param['channel_manager']['booking_id'], $bookingCode, $param); + $reservationConfirm = $channelService->reservationConfirm($reservationConfirmParam); + + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + + $notificationModifiedToPropertyUser = [ + 'booking_id' => $param['booking']['id'], + ]; + + $this->mailer->onQueue('modifiedBookingMail', new ModifiedBookingMail($notificationModifiedToPropertyUser)); + + $response['status'] = true; + $response['data'] = $param; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if ($response['status']) { + + //PUSH CHANNEL MANAGER QUEUE + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingDetail['data']['property_id']], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $channelPropertyData) { + + if(in_array($channelPropertyData['channel_manager_id'],[11,13])) { + continue; + } + + $channelManagerBookingCreateParam = [ + 'property_id' => $bookingDetail['data']['property_id'], + 'booking_id' => $bookingDetail['data']['id'], + 'channel_manager_id' => $channelPropertyData['channel_manager_id'], + 'type' => 'Modify', + ]; + + $channelManagerBookingCreate = $this->channelManagerBookingService->create($channelManagerBookingCreateParam); + + } + + } + //PUSH CHANNEL MANAGER QUEUE + + DB::commit(); + + } else { + DB::rollBack(); + } + + return $response; + + } + + public function cancelBooking($channelCode, $param) + { + + $response = ['status' => false, 'message' => '']; + + DB::beginTransaction(); + + try { + + $channelService = App::make("App\Core\Service\ChannelManager\\{$channelCode}"); + + $bookingCode = null; + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['booking']['property_id']], + ['field' => 'search_key', 'condition' => '=', 'value' => $param['booking']['search_key']], + //['field' => 'channel_manager_id', 'condition' => '=', 'value' => $param['booking']['channel_manager_id']], + ], + 'with' => ['bookingRoom', 'bookingContact', 'bookingChannel'], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success') { + throw new ApiErrorException(lang('Booking could not be found, Reservation: ' . $param['booking']['search_key'])); + } + + if (!empty($bookingDetail['data'])) { + + $bookingCode = $bookingDetail['data']['booking_code']; + + $param['booking']['id'] = $bookingDetail['data']['id']; + $param['booking']['booking_code'] = $bookingDetail['data']['booking_code']; + + $this->bookingService->update($bookingDetail['data']['id'], ['status' => 0]); + + foreach ($bookingDetail['data']['booking_room'] as $roomOrder => $room) { + + $bookingRoomUpdate = $this->bookingRoomService->update($room['id'], ['status' => 0]); + + /* ROOM AVAILABILITY */ + $dateByDay = []; + $dateByDay = $this->getDateByDay(['checkIn' => $room['checkin_date'], 'checkOut' => $room['checkout_date']]); + foreach ($dateByDay as $day) { + + $requestParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['booking']['property_id']], + ['field' => 'property_room_id', 'condition' => '=', 'value' => $room['room_id']], + ['field' => 'availability_type_id', 'condition' => '=', 'value' => 1], + ['field' => 'date', 'condition' => '=', 'value' => $day] + ], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->propertyRoomAvailabilityService->select($requestParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success') { + throw new ApiErrorException('getPropertyRoomAndRoomRateAvailability Empty'); + } + + foreach ($getPropertyRoomAndRoomRateAvailability['data'] as $roomAvailability) { + $roomAvailabilityUpdated = $roomAvailability['availability'] <= 0 ? 1 : ($roomAvailability['availability'] + 1); + $this->propertyRoomAvailabilityService->update($roomAvailability['id'], ['availability' => $roomAvailabilityUpdated]); + } + + } + + /* ROOM AVAILABILITY */ + + } + + } + + $bookingCode = is_null($bookingCode) ? 'ENW' . $param['booking']['search_key'] : $bookingCode; + + $reservationConfirmParam = $channelService->reservationConfirmParam($param['channel_manager']['property_id'], $param['channel_manager']['booking_id'], $bookingCode, $param); + $reservationConfirm = $channelService->reservationConfirm($reservationConfirmParam); + + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + $notificationCancelToPropertyUser = [ + 'booking_id' => $param['booking']['id'], + ]; + + $this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($notificationCancelToPropertyUser)); + + $response['status'] = true; + $response['data'] = $param; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if ($response['status']) { + + //PUSH CHANNEL MANAGER QUEUE + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingDetail['data']['property_id']], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $channelPropertyData) { + + if(in_array($channelPropertyData['channel_manager_id'],[11,13])) { + continue; + } + + $channelManagerBookingCreateParam = [ + 'property_id' => $bookingDetail['data']['property_id'], + 'booking_id' => $bookingDetail['data']['id'], + 'channel_manager_id' => $channelPropertyData['channel_manager_id'], + 'type' => 'Cancel', + ]; + + $channelManagerBookingCreate = $this->channelManagerBookingService->create($channelManagerBookingCreateParam); + + } + + } + //PUSH CHANNEL MANAGER QUEUE + + DB::commit(); + + } else { + DB::rollBack(); + } + + return $response; + + } + + public function handle() + { + + try { + + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + $channelManagerCriteria = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManager = $this->channelManagerService->select($channelManagerCriteria); + + $channelManagerList = []; + if ($channelManager['status'] == 'success' && !empty($channelManager['data'])) { + $channelManagerList = $channelManager['data']; + } + + + //CHANNEL MANAGER + foreach ($channelManagerList as $channelKey => $channel) { + + $this->info(date('Y-m-d H:i:s') . ' : Channel Start: ' . $channel['name']); + + if (!class_exists("App\Core\Service\ChannelManager\\{$channel['name']}")) { + $this->error(date('Y-m-d H:i:s') . ' : Channel: ' . $channel['name'] . ' Class does not exist!'); + continue; + } + + $channelService = App::make("App\Core\Service\ChannelManager\\{$channel['name']}"); + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $channel['id']], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping', 'property'], + 'orderBy' => [ + ['field' => 'property_id', 'value' => 'ASC'] + ] + ]; + + //TODO: Channex limited hotels + if ($channel['id'] == 2) { + $channexPullReservationPropertyIds = [71,313, /*538, 541, 545, 546, 548, 549, 550,*/ 672, 712, 785, 808, 790, 755, 901, 952, 912, 848, 1098, 1103, 1035]; + $channelManagerPropertyMappingCriteria['whereIn'][] = ["field" => "property_id", "value" => $channexPullReservationPropertyIds]; + } + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + //Eğer kanala ait bir otel yok ise devam et + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + continue; + } + + if ($channelManagerPropertyMapping['status'] == 'success') { + + //CHANNEL MANAGER PROPERTY + foreach ($channelManagerPropertyMapping['data'] as $propertyMapping) { + + //Property + if (!in_array($propertyMapping['property']['id'],[623])) { + //continue; + } + + $this->info(date('Y-m-d H:i:s') . ' : Property: ' . $propertyMapping['property']['id'] . ' - ' . $propertyMapping['property']['name']); + + $reservationListParam = $channelService->reservationListParam($propertyMapping['channel_manager_property_id']); + $reservationList = $channelService->reservationList($reservationListParam); + + //Eğer kanaldaki otele ait bir rezervasyon yok ise devam et + if (!$reservationList['status']) { + continue; + } + + //CHANNEL MANAGER PROPERTY Reservation + foreach ($reservationList['data'] as $reservationKey => $reservation) { + + + $reservationPullFormatted = $channelService->reservationPullFormattedData($propertyMapping['property_id'], $propertyMapping['channel_manager_property_id'], $reservation); + + if (!$reservationPullFormatted['status']) { + //Burada bi hata var demektir, mail atılsın ama sonraki işleme devam edilsin. + + //$logMessage + $mailParams = [ + 'title' => $channel['name'] . ' - ReservationPullService Error - reservationPullFormatted', + 'logMessage' => '
' . print_r($reservationPullFormatted, true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $this->error(date('Y-m-d H:i:s') . ' : Property: ' . $propertyMapping['property']['id'] . ' - ' . $propertyMapping['property']['name'].' Error: reservationPullFormattedData'); + + continue; + } + + //TODO: Eğer burada gelen kanal extranetwork ise alınmış gibi yapılıp diğer işleme geçilmeli + if (in_array($reservationPullFormatted['data']['channel_manager']['sub_channel'], ['ExtranetWork'])) { + + $reservationConfirmParam = $channelService->reservationConfirmParam($reservationPullFormatted['data']['channel_manager']['property_id'], $reservationPullFormatted['data']['channel_manager']['booking_id'], 'ENW' . $reservationPullFormatted['data']['channel_manager']['booking_id'], $reservationPullFormatted['data']); + $reservationConfirm = $channelService->reservationConfirm($reservationConfirmParam); + + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + continue; + } + + + $reservationPull = ['status' => false, 'message' => '']; + + + if ($reservationPullFormatted['type'] == 'createBooking') { + $reservationPull = $this->createBooking($channel['name'], $reservationPullFormatted['data']); + } elseif ($reservationPullFormatted['type'] == 'modifiedBooking') { + + //If the modified reservation is not found, first create a reservation. + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $reservationPullFormatted['data']['booking']['property_id']], + ['field' => 'search_key', 'condition' => '=', 'value' => $reservationPullFormatted['data']['booking']['search_key']], + ], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if (empty($bookingDetail['data'])) { + $reservationPull = $this->createBooking($channel['name'], $reservationPullFormatted['data']); + } else { + $reservationPull = $this->modifiedBooking($channel['name'], $reservationPullFormatted['data']); + } + + + } elseif ($reservationPullFormatted['type'] == 'cancelBooking') { + $reservationPull = $this->cancelBooking($channel['name'], $reservationPullFormatted['data']); + } + + if ($reservationPull['status']) { + $this->info(date('Y-m-d H:i:s') . ' : Success ' . $reservationPullFormatted['type'] . ': ' . $reservationPull['data']['booking']['booking_code']); + } else { + + //$logMessage + $mailParams = [ + 'title' => $channel['name'] . ' - ReservationPullService Error - Booking', + 'logMessage' => '
' . print_r(array_merge($reservationPullFormatted, $reservationPull), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $reservationPull['message']); + } + + + } + + } + + + } + + $this->info(PHP_EOL); + } + + + //dd($channelManagerList); + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/ChannelManager/ReservationPushService.php b/app/Console/Commands/ChannelManager/ReservationPushService.php new file mode 100644 index 0000000..967768f --- /dev/null +++ b/app/Console/Commands/ChannelManager/ReservationPushService.php @@ -0,0 +1,150 @@ +mailer = $mailer; + $this->channelService = $channelService; + $this->channelManagerBookingService = $channelManagerBookingService; + + } + + public function handle() + { + + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + $pendingBookingCriteria = [ + 'criteria' => + [ + //['field' => 'id', 'condition' => '=', 'value' => 22461],#21390 + ['field' => 'is_pushed', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'channelManager', + 'bookingDetail.bookingContact', 'bookingDetail.bookingChannel', + 'bookingDetail.bookingPayment', 'bookingDetail.bookingPaymentType', + 'bookingDetail.bookingStatus', 'bookingDetail.bookingRoom.roomPax.paxCountry' + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + 'take' => 100 + ]; + + $pendingBooking = $this->channelManagerBookingService->select($pendingBookingCriteria); + + + if ($pendingBooking['status'] == 'success') { + + $pendingBooking = $pendingBooking['data']; + + foreach ($pendingBooking as $booking) { + + try { + + $this->info(date('Y-m-d H:i:s') . ' : Booking ID: ' . $booking['booking_id']); + + $channelService = App::make("App\Core\Service\ChannelManager\\{$booking['channel_manager']['name']}"); + + $this->info(date('Y-m-d H:i:s') . ' : Channel: ' . $booking['channel_manager']['name']); + + if ($booking['channel_manager_id'] == 10) { + //After 3 minutes, the faulty operation is set to status 2 for Hyperguest. + if (Carbon::createFromTimestamp($booking['created_at'])->diffInMinutes(Carbon::now()) > 3) { + $this->channelManagerBookingService->update($booking['id'], ['status' => 2]); + $this->error(date('Y-m-d H:i:s') . ' : Channel: ' . $booking['channel_manager']['name']); + continue; + } + } + + //After 30 minutes, the faulty operation is set to status 2. + if (Carbon::createFromTimestamp($booking['created_at'])->diffInMinutes(Carbon::now()) > 30) { + $this->channelManagerBookingService->update($booking['id'], ['status' => 2]); + } + + $requestPostReservationPushParam = $channelService->requestPostReservationPushParam($booking['property_id'], $booking['booking_id'], $booking['type'], $booking); + + if (empty($requestPostReservationPushParam)) { + throw new ApiErrorException('requestPostReservationPushParam Error'); + } + + $this->info(date('Y-m-d H:i:s') . ' : Booking ID: ' . $booking['booking_id'] . ' Push'); + + $this->channelManagerBookingService->update($booking['id'], ['request' => $requestPostReservationPushParam]); + + $requestPostReservationPush = $channelService->requestPostReservationPush($requestPostReservationPushParam); + + if ($requestPostReservationPush['status']) { + + $this->info(date('Y-m-d H:i:s') . ' : Booking ID: ' . $booking['booking_id'] . ' Success'); + $this->channelManagerBookingService->update($booking['id'], ['is_pushed' => 1, 'status' => 1, 'response' => $requestPostReservationPush['response']]); + + } else { + + //$logMessage + $mailParams = [ + 'title' => 'ReservationPushService Error - ChannelManagerName: ' . $booking['channel_manager']['name'], + 'logMessage' => '
' . print_r(array_merge($booking, $requestPostReservationPush), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $this->error(date('Y-m-d H:i:s') . ' : Booking ID: ' . $booking['booking_id'] . ' Failed'); + + $this->channelManagerBookingService->update($booking['id'], ['response' => $requestPostReservationPush['message']]); + } + + + } catch (ApiErrorException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + } + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + + } + +} diff --git a/app/Console/Commands/ChannelManager/RoomAvailabilityPushService.php b/app/Console/Commands/ChannelManager/RoomAvailabilityPushService.php new file mode 100644 index 0000000..749a34c --- /dev/null +++ b/app/Console/Commands/ChannelManager/RoomAvailabilityPushService.php @@ -0,0 +1,262 @@ +mailer = $mailer; + $this->channelManagerService = $channelManagerService; + $this->propertyRoomAvailabilityQueueService = $propertyRoomAvailabilityQueueService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + } + + public function handle() + { + + try { + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + //Queue Check + $this->info(date('Y-m-d H:i:s') . ' : Queue Check'); + $roomAvailabilityQueueCountCriteria = ['criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]], 'count' => true]; + $roomAvailabilityQueueCount = $this->propertyRoomAvailabilityQueueService->select($roomAvailabilityQueueCountCriteria); + if ($roomAvailabilityQueueCount['status'] == 'success') { + $roomAvailabilityQueueCount = $roomAvailabilityQueueCount['data']; + + if ($roomAvailabilityQueueCount > 2000) { + + + $roomAvailabilityQueueCountGroupByProperty = PropertyRoomAvailabilityQueue::selectRaw('property_id, COUNT(*) as total') + ->groupBy('property_id') + ->orderByDesc('total') + ->first(); + + $roomAvailabilityQueueCountGroupByProperty = $roomAvailabilityQueueCountGroupByProperty ? $roomAvailabilityQueueCountGroupByProperty->toArray() : null; + if ($roomAvailabilityQueueCountGroupByProperty) { + if ($roomAvailabilityQueueCountGroupByProperty['total'] > 1000) { + PropertyRoomAvailabilityQueue::where('property_id', $roomAvailabilityQueueCountGroupByProperty['property_id'])->delete(); + } + } + + } + + if ($roomAvailabilityQueueCount > 1000) { + + $this->error(date('Y-m-d H:i:s') . ' : Queue Alarm: ' . $roomAvailabilityQueueCount); + + //Clear Test Property Data + PropertyRoomAvailabilityQueue::where('property_id', 1)->delete(); + + //$logMessage + $mailParams = [ + 'title' => 'RoomAvailabilityPushService Error - Queue Error', + 'logMessage' => 'Queue: ' . $roomAvailabilityQueueCount + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + } + } + //Queue Check + + + $channelManagerCriteria = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + /*'whereIn' => + [ + ['field' => 'id', "value" => [7]] + ],*/ + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManager = $this->channelManagerService->select($channelManagerCriteria); + + $channelManagerList = []; + if ($channelManager['status'] == 'success' && !empty($channelManager['data'])) { + $channelManagerList = $channelManager['data']; + } + + + $roomAvailabilityQueueCriteria = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['property'/*, 'propertyChannelManager.channelManagerRoomRate.propertyRoomRateMapping'*/], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + 'take' => 500 + ]; + + $roomAvailabilityQueue = $this->propertyRoomAvailabilityQueueService->select($roomAvailabilityQueueCriteria); + + if ($roomAvailabilityQueue['status'] == 'success' && !empty($roomAvailabilityQueue['data'])) { + + + foreach ($roomAvailabilityQueue['data'] as $data) { + $roomAvailabilityQueueData[$data['property_id']][] = $data; + } + + + $channelManagerPushedIdsList = []; + $channelManagerNoneMappingIdsList = []; + + //PROPERTY + foreach ($roomAvailabilityQueueData as $roomAvailabilityQueuePropertyId => $roomAvailabilityQueueProperty) { + + + //CHANNEL MANAGER + $channelManagerCheckList = []; + foreach ($channelManagerList as $channelKey => $channel) { + + if (in_array($channel['id'], [7, 10, 13])) { + continue; + } + + $this->info(date('Y-m-d H:i:s') . ' : Channel Start: ' . $channel['name']); + + if (!class_exists("App\Core\Service\ChannelManager\\{$channel['name']}")) { + $this->error(date('Y-m-d H:i:s') . ' : Channel: ' . $channel['name'] . ' Class does not exist!'); + continue; + } + + $channelService = App::make("App\Core\Service\ChannelManager\\{$channel['name']}"); + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $roomAvailabilityQueuePropertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $channel['id']], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'firstRow' => true, + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + + if ($channelManagerPropertyMapping['status'] != 'success' || ($channelManagerPropertyMapping['status'] == 'success' && empty($channelManagerPropertyMapping['data']))) { + $this->info(date('Y-m-d H:i:s') . ' : Channel: ' . $channel['name'] . ' PropertyID: ' . $roomAvailabilityQueuePropertyId . ' : PASS'); + continue; + } + + $channelManagerCheckList[$channel['id']] = true; + + $roomAvailabilityPush = $channelService->roomAvailabilityPush($roomAvailabilityQueuePropertyId, $roomAvailabilityQueueProperty); + + if ($roomAvailabilityPush['status']) { + + $channelManagerPushedIdsList[$channel['id']] = $roomAvailabilityPush['data']['channelManagerPushedIds']; + $channelManagerNoneMappingIdsList[$channel['id']] = $roomAvailabilityPush['data']['channelManagerNoneMappingIds']; + + $this->info(date('Y-m-d H:i:s') . ' : Success: ' . $channel['name']); + + } else { + + $channelManagerCheckList[$channel['id']] = false; + + //$logMessage + $mailParams = [ + 'title' => $channel['name'] . ' - RoomAvailabilityPushService Error - inventoryRoomRateUpdate Error', + 'logMessage' => '
' . print_r($roomAvailabilityPush, true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $channel['name'] . ' - ' . $roomAvailabilityPush['message']); + + + } + + $this->info(date('Y-m-d H:i:s') . ' : Channel Finished: ' . $channel['name']); + + } + + if (in_array(false, $channelManagerCheckList)) { + $this->error(date('Y-m-d H:i:s') . ' : Error Break: ' . var_export($channelManagerCheckList)); + continue; + } + + if (count($channelManagerPushedIdsList) > 1) { + $channelManagerPushedIdIntersect = call_user_func_array('array_intersect', $channelManagerPushedIdsList); + if (!empty($channelManagerPushedIdIntersect)) { + $this->propertyRoomAvailabilityQueueService->delete($channelManagerPushedIdIntersect); + $this->info(date('Y-m-d H:i:s') . ' : channelManagerPushedIdsList Deleted: ' . var_dump($channelManagerPushedIdIntersect)); + } + } elseif (!empty($channelManagerPushedIdsList)) { + $this->propertyRoomAvailabilityQueueService->delete(reset($channelManagerPushedIdsList)); + $this->info(date('Y-m-d H:i:s') . ' : channelManagerPushedIdsList Deleted: ' . var_dump($channelManagerPushedIdsList)); + } + + if (empty($channelManagerNoneMappingIdsList)) { + $this->info(date('Y-m-d H:i:s') . ' : channelManagerNoneMappingIdsList Empty'); + continue; + } + + $channelManagerNoneMappingIdsListMerge = call_user_func_array('array_merge', $channelManagerNoneMappingIdsList); + if (!empty($channelManagerNoneMappingIdsListMerge)) { + $this->propertyRoomAvailabilityQueueService->delete($channelManagerNoneMappingIdsListMerge); + $this->info(date('Y-m-d H:i:s') . ' : channelManagerNoneMappingIdsListMerge Deleted: ' . var_dump($channelManagerNoneMappingIdsListMerge)); + } + + } + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/ChannelManager/RoomRatePushService.php b/app/Console/Commands/ChannelManager/RoomRatePushService.php new file mode 100644 index 0000000..a48b1cd --- /dev/null +++ b/app/Console/Commands/ChannelManager/RoomRatePushService.php @@ -0,0 +1,255 @@ +mailer = $mailer; + $this->propertyRoomRatePriceQueueService = $propertyRoomRatePriceQueueService; + $this->channelManagerService = $channelManagerService; + $this->propertyRoomAvailabilityQueueService = $propertyRoomAvailabilityQueueService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + } + + public function handle() + { + //$request = $this->channelService->productList(14480); + + try { + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + //Queue Check + $this->info(date('Y-m-d H:i:s') . ' : Queue Check'); + $roomRatePriceQueueCountCriteria = ['criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]],'count' => true]; + $roomRatePriceQueueCount = $this->propertyRoomRatePriceQueueService->select($roomRatePriceQueueCountCriteria); + if($roomRatePriceQueueCount['status'] == 'success') { + $roomRatePriceQueueCount = $roomRatePriceQueueCount['data']; + + if($roomRatePriceQueueCount > 1000) { + + $this->error(date('Y-m-d H:i:s') . ' : Queue Alarm: '. $roomRatePriceQueueCount); + + //Clear Test Property Data + PropertyRoomRatePriceQueue::where('property_id',1)->delete(); + + //$logMessage + $mailParams = [ + 'title' => 'RoomRatePushService Error - Queue Error', + 'logMessage' => 'Queue: '.$roomRatePriceQueueCount + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + } + } + //Queue Check + + $channelManagerCriteria = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + /*'whereIn' => + [ + ['field' => 'id', "value" => [7]] + ],*/ + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManager = $this->channelManagerService->select($channelManagerCriteria); + + $channelManagerList = []; + if ($channelManager['status'] == 'success' && !empty($channelManager['data'])) { + $channelManagerList = $channelManager['data']; + } + + $roomRatePriceQueueCriteria = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['property'/*, 'propertyChannelManager.channelManagerRoomRate'*/], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + 'take' => 500 + ]; + + $roomRatePriceQueue = $this->propertyRoomRatePriceQueueService->select($roomRatePriceQueueCriteria); + + if ($roomRatePriceQueue['status'] == 'success' && !empty($roomRatePriceQueue['data'])) { + + + foreach ($roomRatePriceQueue['data'] as $data) { + $roomRatePriceQueueData[$data['property_id']][] = $data; + } + + $channelManagerPushedIdsList = []; + $channelManagerNoneMappingIdsList = []; + + //PROPERTY + foreach ($roomRatePriceQueueData as $roomRatePriceQueuePropertyId => $roomRatePriceQueueProperty) { + + + //CHANNEL MANAGER + $channelManagerCheckList = []; + foreach ($channelManagerList as $channelKey => $channel) { + + if(in_array($channel['id'],[7,10, 13])) { + continue; + } + + $this->info(date('Y-m-d H:i:s') . ' : Channel Start: ' . $channel['name']); + + if(!class_exists("App\Core\Service\ChannelManager\\{$channel['name']}")) { + $this->error(date('Y-m-d H:i:s') . ' : Channel: ' . $channel['name'].' Class does not exist!'); + continue; + } + + $channelService = App::make("App\Core\Service\ChannelManager\\{$channel['name']}"); + + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $roomRatePriceQueuePropertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $channel['id']], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'firstRow' => true, + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + + if ($channelManagerPropertyMapping['status'] != 'success' || ($channelManagerPropertyMapping['status'] == 'success' && empty($channelManagerPropertyMapping['data']))) { + $this->info(date('Y-m-d H:i:s') . ' : Channel: ' . $channel['name'] . ' PropertyID: ' . $roomRatePriceQueuePropertyId . ' : PASS'); + continue; + } + + $channelManagerCheckList[$channel['id']] = true; + + + $roomRatePricePush = $channelService->roomRatePricePush($roomRatePriceQueuePropertyId, $roomRatePriceQueueProperty); + + + if ($roomRatePricePush['status']) { + + if (!empty($roomRatePricePush['data']['channelManagerPushedIds']) || !empty($roomRatePricePush['data']['channelManagerNoneMappingIds'])) { + $channelManagerPushedIdsList[$channel['id']] = $roomRatePricePush['data']['channelManagerPushedIds']; + $channelManagerNoneMappingIdsList[$channel['id']] = $roomRatePricePush['data']['channelManagerNoneMappingIds']; + } + + $this->info(date('Y-m-d H:i:s') . ' : Channel Success: ' . $channel['name']); + + } else { + + $channelManagerCheckList[$channel['id']] = false; + + //$logMessage + $mailParams = [ + 'title' => $channel['name'] . ' - RoomRatePushService Error - InventoryRoomRateUpdate Error', + 'logMessage' => '
' . print_r($roomRatePricePush, true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $channel['name'] . ' - ' . $roomRatePricePush['message']); + + + } + + $this->info(date('Y-m-d H:i:s') . ' : Channel Finished: ' . $channel['name']); + + } + + /*foreach ($channelManagerPushedIdsList as $key => $channelManagerPushedIds) { + if(empty($channelManagerPushedIds)) { + unset($channelManagerPushedIdsList[$key]); + } + }*/ + + if (in_array(false, $channelManagerCheckList)) { + $this->error(date('Y-m-d H:i:s') . ' : Error Break: ' . var_export($channelManagerCheckList)); + continue; + } + + if (count($channelManagerPushedIdsList) > 1) { + $channelManagerPushedIdIntersect = call_user_func_array('array_intersect', $channelManagerPushedIdsList); + if (!empty($channelManagerPushedIdIntersect)) { + $this->propertyRoomRatePriceQueueService->delete($channelManagerPushedIdIntersect); + $this->info(date('Y-m-d H:i:s') . ' : $channelManagerPushedIdsList Deleted: ' . var_export($channelManagerPushedIdIntersect)); + } + } elseif (!empty($channelManagerPushedIdsList)) { + $this->propertyRoomRatePriceQueueService->delete(reset($channelManagerPushedIdsList)); + $this->info(date('Y-m-d H:i:s') . ' : $channelManagerPushedIdsList Deleted: ' . var_export($channelManagerPushedIdsList)); + } + + if (!empty($channelManagerNoneMappingIdsList)) { + $channelManagerNoneMappingIdsListMerge = call_user_func_array('array_merge', $channelManagerNoneMappingIdsList); + if (!empty($channelManagerNoneMappingIdsListMerge)) { + $this->propertyRoomRatePriceQueueService->delete($channelManagerNoneMappingIdsListMerge); + $this->info(date('Y-m-d H:i:s') . ' : $channelManagerNoneMappingIdsListMerge Deleted: ' . var_dump($channelManagerNoneMappingIdsListMerge)); + } + } + + + } + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/ChannelManager/_ReselivaAvailRateUpdateService.php b/app/Console/Commands/ChannelManager/_ReselivaAvailRateUpdateService.php new file mode 100644 index 0000000..95b93f0 --- /dev/null +++ b/app/Console/Commands/ChannelManager/_ReselivaAvailRateUpdateService.php @@ -0,0 +1,497 @@ +mailer = $mailer; + $this->propertyRoomService = $propertyRoomService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->channelManagerLogService = $channelManagerLogService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + } + + public function propertyChannelMapping($propertyId, $channelId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId, $channelId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId, $channelId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $channelId], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + if (!is_null($channelManagerPropertyMapping['channel_manager_property_id'])) { + throw new ApiErrorException('This hotel can only be updated by ENW'); + } + + $response = [ + 'status' => true + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + $channelId = 1; + $channelManagerId = 1; + + $channelManagerLogCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => 32670], + ['field' => 'service', 'condition' => '=', 'value' => 'AvailRateUpdate'], + ['field' => 'status', 'condition' => '=', 'value' => null], + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManagerLog = $this->channelManagerLogService->select($channelManagerLogCriteria); + + if ($channelManagerLog['status'] && !empty($channelManagerLog['data'])) { + + foreach ($channelManagerLog['data'] as $logData) { + + $response = ['status' => false, 'message' => '']; + + $payloadXML = simplexml_load_string($logData['request']); + $payloadParam = json_decode(json_encode($payloadXML), 1); + + try { + + $propertyId = $payloadParam['Hotel']['@attributes']['id']; + + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId, $channelId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId, $channelId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId, $channelId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $availRateUpdates = singleElementXMLArray($payloadParam['AvailRateUpdate']); + + DB::beginTransaction(); + + foreach ($availRateUpdates as $availRateUpdate) { + + $dateRange = []; + $dateRange['startDate'] = $availRateUpdate['DateRange']['@attributes']['from']; + $dateRange['finishDate'] = $availRateUpdate['DateRange']['@attributes']['to']; + + if (Carbon::parse($dateRange['startDate'])->isBefore(Carbon::now()->toDateString()) || Carbon::parse($dateRange['finishDate'])->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + $roomTypes = singleElementXMLArray($availRateUpdate['RoomType']); + + $this->info(date('Y-m-d H:i:s') . ' : ' . $dateRange['startDate'] . ' - ' . $dateRange['finishDate']); + + foreach ($roomTypes as $roomType) { + + $roomId = $roomType['@attributes']['id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + throw new ApiErrorException('Tanımlı ya da aktif olmayan oda'); + } + + + $totalInventoryAvailable = null; + if (isset($roomType['Inventory']['@attributes']['totalInventoryAvailable'])) { + $totalInventoryAvailable = $roomType['Inventory']['@attributes']['totalInventoryAvailable']; + } + + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $dateRange['startDate'], + 'end_date' => $dateRange['finishDate'], + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + if (!is_null($totalInventoryAvailable)) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'availability'; + $requestParams['value'] = $totalInventoryAvailable; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $this->info(date('Y-m-d H:i:s') . ' : Update Type: ' . $requestParams['update_type']); + } + + + if (isset($roomType['RatePlan'])) { + + $ratePlans = singleElementXMLArray($roomType['RatePlan']); + + foreach ($ratePlans as $ratePlan) { + + $roomRateMappingId = $ratePlan['@attributes']['id']; + + $isCloseRoomRateMappingSale = null; + if (isset($ratePlan['@attributes']['closed'])) { + $isCloseRoomRateMappingSale = $ratePlan['@attributes']['closed'] == 'true' ? true : false; + } + + $channelRoomRateMappingCheck = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->isEmpty(); + if ($channelRoomRateMappingCheck) { + throw new ApiErrorException('Tanımlı ya da aktif olmayan oda konaklama'); + } + + + $roomRates = [ + 'room_id' => $roomId, + 'room_rate_mapping_id' => [ + $roomRateMappingId + ] + ]; + + + $paramsListChannel = []; + $paramsListChannel[] = 1; + //CONNECTED CHANNEL RATE UPDATE + $propertyChannelMappingConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParamBase['property_id']], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $requestParamBase['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + ]; + + $propertyChannelMappingConnected = $this->propertyChannelMappingService->select($propertyChannelMappingConnectedCriteria); + if ($propertyChannelMappingConnected['status'] == 'success') { + + foreach ($propertyChannelMappingConnected['data'] as $channel) { + + if (!is_null($channel['channel']['parent_id'])) { + continue; + } + + $paramsListChannel[] = $channel['channel_id']; + + } + + } + //CONNECTED CHANNEL RATE UPDATE + + + foreach ($paramsListChannel as $paramChannelId) { + + + //Eğer Rate var ise currency check yapılmalı ve PerDay var mı check edilmeli, burada sonra günceleme yaptırılabilri + if (isset($ratePlan['Rate'])) { + + $currency = $ratePlan['Rate']['@attributes']['currency']; + + $currencyCheck = ($currency == $propertyChannelMapping['currency_code']) ? true : false; + + if (!$currencyCheck) { + throw new ApiErrorException('Kanal döviz kuru ile eşleşmeyen döviz kuru, kanal döviz kuru: ' . $propertyChannelMapping['currency_code']); + } + + $channelRoomRateMapping = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->first(); + + $amountPerDay = $ratePlan['Rate']['PerDay']['@attributes']['rate']; + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate'; + $requestParams['value'] = $amountPerDay; + $requestParams['room_rates'] = [$roomRates]; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $this->info(date('Y-m-d H:i:s') . + ' : Update Type: ' .$requestParams['update_type']. + ' : Channel ID: '. $requestParams['channel_id']. + ' : Room ID: '. $roomRates['room_id'] + ); + } + + + //Room Rate Stop Sale + if (!is_null($isCloseRoomRateMappingSale)) { + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate_stop_sell'; + $requestParams['value'] = $isCloseRoomRateMappingSale ? 1 : 0; + $requestParams['room_rates'] = [$roomRates]; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $this->info(date('Y-m-d H:i:s') . ' : Update Type: ' . $requestParams['update_type']); + } + + + //Kısıtlamalar var ise min los + if (isset($ratePlan['Restrictions'])) { + + //Minimum Konaklama Gün Sayısı + if (isset($ratePlan['Restrictions']['@attributes']['minLOS'])) { + + $minStay = $ratePlan['Restrictions']['@attributes']['minLOS']; + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'min_stay'; + $requestParams['value'] = $minStay; + $requestParams['room_rates'] = [$roomRates]; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $this->info(date('Y-m-d H:i:s') . ' : Update Type: ' . $requestParams['update_type']); + + } + + } + + } + + + } + + } + + + } + + } + + $response['status'] = true; + + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 1 + ]; + + + $channelManagerLog = $this->channelManagerLogService->update($logData['id'], $updateDataLog); + + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + //TODO: mail + if (!$response['status']) { + DB::rollBack(); + } + + } + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + + } +} diff --git a/app/Console/Commands/ChannelManager/_ReservationPullService.php b/app/Console/Commands/ChannelManager/_ReservationPullService.php new file mode 100644 index 0000000..099b6f5 --- /dev/null +++ b/app/Console/Commands/ChannelManager/_ReservationPullService.php @@ -0,0 +1,784 @@ +mailer = $mailer; + $this->channelService = $channelService; + $this->channelManagerMappingService = $channelManagerMappingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->bookingService = $bookingService; + $this->bookingContactService = $bookingContactService; + $this->bookingRoomService = $bookingRoomService; + $this->bookingPaymentService = $bookingPaymentService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->newBookingMailService = $newBookingMailService; + } + + public function reservationListParam($channelManagerPropertyId) + { + + $userId = Config::get('app.channelManager.reseliva.userId'); + $userPSW = Config::get('app.channelManager.reseliva.userPassword'); + + $requestParam = new \SimpleXMLElement(''); + + $Authentication = $requestParam->addChild('Authentication'); + $Authentication->addChild('UserID', $userId); + $Authentication->addChild('UserPSW', $userPSW); + $Authentication->addChild('PropertyID', $channelManagerPropertyId); + + return $requestParam->asXML(); + } + + public function reservationConfirmParam($propertyId, $channelManagerBookingId, $bookingCode) + { + + $userId = Config::get('app.channelManager.reseliva.userId'); + $userPSW = Config::get('app.channelManager.reseliva.userPassword'); + + $requestParam = new \SimpleXMLElement(''); + + $Authentication = $requestParam->addChild('Authentication'); + $Authentication->addChild('UserID', $userId); + $Authentication->addChild('UserPSW', $userPSW); + $Authentication->addChild('PropertyID', $propertyId); + + $reservations = $requestParam->addChild('reservations'); + $reservation = $reservations->addChild('reservation'); + $reservation->addAttribute('reseliva_id', $channelManagerBookingId); + $reservation->addAttribute('pms_id', $bookingCode); + + return $requestParam->asXML(); + } + + public function getDateByDay($dates = []) + { + + $dateByDay = []; + + $diffInDays = Carbon::parse($dates['checkIn'])->floatDiffInDays(Carbon::parse($dates['checkOut'])); + + for ($i = 0; $i < $diffInDays; $i++) { + $dateByDay[] = Carbon::parse($dates['checkIn'])->addDay($i)->format('Y-m-d'); + } + + return $dateByDay; + + } + + public function createBooking($param) + { + + $response = ['status' => false, 'message' => '']; + + DB::beginTransaction(); + + try { + + $bookingCode = getCodeGenerate('BKG'); + $bookingStatus = 1; + + $roomRequest = []; + foreach ($param['room'] as $room) { + $roomRequest[] = [ + 'adults' => $room['totalpax'], + 'children' => $room['totalchd'], + 'age' => [] + ]; + } + + if (empty(fillOnUndefined($param, 'reservno_ota'))) { + $param['reservno_ota'] = null; + } + + + $bookingCreateParam = [ + 'property_id' => $param['property']['id'], + 'channel_id' => $param['property']['channel_id'], + 'booking_code' => $bookingCode, + 'channel_booking_code' => fillOnUndefined($param, 'reservno_ota'), + 'search_key' => $param['reservno'], + 'checkin_date' => $param['checkinFormatted'], + 'checkout_date' => $param['checkoutFormatted'], + 'rooms' => json_encode($roomRequest), + 'payment_type_code' => 'CHN', + 'room_amount' => $param['paymenttotal'], + 'addon_amount' => 0, + 'discount_amount' => 0, + 'total' => $param['paymenttotal'], + 'currency_code' => $param['paymentcurr'], + 'channel_token' => null, + 'booking_engine_token' => null, + 'reservation_time' => $param['restimeUnixFormatted'], + 'status' => $param['status'] == 'A' ? 1 : 0 + ]; + + $bookingCreate = $this->bookingService->create($bookingCreateParam); + //$bookingCreate['status'] = 'success'; + //$bookingCreate['data']['id'] = 52; + + if ($bookingCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking could not be made, Reservation: ' . $param['reservno'])); + } + + //INSERT CONTACT DATA + $bookingContactCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'name' => $param['firstname'], + 'surname' => !empty($param['lastname']) ? $param['lastname'] : null, + 'phone_code' => null, + 'phone_number' => $param['tel'], + 'email' => $param['email'], + 'note' => !empty($param['note']) ? $param['note'] : null, + 'language_code' => fillOnUndefined($param, 'language', 'en'), + 'status' => 1 + ]; + + $bookingContactCreate = $this->bookingContactService->create($bookingContactCreateParam); + //$bookingContactCreate['status'] = 'success'; + + if ($bookingContactCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Contact could not be made')); + } + + //INSERT ROOM DATA + foreach ($param['room'] as $roomOrder => $room) { + + $bookingRoomCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'room_order_number' => ($roomOrder + 1), + 'occupancy_code' => str_repeat('A', $room['totalpax']) . str_repeat('C', $room['totalchd']), + 'checkin_date' => $param['checkinFormatted'], //$room['checkin'], + 'checkout_date' => $param['checkoutFormatted'],//$room['checkout'], + 'rate_key' => null, + 'rate_key_code' => null, + 'availability_id' => 1, + 'availability_code' => 'ROM', + 'room_id' => $room['property']['room_id'], + 'room_name' => $room['roomtype'], + 'room_rate_mapping_id' => $room['property']['room_rate_mapping_id'], + 'room_rate_name' => $room['ratename'], + 'cancellation_policy' => null, + 'payment_type_code' => 'CHN', + 'rate_detail' => json_encode($room), + 'total' => $room['total_amount'], + 'currency_code' => $param['paymentcurr'], + 'status' => $param['status'] == 'A' ? 1 : 0 + ]; + + $bookingRoomCreate = $this->bookingRoomService->create($bookingRoomCreateParam); + //$bookingRoomCreate['status'] = 'success'; + + if ($bookingRoomCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Room could not be made')); + } + + + /* ROOM AVAILABILITY */ + $dateByDay = []; + $dateByDay = $this->getDateByDay(['checkIn' => $room['checkin'], 'checkOut' => $room['checkout']]); + foreach ($dateByDay as $day) { + + $requestParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property']['id']], + ['field' => 'property_room_id', 'condition' => '=', 'value' => $room['property']['room_id']], + ['field' => 'availability_type_id', 'condition' => '=', 'value' => 1], + ['field' => 'date', 'condition' => '=', 'value' => $day] + ], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->propertyRoomAvailabilityService->select($requestParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success') { + throw new ApiErrorException('getPropertyRoomAndRoomRateAvailability Empty'); + } + + foreach ($getPropertyRoomAndRoomRateAvailability['data'] as $roomAvailability) { + $roomAvailabilityUpdated = $roomAvailability['availability'] <= 0 ? 0 : ($roomAvailability['availability'] - 1); + $this->propertyRoomAvailabilityService->update($roomAvailability['id'], ['availability' => $roomAvailabilityUpdated]); + } + + } + + /* ROOM AVAILABILITY */ + + } + + //INSERT PAYMENT DATA + $bookingPaymentCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'payment_code' => (isset($param['cc_token']) && !empty($param['cc_token'])) ? fillOnUndefined($param, 'cc_token') : null, + 'payment_type_code' => 'CHN', + 'total' => $param['paymenttotal'], + 'currency_code' => $param['paymentcurr'], + 'status' => 2 + ]; + + $bookingPaymentCreate = $this->bookingPaymentService->create($bookingPaymentCreateParam); + //$bookingContactCreate['status'] = 'success'; + + if ($bookingPaymentCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Payment could not be made')); + } + //INSERT PAYMENT DATA + + //INSERT CHANNEL PAYMENT DATA + if (isset($param['cc_token']) && !empty($param['cc_token'])) { + + $bookingPaymentDataCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'type' => 'ch', + 'data' => md5($param['reservno'].$param['channelManagerPropertyId'].$param['cc_token']) + ]; + + $bookingPaymentDataCreate = $this->bookingPaymentService->createPaymentData($bookingPaymentDataCreateParam); + + if ($bookingPaymentDataCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Payment could not be made')); + } + } + //INSERT CHANNEL PAYMENT DATA + + + //$param['reservno'] = '27145472222'; + $reservationConfirmParam = $this->reservationConfirmParam($param['channelManagerPropertyId'], $param['reservno'], $bookingCode); + $reservationConfirm = $this->channelService->reservationConfirm($reservationConfirmParam); + + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + DB::commit(); + + $mailParams = ['booking_id' => $bookingCreate['data']['id']]; + $this->newBookingMailService->process($mailParams); + + $response['status'] = true; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + DB::rollBack(); + } + + return $response; + + } + + public function cancelBooking($param) + { + + $response = ['status' => false, 'message' => '']; + + DB::beginTransaction(); + + try { + + $bookingCode = null; + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property']['id']], + ['field' => 'search_key', 'condition' => '=', 'value' => $param['reservno']] + ], + 'with' => ['bookingRoom'], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success') { + throw new ApiErrorException(lang('Booking could not be found, Reservation: ' . $param['reservno'])); + } + + if (!empty($bookingDetail['data'])) { + + $bookingCode = $bookingDetail['data']['booking_code']; + + $this->bookingService->update($bookingDetail['data']['id'], ['status' => 0]); + + foreach ($bookingDetail['data']['booking_room'] as $roomOrder => $room) { + + $bookingRoomCreate = $this->bookingRoomService->update($room['id'], ['status' => 0]); + + + /* ROOM AVAILABILITY */ + $dateByDay = []; + $dateByDay = $this->getDateByDay(['checkIn' => $room['checkin_date'], 'checkOut' => $room['checkout_date']]); + foreach ($dateByDay as $day) { + + $requestParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property']['id']], + ['field' => 'property_room_id', 'condition' => '=', 'value' => $room['room_id']], + ['field' => 'availability_type_id', 'condition' => '=', 'value' => 1], + ['field' => 'date', 'condition' => '=', 'value' => $day] + ], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->propertyRoomAvailabilityService->select($requestParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success') { + throw new ApiErrorException('getPropertyRoomAndRoomRateAvailability Empty'); + } + + foreach ($getPropertyRoomAndRoomRateAvailability['data'] as $roomAvailability) { + $roomAvailabilityUpdated = $roomAvailability['availability'] <= 0 ? 1 : ($roomAvailability['availability'] + 1); + $this->propertyRoomAvailabilityService->update($roomAvailability['id'], ['availability' => $roomAvailabilityUpdated]); + } + + } + + /* ROOM AVAILABILITY */ + + } + + } + + $response['status'] = true; + + $bookingCode = is_null($bookingCode) ? 'ENW' . $param['reservno'] : $bookingCode; + + $reservationConfirmParam = $this->reservationConfirmParam($param['channelManagerPropertyId'], $param['reservno'], $bookingCode); + $reservationConfirm = $this->channelService->reservationConfirm($reservationConfirmParam); + + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + $notificationCancelToPropertyUser = [ + 'channel' => $param['otaname'], + 'channelBookingCode' => $param['reservno_ota'], + 'propertyChannelManager' => $param['otaname'],//$channelManagerMapping['property_channel_manager']['name'], + 'nameSurname' => $param['firstname'] . ' ' . $param['lastname'], + 'propertyId' => $param['property']['id'], + ]; + + $this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($notificationCancelToPropertyUser)); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if ($response['status']) { + + DB::commit(); + + } else { + DB::rollBack(); + } + + return $response; + + } + + public function modifiedBooking($param) + { + + $response = ['status' => false, 'message' => '']; + + DB::beginTransaction(); + + try { + + $bookingCode = null; + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property']['id']], + ['field' => 'search_key', 'condition' => '=', 'value' => $param['reservno']] + ], + 'with' => ['bookingRoom', 'bookingContact'], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success') { + throw new ApiErrorException(lang('Booking could not be found, Reservation: ' . $param['reservno'])); + } + + if (!empty($bookingDetail['data'])) { + + $bookingCode = $bookingDetail['data']['booking_code']; + + $bookingUpdateParam = [ + 'channel_booking_code' => fillOnUndefined($param, 'reservno_ota'), + 'search_key' => $param['reservno'], + 'checkin_date' => $param['checkinFormatted'], + 'checkout_date' => $param['checkoutFormatted'], + 'payment_type_code' => 'CHN', + 'total' => $param['paymenttotal'], + 'currency_code' => $param['paymentcurr'], + ]; + + $bookingUpdate = $this->bookingService->update($bookingDetail['data']['id'], $bookingUpdateParam); + + $bookingContactUpdateParam = [ + 'name' => $param['firstname'], + 'surname' => $param['lastname'], + 'phone_code' => null, + 'phone_number' => $param['tel'], + 'email' => $param['email'], + 'note' => !empty($param['note']) ? $param['note'] : null, + 'language_code' => fillOnUndefined($param, 'language', 'en') + ]; + + $bookingContactUpdate = $this->bookingContactService->update($bookingDetail['data']['booking_contact']['id'], $bookingContactUpdateParam); + + foreach ($bookingDetail['data']['booking_room'] as $roomOrder => $room) { + + foreach ($param['room'] as $roomOrderChannel => $roomChannel) { + + if ($roomOrder == $roomOrderChannel) { + + + $bookingRoomUpdateParam = [ + 'occupancy_code' => str_repeat('A', $roomChannel['totalpax']) . str_repeat('C', $roomChannel['totalchd']), + 'checkin_date' => $roomChannel['checkin'], + 'checkout_date' => $roomChannel['checkout'], + 'room_id' => $roomChannel['property']['room_id'], + 'room_name' => $roomChannel['roomtype'], + 'room_rate_mapping_id' => $roomChannel['property']['room_rate_mapping_id'], + 'room_rate_name' => $roomChannel['ratename'], + 'rate_detail' => json_encode($roomChannel), + 'total' => $roomChannel['total_amount'], + 'currency_code' => $param['paymentcurr'], + ]; + + $bookingRoomUpdate = $this->bookingRoomService->update($room['id'], $bookingRoomUpdateParam); + + } + } + + } + + } + + $response['status'] = true; + + $bookingCode = is_null($bookingCode) ? 'ENW' . $param['reservno'] : $bookingCode; + + $reservationConfirmParam = $this->reservationConfirmParam($param['channelManagerPropertyId'], $param['reservno'], $bookingCode); + $reservationConfirm = $this->channelService->reservationConfirm($reservationConfirmParam); + + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + $notificationModifiedToPropertyUser = [ + 'channel' => $param['otaname'], + 'channelBookingCode' => $param['reservno_ota'], + 'propertyChannelManager' => $param['otaname'],//$channelManagerMapping['property_channel_manager']['name'], + 'nameSurname' => $param['firstname'] . ' ' . $param['lastname'], + 'propertyId' => $param['property']['id'], + ]; + + $this->mailer->onQueue('modifiedBookingMail', new ModifiedBookingMail($notificationModifiedToPropertyUser)); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if ($response['status']) { + + DB::commit(); + + } else { + DB::rollBack(); + } + + return $response; + + } + + public function handle() + { + //$request = $this->channelService->productList(14480); + + try { + + /* + 1. Reseliva mapping olan oteller çekilecek + 2. Her otel için foreach ile reservaion servisi çağrılacak + 3. Gelen bilgiler enw ye işlenecek, sonra da confirmcheck yapılacak + * */ + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + $channelManagerMappingCriteria = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyChannelManager'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManagerMappingData = $this->channelManagerMappingService->select($channelManagerMappingCriteria); + $channelManagerMappingCollect = collect($channelManagerMappingData['data']); + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => 1], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + //['field' => 'property_id', 'condition' => '=', 'value' => 326],//TODO: Delete + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success') { + + foreach ($channelManagerPropertyMapping['data'] as $propertyMapping) { + + $channelManagerRoomRateCollect = collect($propertyMapping['channel_manager_room_rate']); + + $reservationListParam = $this->reservationListParam($propertyMapping['channel_manager_property_id']); + $reservationList = $this->channelService->reservationList($reservationListParam); + + if (!$reservationList['status']) { + + //$logMessage + $mailParams = [ + 'title' => 'ReservationPullService Error - ReservationList Error', + 'logMessage' => '
' . $reservationListParam . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + throw new ApiErrorException('ReservationList Error: ' . $reservationList['message']); + } + + foreach ($reservationList['data'] as $reservationKey => $reservation) { + + //Check Channel Manager Mapping + $channelManagerMapping = $channelManagerMappingCollect->where('channel_manager_channel_id', $reservation['otaname'])->first(); + + //Eğer ExtranetWork ise içeri almadan onayla + if ($reservation['otaname'] == 'ExtranetWork') { + + $reservationConfirmParam = $this->reservationConfirmParam($propertyMapping['channel_manager_property_id'], $reservation['reservno'], 'ENWRES' . $reservation['reservno']); + //$reservationConfirm = $this->channelService->reservationConfirm($reservationConfirmParam); + $reservationConfirm['status'] = true; + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + continue; + } + + if (empty($channelManagerMapping)) { + //Eğer mapping yapılmış kanal bulunamamış ise rezervasyonu içeri alamazsın + //$logMessage + $mailParams = [ + 'title' => 'ReservationPullService Error - Channel Manager Mapping', + 'logMessage' => '
' . print_r(array_merge($propertyMapping, $reservation), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + unset($reservationList['data'][$reservationKey]); + continue; + } + + $reservationList['data'][$reservationKey]['property']['id'] = $propertyMapping['property_id']; + $reservationList['data'][$reservationKey]['property']['channel_id'] = $channelManagerMapping['property_channel_id']; + + foreach ($reservation['room'] as $roomKey => $room) { + + //Check Room Rate Mapping + $channelManagerRoomRate = $channelManagerRoomRateCollect->where('channel_manager_room_id', $room['roomid'])->where('channel_manager_room_rate_id', $room['rateid'])->first(); + if (empty($channelManagerRoomRate)) { + $channelManagerRoomRate = $channelManagerRoomRateCollect->where('channel_manager_room_id', $room['roomid'])->first(); + } + + $reservationList['data'][$reservationKey]['room'][$roomKey]['property']['room_id'] = null; + $reservationList['data'][$reservationKey]['room'][$roomKey]['property']['room_rate_id'] = null; + $reservationList['data'][$reservationKey]['room'][$roomKey]['property']['room_rate_mapping_id'] = null; + $reservationList['data'][$reservationKey]['channelManagerPropertyId'] = $propertyMapping['channel_manager_property_id']; + + if (empty($channelManagerRoomRate)) { + //Eğer mapping yapılmış oda bulunamamış ise rezervasyonu içeri alamazsın + //$logMessage + $mailParams = [ + 'title' => 'ReservationPullService Error - Channel Manager Room Rate Mapping', + 'logMessage' => '
' . print_r(array_merge($propertyMapping, $reservation), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + unset($reservationList['data'][$reservationKey]); + $this->error(date('Y-m-d H:i:s') . ' : Error: RoomRate Mapping Reservation: ' . $reservation['reservno']); + + + //Eşleşmeyen ve iptal edilen işlemin otele bildirilmesi. + if ($reservation['status'] == 'C') { + + $notificationCancelToPropertyUser = [ + 'channel' => $reservation['otaname'], + 'channelBookingCode' => $reservation['reservno_ota'], + 'propertyChannelManager' => $channelManagerMapping['property_channel_manager']['name'], + 'nameSurname' => $reservation['firstname'] . ' ' . $reservation['lastname'], + 'propertyId' => $propertyMapping['property_id'], + ]; + + $this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($notificationCancelToPropertyUser)); + + $reservationConfirmParam = $this->reservationConfirmParam($propertyMapping['channel_manager_property_id'], $reservation['reservno'], 'ENWRES' . $reservation['reservno']); + $reservationConfirm = $this->channelService->reservationConfirm($reservationConfirmParam); + + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + } + + } else { + $reservationList['data'][$reservationKey]['room'][$roomKey]['property']['room_id'] = $channelManagerRoomRate['property_room_rate_mapping']['room_id']; + $reservationList['data'][$reservationKey]['room'][$roomKey]['property']['room_rate_id'] = $channelManagerRoomRate['property_room_rate_mapping']['room_rate_id']; + $reservationList['data'][$reservationKey]['room'][$roomKey]['property']['room_rate_mapping_id'] = $channelManagerRoomRate['property_room_rate_mapping']['id']; + } + + } + + } + + foreach ($reservationList['data'] as $reservationKey => $reservation) { + + //Eğer ExtranetWork ise içeri almadan onayla + if ($reservation['otaname'] == 'ExtranetWork') { + + $reservationConfirmParam = $this->reservationConfirmParam($propertyMapping['channel_manager_property_id'], $reservation['reservno'], 'ENWRES' . $reservation['reservno']); + $reservationConfirm = $this->channelService->reservationConfirm($reservationConfirmParam); + + if (!$reservationConfirm['status']) { + throw new ApiErrorException($reservationConfirm['message']); + } + + continue; + } + + $booking = ['status' => false, 'message' => '']; + + if ($reservation['status'] == 'A') { + $booking = $this->createBooking($reservation); + } elseif ($reservation['status'] == 'C') { + $booking = $this->cancelBooking($reservation); + } elseif ($reservation['status'] == 'M') { + $booking = $this->modifiedBooking($reservation); + } + + //M Modified kayıt bul güncelle ve sonra mail atılacak cancel ile aynı mail yapısı kullanılacak + + if ($booking['status']) { + $this->info(date('Y-m-d H:i:s') . ' : Success: ' . $reservation['reservno']); + } else { + + //$logMessage + $mailParams = [ + 'title' => 'ReservationPullService Error - Booking', + 'logMessage' => '
' . print_r(array_merge($booking, $reservation), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $booking['message']); + } + + + } + + } + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch + (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/ChannelManager/_ReservationPushService.php b/app/Console/Commands/ChannelManager/_ReservationPushService.php new file mode 100644 index 0000000..edfdc80 --- /dev/null +++ b/app/Console/Commands/ChannelManager/_ReservationPushService.php @@ -0,0 +1,281 @@ +mailer = $mailer; + $this->channelService = $channelService; + $this->channelManagerBookingService = $channelManagerBookingService; + + $this->typeMapping = [ + 'Booking' => 'Book', + 'Modify' => 'Modify', + 'Cancel' => 'Cancel' + ]; + + } + + public function handle() + { + + try { + + $this->info(date('Y-m-d H:i:s') . ' : Start'); + + $pendingBookingCriteria = [ + 'criteria' => + [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => 1], + ['field' => 'is_pushed', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['bookingDetail.bookingContact', 'bookingDetail.bookingChannel', 'bookingDetail.bookingPayment', 'bookingDetail.bookingPaymentType', 'bookingDetail.bookingStatus', 'bookingDetail.bookingRoom.roomPax.paxCountry'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + 'take' => 100 + ]; + + $pendingBooking = $this->channelManagerBookingService->select($pendingBookingCriteria); + + if ($pendingBooking['status'] == 'success') { + + $pendingBooking = $pendingBooking['data']; + + foreach ($pendingBooking as $booking) { + + + $bookingDetail = $booking['booking_detail']; + + $type = $this->typeMapping[$booking['type']]; + + $serviceRequestName = 'BookingRetrieval'; + $xmlResponse = new \SimpleXMLElement('<' . $serviceRequestName . 'RS>'); + + if (in_array($bookingDetail['status'], [0, 3])) { + + $Bookings = $xmlResponse->addChild('Bookings'); + $Booking = $Bookings->addChild('Booking'); + + if(Carbon::createFromTimestamp($booking['booking_detail']['created_at'])->lessThan(Carbon::parse('2022-04-01'))) { + $Booking->addAttribute('id', $bookingDetail['id']); + } else { + $Booking->addAttribute('id', $bookingDetail['booking_code']); + } + + $Booking->addAttribute('type', 'Cancel'); + $Booking->addAttribute('cancelDateTime', Carbon::createFromTimestamp($bookingDetail['updated_at'])->toISOString()); + + $Hotel = $Booking->addChild('Hotel'); + $Hotel->addAttribute('id', $bookingDetail['property_id']); + + foreach ($bookingDetail['booking_room'] as $room) { + + $RoomStay = $Booking->addChild('RoomStay'); + $RoomStay->addAttribute('roomTypeID', $room['room_id']); + $RoomStay->addAttribute('ratePlanID', $room['room_rate_mapping_id']); + + $StayDate = $RoomStay->addChild('StayDate'); + $StayDate->addAttribute('arrival', $room['checkin_date']); + $StayDate->addAttribute('departure', $room['checkout_date']); + + } + + } else { + + + $Bookings = $xmlResponse->addChild('Bookings'); + $Booking = $Bookings->addChild('Booking'); + + if(Carbon::createFromTimestamp($booking['booking_detail']['created_at'])->lessThan(Carbon::parse('2022-04-01'))) { + $Booking->addAttribute('id', $bookingDetail['id']); + } else { + $Booking->addAttribute('id', $bookingDetail['booking_code']); + } + + //Book ve Modify Aynı olacak, Cancel + if ($type == 'Book') { + $Booking->addAttribute('type', 'Book'); + $Booking->addAttribute('createDateTime', Carbon::createFromTimestamp($bookingDetail['created_at'])->toISOString()); + } + + if ($type == 'Modify') { + $Booking->addAttribute('type', 'Modify'); + $Booking->addAttribute('modifyDateTime', Carbon::createFromTimestamp($bookingDetail['updated_at'])->toISOString()); + } + + $Hotel = $Booking->addChild('Hotel'); + $Hotel->addAttribute('id', $bookingDetail['property_id']); + + + foreach ($bookingDetail['booking_room'] as $room) { + + $RoomStay = $Booking->addChild('RoomStay'); + $RoomStay->addAttribute('roomTypeID', $room['room_id']); + $RoomStay->addAttribute('ratePlanID', $room['room_rate_mapping_id']); + + $StayDate = $RoomStay->addChild('StayDate'); + $StayDate->addAttribute('arrival', $room['checkin_date']); + $StayDate->addAttribute('departure', $room['checkout_date']); + + + $roomPaxCollect = collect($room['room_pax']); + + $GuestCount = $RoomStay->addChild('GuestCount'); + $GuestCount->addAttribute('adult', $roomPaxCollect->where('type', 'ADT')->count()); + + $diffInDays = Carbon::parse($room['checkin_date'])->diffInDays(Carbon::parse($room['checkout_date'])); + + //dd($room['total'], $diffInDays, $room); + + $PerDayRates = $RoomStay->addChild('PerDayRates'); + $PerDayRates->addAttribute('currency', $room['currency_code']); + + $baseRateDaily = moneyDoubleFormatDecimal($room['total'] / $diffInDays); + + $currentDate = $room['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $PerDayRate = $PerDayRates->addChild('PerDayRate'); + $PerDayRate->addAttribute('stayDate', $currentDate); + $PerDayRate->addAttribute('baseRate', $baseRateDaily); + $PerDayRate->addAttribute('hotelServiceFees', 0); + + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + + $Total = $RoomStay->addChild('Total'); + $Total->addAttribute('amountAfterTaxes', $room['total']); + $Total->addAttribute('amountOfTaxes', 0); + $Total->addAttribute('currency', $room['currency_code']); + + $roomNotes['cancellationPolicy'] = 'Cancellation Policy: Refundable'; + $cancellationPolicy = json_decode($room['cancellation_policy'],1); + if(!empty($cancellationPolicy) && is_array($cancellationPolicy)) { + if(isset($cancellationPolicy['isNonRefundable']) && $cancellationPolicy['isNonRefundable']) { + $roomNotes['cancellationPolicy'] = 'Cancellation Policy: NonRefundable'; + } + } + + $roomNotes['payment'] = 'Payment: '.$bookingDetail['booking_payment_type']['name']; + + $RoomStay->addChild('RoomNotes', implode('. ',$roomNotes)); + + } + + + $PrimaryGuest = $Booking->addChild('PrimaryGuest'); + + $Name = $PrimaryGuest->addChild('Name'); + $Name->addAttribute('givenName', $bookingDetail['booking_contact']['name']); + $Name->addAttribute('surname', $bookingDetail['booking_contact']['surname']); + + $Phone = $PrimaryGuest->addChild('Phone'); + $Phone->addAttribute('countryCode', $bookingDetail['booking_contact']['phone_code']); + //$Phone->addAttribute('cityAreaCode'); + $Phone->addAttribute('number', $bookingDetail['booking_contact']['phone_number']); + + $PrimaryGuest->addChild('Email', $bookingDetail['booking_contact']['email']); + + + $Total = $Booking->addChild('Total'); + $Total->addAttribute('amountAfterTaxes', $bookingDetail['total']); + $Total->addAttribute('amountOfTaxes', 0); + $Total->addAttribute('currency', $bookingDetail['currency_code']); + + + $Booking->addChild('SpecialRequest'); + $Booking->addChild('BookingNotes', $bookingDetail['booking_contact']['note']); + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Booking Id: '. $booking['booking_id']. ' Push'); + + + $xmlPayload = $xmlResponse->asXML(); + + $this->channelManagerBookingService->update($booking['id'], ['request' => $xmlPayload]); + + $requestPostReservationPush = $this->channelService->requestPostReservationPush($xmlPayload); + + $this->channelManagerBookingService->update($booking['id'], ['response' => $requestPostReservationPush['data']['xmlBase']]); + + + if ($requestPostReservationPush['status']) { + + if($requestPostReservationPush['data']['Status'] == 'success') { + $this->info(date('Y-m-d H:i:s') . ' : Booking Id: '. $booking['booking_id']. ' Success'); + $this->channelManagerBookingService->update($booking['id'], ['is_pushed' => 1]); + } else { + + //$logMessage + $mailParams = [ + 'title' => 'ReservationPushService Error - requestPostReservationPush Error', + 'logMessage' => '
' . print_r(array_merge($booking, $requestPostReservationPush), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + } + + + } else { + + //$logMessage + $mailParams = [ + 'title' => 'ReservationPushService Error - requestPostReservationPush Error', + 'logMessage' => '
' . print_r(array_merge($booking, $requestPostReservationPush), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $this->info(date('Y-m-d H:i:s') . ' : Booking Id: '. $booking['booking_id']. ' Failed'); + } + + } + + } + + + $this->info(date('Y-m-d H:i:s') . ' : Finished'); + + } catch (ApiErrorException $e) { + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/ChannelManager/akgun.xlsx b/app/Console/Commands/ChannelManager/akgun.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..97e5cf6292b28e646fccc9317b540c0b7b05ce0d GIT binary patch literal 53811 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

xj; zY2A$>x4Eon^H4bKy1n!LN_@kNv)`5)Qsel&g7TEx;tr`}%#_%FMtj>ymjbU6#3UJx zx=GhNNklc9-A!3-Mv=XrwOe#7Comg+$ra04okH^gttG)AZI;QI$|x*wC;l6Wh?FD) zH;WLl2y6Mo)UivrZF;J;!B4xpYBog|0mfe!g{^nnek8Ze`Kq*c3KX&wUpe2}AnduC z29jK1|F@kuz>15L2y8PjG6DkLKX;-z1oHoH#D6}wK6BLw3O+X0!uTvo;k~3^P0V6zY&Ll-e5)q-Ck27-#rT`j++60;a^t;PV$0et z5)0w4vf)2MC$HRTb%(wmkI^ZC+Ce0u#1^p%Mq72buc@R%;@LjH*!@^~!%NZ?(uYy) zUl62-vFb$f;T6AOm)6k_5$JNTKVjfJx=HM%%;&5TbvD83gA3Yk1*#003Aej-EDB&% z%%pyJGmG$>{nXRm;zfSJ?#^hQmJ1up2gAI1IVIn0Rw31y?w}rf{#l~tP(>=tZJd0= z1qugcgNA4J6Y7!rwa>jBN$7loLMc^g4I+s}xplwsGx+aj$Y|VOqU>L89UPF?qU?G< z>fK;^^2u}j8sg_b@m)!5@3Eku<1Q8|BeAMoY6`^%>KO{UHst4&J?B`+S3$_JJC%VU z-%NAR+G_7!-C7#(4MC7YBiM6@XDpEMl}v6VpA%10a&ond{`5f(i2jwoQ90grd3#uM zdg780ID9E2&-WzOR1b)ze}_~=BzA;<2UUyxY+G|+E60Hl5D5Oga(}-vly-Jc|>E_O~fuK#WR{mM@kQh+a<|M-6!aDw5_ z2yV94j@F!iesce}0fsA0w++z|5VjQ&5P)d&k4GBoUtht^+rj$3el}k1=f$%m1caB$ z$OxqWI1mAWitm4>?rLjo?dHn)=jp$Gf&a{_{~Shf5CP%;m1Y083I8(~{`2tZ;eQcurrencyRatesRepository = $currencyRatesRepository; + $this->serviceUrl = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'; + $this->currencyService = $currencyService; + } + + public function handle() + { + + $currencyList = $this->currencyService->getCurrencyList(); + + if (!$currencyList['status']) { + $this->error(date('Y-m-d H:i:s') . ' : CurrencyList alınamadı.'); + } + + $availableCurrencies = []; + foreach ($currencyList['data'] as $currency) { + $availableCurrencies[] = $currency['code']; + } + $availableCurrency = $availableCurrencies; + + $this->restClient = new Client(['allow_redirects' => false]); + $request = $this->restClient->GET($this->serviceUrl); + $requestResponse = $request->getBody(); + + $currencyRatesData = json_decode(json_encode(simplexml_load_string($requestResponse)), 1); + + if (empty($currencyRatesData)) { + $this->error(date('Y-m-d H:i:s') . ' : CurrencyRatesData alınamadı.'); + + return false; + } + + //Georgia GEL + $tbcBankApiUrl = 'https://nbg.gov.ge/gw/api/ct/monetarypolicy/currencies/en/json/'; + $requestTbcBank = $this->restClient->GET($tbcBankApiUrl); + + $requestResponseTbcBank = $requestTbcBank->getBody()->getContents(); + $currencyRatesDataTbcBank = json_decode($requestResponseTbcBank, 1); + + if (empty($currencyRatesDataTbcBank)) { + $this->error(date('Y-m-d H:i:s') . ' : GEL CurrencyRatesData alınamadı.'); + } + + $currencyRatesDataTbcBank = reset($currencyRatesDataTbcBank); + $currencyRatesDataTbcBank = collect($currencyRatesDataTbcBank['currencies'])->where('code','EUR')->first(); + + $currencyRatesData['Cube']['Cube']['Cube'][] = [ + '@attributes' => [ + 'currency' => 'GEL', + 'rate' => $currencyRatesDataTbcBank['rate'] + ] + ]; + //Georgia GEL + + //Morocco MAD + $euroApiUrl = 'https://www.floatrates.com/daily/eur.xml'; + $requestEuroApiUrl = $this->restClient->GET($euroApiUrl); + $responseEuroApi = $requestEuroApiUrl->getBody(); + + $currencyRatesDataEuroApi = json_decode(json_encode(simplexml_load_string($responseEuroApi)), 1); + + if (empty($currencyRatesDataEuroApi)) { + $this->error(date('Y-m-d H:i:s') . ' : EUR CurrencyRatesData alınamadı.'); + } + + $currencyRatesDataEuroApiSelected = collect($currencyRatesDataEuroApi['item'])->where('targetCurrency','MAD')->first(); + + $currencyRatesData['Cube']['Cube']['Cube'][] = [ + '@attributes' => [ + 'currency' => 'MAD', + 'rate' => (float)number_format($currencyRatesDataEuroApiSelected['exchangeRate'],4) + ] + ]; + //Morocco MAD + + $currencyRatesDataEuroApiSelected = collect($currencyRatesDataEuroApi['item'])->where('targetCurrency','AZN')->first(); + + $currencyRatesData['Cube']['Cube']['Cube'][] = [ + '@attributes' => [ + 'currency' => 'AZN', + 'rate' => (float)number_format($currencyRatesDataEuroApiSelected['exchangeRate'],4) + ] + ]; + //Azerbaijani AZN + + $date = $currencyRatesData['Cube']['Cube']['@attributes']['time']; + + $currencyRatesCriteria = [ + 'criteria' => [ + ['field' => 'date', 'condition' => '=', 'value' => $date], + ], + 'count' => true, + ]; + + $currencyRateCount = $this->currencyRatesRepository->findByCriteria($currencyRatesCriteria); + + + if ($currencyRateCount > 0) { + $this->error(date('Y-m-d H:i:s') . ' : ' . $date . ' tarihi için döviz bilgisi güncel.'); + + return false; + } + + $euroParity[] = [ + 'currency_code' => 'EUR', + 'exc_currency_code' => 'EUR', + 'rate' => 1 + ]; + foreach ($currencyRatesData['Cube']['Cube']['Cube'] as $perEuroParity) { + if (in_array($perEuroParity['@attributes']['currency'], $availableCurrency)) { + $euroParity[] = [ + 'currency_code' => 'EUR', + 'exc_currency_code' => $perEuroParity['@attributes']['currency'], + 'rate' => floatval($perEuroParity['@attributes']['rate']), + ]; + } + } + + $exchangeParity = []; + foreach ($euroParity as $perParity) { + foreach ($euroParity as $perParityExchange) { + $exchangeParity[$perParity['exc_currency_code'] . $perParityExchange['exc_currency_code']] = [ + 'currency_code' => $perParity['exc_currency_code'], + 'exc_currency_code' => $perParityExchange['exc_currency_code'], + 'rate' => $perParityExchange['rate'] / $perParity['rate'] + ]; + + + $exchangeParity[$perParityExchange['exc_currency_code'] . $perParity['exc_currency_code']] = [ + 'currency_code' => $perParityExchange['exc_currency_code'], + 'exc_currency_code' => $perParity['exc_currency_code'], + 'rate' => $perParity['rate'] / $perParityExchange['rate'] + ]; + } + } + + ksort($exchangeParity); + + foreach ($exchangeParity as $perExchangeParity) { + + + $data[] = [ + 'date' => $date, + 'currency_code' => $perExchangeParity['currency_code'], + 'exc_currency_code' => $perExchangeParity['exc_currency_code'], + 'rate' => $perExchangeParity['rate'], + 'created_at' => Carbon::now()->timestamp + ]; + } + + $currencyRateUpdate = $this->currencyRatesRepository->insert($data); + + if ($currencyRateUpdate['status'] == 'success') { + $this->info(date('Y-m-d H:i:s') . ' : ' . $date . ' tarihi için ' . count($exchangeParity) . ' adet döviz bilgisi güncellendi.'); + } + + + } +} diff --git a/app/Console/Commands/Data/DashboardCacheService.php b/app/Console/Commands/Data/DashboardCacheService.php new file mode 100644 index 0000000..03e5933 --- /dev/null +++ b/app/Console/Commands/Data/DashboardCacheService.php @@ -0,0 +1,149 @@ +restClient = $restClient; + } + + + public function handle() + { + try { + + + $this->info(date('Y-m-d H:i:s') . ' : START'); + + $bookingService = App::make("App\Core\Service\BookingService"); + $propertyWebController = App::make("App\Http\Controllers\V1\PropertyWebController"); + + $vwActiveProperty = vwActiveProperty::where('commission', '>', 1)->orderBy('id')->get()->toArray(); + + + //$dashboardCountablePlaceCacheKey + $this->info(date('Y-m-d H:i:s') . ' : $dashboardCountablePlaceCacheKey'); + foreach ($vwActiveProperty as $property) { + + + //$dashboardCountablePlaceCacheKey + $dashboardCountablePlaceCacheKey = md5('dashboardCountablePlace-' . $property['id']); + + try { + + $this->line(date('Y-m-d H:i:s') . ' : ' . $property['id'] . ' - ' . $property['name']); + + $propertyWeb = PropertyWeb::where('property_id', $property['id'])->where('status', 1)->orderByDesc('id')->first(); + $propertyWeb = $propertyWeb ? $propertyWeb->toArray() : null; + if (empty($propertyWeb)) { + throw new Exception('Empty Web ID'); + } + + $dashboardCountablePlaceParam = ['property_id' => $property['id']]; + $dashboardCountablePlace = $propertyWebController->dashboardCountablePlace($dashboardCountablePlaceParam, $propertyWeb['id']); + + if ($dashboardCountablePlace['status']) { + Cache::put($dashboardCountablePlaceCacheKey, $dashboardCountablePlace, 24 * 60 * 60);//1 Day + } + + + } catch (Exception $e) { + $this->error(date('Y-m-d H:i:s') . ' : ' . $e->getMessage()); + } + + } + + + //$getBookingDetailedListCacheKey + $this->info(date('Y-m-d H:i:s') . ' : GetBookingDetailedListCacheKey'); + foreach ($vwActiveProperty as $property) { + $getBookingDetailedListCacheKey = md5('getBookingDetailedList-' . $property['id']); + + try { + + $this->line(date('Y-m-d H:i:s') . ' : ' . $property['id'] . ' - ' . $property['name']); + + $getBookingDetailedListParam = ['property_id' => $property['id'], 'channel_id' => 1]; + $getBookingDetailedList = $bookingService->getBookingDetailedList($getBookingDetailedListParam); + + if ($getBookingDetailedList['status'] == 'success') { + + + $bookings = collect($getBookingDetailedList['data']); + $channelBookings = $bookings->where('channel_id', '=', 1); + $getBookingEngineBookings = $channelBookings->all(); + $paxCountArray = $channelBookings->where('status', '=', 1)->map(function ($booking) { + $roomPaxCount = collect($booking['booking_room'])->map(function ($room) { + return collect($room['room_pax'])->count(); + })->values()->first(); + return [ + 'booking_id' => $booking['id'], + 'room_pax_count' => $roomPaxCount, + ]; + + })->values()->all(); + + $totalPaxCount = collect($paxCountArray)->sum('room_pax_count'); + + $allBookingCount = $channelBookings->count(); + $preBookingCount = $channelBookings->where('status', '=', 2)->count(); + $successBookingCount = $channelBookings->where('status', '=', 1)->count(); + + $conversionRate = $allBookingCount > 0 ? ($successBookingCount * 100) / $allBookingCount : 0; + + $responseData['all_booking_count'] = $allBookingCount; + $responseData['success_booking_count'] = $successBookingCount; + $responseData['pre_booking_count'] = $preBookingCount; + $responseData['conversion_rate'] = number_format($conversionRate, 2); + $responseData['total_pax_count'] = $totalPaxCount; + + Cache::put($getBookingDetailedListCacheKey, $responseData, 24 * 60 * 60);//1 Day + + + } + + + } catch (Exception $e) { + $this->error(date('Y-m-d H:i:s') . ' : ' . $e->getMessage()); + } + //$getBookingDetailedListCacheKey + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : FINISHED'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/Data/HotelBedsList.php b/app/Console/Commands/Data/HotelBedsList.php new file mode 100644 index 0000000..ea0dd18 --- /dev/null +++ b/app/Console/Commands/Data/HotelBedsList.php @@ -0,0 +1,128 @@ +info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' START'); + + $baseUrl = 'https://api.hotelbeds.com'; + $apiKey = 'ddaa6y6zpf6sy8fne4n2zj7q'; + $secret = 'wc87VEfTSD'; + + $countryCode = 'TR'; + $language = 'TUR'; + $destinationCode = 'AYT'; + + $fields = ['code', 'name', 'email', 'countryCode', 'destinationCode', 'city','phones']; + + + $chunk = 500; + + $from = 1; + $to = $chunk; + + $isContinue = true; + while ($isContinue) { + + $xSignature = hash("sha256", $apiKey . $secret . time()); + $urlGenerate = $baseUrl . '/hotel-content-api/1.0/hotels?fields=' . implode(',', $fields) . '&countryCode=' . $countryCode . '&language=' . $language . '&from=' . $from . '&to=' . $to.'&destinationCode='.$destinationCode; + + $this->restClient = new Client(['allow_redirects' => false]); + + $request = $this->restClient->GET($urlGenerate, [ + 'headers' => [ + 'content-type' => 'application/json', + 'Api-key' => $apiKey, + 'X-Signature' => $xSignature, + ], + 'body' => null, + ]); + Log::debug($xSignature); + $requestResponse = $request->getBody(); + $requestResponse = json_decode($requestResponse, 1); + + $this->info(date('Y-m-d H:i:s') . ' : ' . $urlGenerate); + + $from += $chunk; + $to = $from + $chunk - 1; + + if (empty($requestResponse['hotels'])) { + $isContinue = false; + } + + foreach ($requestResponse['hotels'] as $hotel) { + + if(!isset($hotel['email'])) { + continue; + } + + $email = mb_strtolower($hotel['email']); + + $hotelList[$email] = [ + 'code' => $hotel['code'], + 'content' => (string)uCase($hotel['name']['content']), + 'countryCode' => $hotel['countryCode'], + 'email' => $email + ]; + + foreach ($hotel['phones'] as $phone) { + if(in_array($phone['phoneType'], ['PHONEHOTEL','PHONEMANAGEMENT'])) { + $hotelList[$email]['phone'] = $phone['phoneNumber']; + break; + } + } + } + } + + + + // Open a file in write mode ('w') + $fileName = $countryCode.(!empty($destinationCode) ? '-'.$destinationCode : ''); + $fp = fopen(resource_path().'/data/'.$fileName.'.csv', 'w'); + + // Loop through file pointer and a line + foreach ($hotelList as $hotel) { + fputcsv($fp, $hotel); + } + + fclose($fp); + + $this->info(date('Y-m-d H:i:s') . ' : Total: ' . count($hotelList).' / '. $requestResponse['total']); + + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' FINISHED'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/Data/WeatherService.php b/app/Console/Commands/Data/WeatherService.php new file mode 100644 index 0000000..bbc26bd --- /dev/null +++ b/app/Console/Commands/Data/WeatherService.php @@ -0,0 +1,130 @@ +restClient = $restClient; + } + + + public function handle() + { + try { + + + $date = date('Y-m-d'); + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' START'); + + $baseUrl = 'https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline'; + $apiKey = 'GQG8FVH2C7ZZYZXS6PECE2QFX'; + + $this->restClient = new Client(['http_errors' => false]); + + $propertyWeb = PropertyWeb::where('status', 1) + ->where('weather_active', 1)->with('property.propertyContact') + //->where('property_id',1231)//TODO:Del + ->get()->toArray(); + + foreach ($propertyWeb as $property) { + + + $propertyWebWeatherId = null; + $propertyWebWeather = PropertyWebWeather::where('property_id', $property['property']['id'])->first(); + if ($propertyWebWeather) { + $propertyWebWeatherId = $propertyWebWeather['id']; + } + + + $latitude = $property['property']['property_contact']['latitude']; + $longitude = $property['property']['property_contact']['longitude']; + if (!empty($latitude) && !empty($longitude)) { + + $requestUrl = $baseUrl . '/' . $latitude . ',' . $longitude . '/' . $date . '?key=' . $apiKey . '&include=days&unitGroup=metric&elements=tempmax,tempmin,temp,conditions,icon'; + + $requestWeather = $this->restClient->request('GET', $requestUrl); + + $getResponseBody = $requestWeather->getBody(); + $getResponse = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + $propertyWebWeatherParam = [ + 'property_id' => $property['property']['id'], + 'date' => $date, + 'temp' => $getResponse['days'][0]['tempmax'], + 'conditions' => $getResponse['days'][0]['conditions'], + 'icon' => $getResponse['days'][0]['icon'], + 'response' => json_encode($getResponse), + 'created_by' => 1, + 'updated_by' => 1, + ]; + + if (empty($propertyWebWeatherId)) { + + $locationUrl = 'https://geocode.maps.co/reverse?lat=' . $latitude . '&lon=' . $longitude . '&api_key=6683d9ee52e5d111344805khdf0451c'; + $requestLocation = $this->restClient->request('GET', $locationUrl); + + $getResponseBodyLocation = $requestLocation->getBody(); + $getResponseLocation = $getResponseBodyLocation ? json_decode($getResponseBodyLocation, 1) : []; + + + if ($getResponseLocation) { + + $location = null; + if(isset($getResponseLocation['address']['province'])) { + $location = $getResponseLocation['address']['province']; + }elseif(isset($getResponseLocation['address']['town'])) { + $location = $getResponseLocation['address']['town']; + }elseif(isset($getResponseLocation['address']['city'])) { + $location = $getResponseLocation['address']['city']; + } + + $propertyWebWeatherParam['location'] = $location; + $propertyWebWeatherParam['address'] = json_encode($getResponseLocation); + } + + PropertyWebWeather::create($propertyWebWeatherParam); + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyWebWeatherParam['property_id'] . ' - ' . $propertyWebWeatherParam['date'] . ' : ' . $propertyWebWeatherParam['conditions']. ' : ' . $propertyWebWeatherParam['temp']); + + } else { + + PropertyWebWeather::where('id', $propertyWebWeatherId)->update($propertyWebWeatherParam); + $this->line(date('Y-m-d H:i:s') . ' : ' . $propertyWebWeatherParam['property_id'] . ' - ' . $propertyWebWeatherParam['date'] . ' : ' . $propertyWebWeatherParam['conditions']. ' : ' . $propertyWebWeatherParam['temp']); + } + + + } + + } + + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' FINISHED'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/Google/GoogleReview.php b/app/Console/Commands/Google/GoogleReview.php new file mode 100644 index 0000000..28768f6 --- /dev/null +++ b/app/Console/Commands/Google/GoogleReview.php @@ -0,0 +1,140 @@ +restClient = $restClient; + } + + public function handle() + { + try { + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' START'); + + $baseUrl = 'https://maps.googleapis.com/maps/api/place/details/json'; + $apiKey = 'AIzaSyAPsKZ_XasAieKEhbmpOLUpArY2u2UcXuk'; + $availableRating = [4,5]; + + $this->restClient = new Client(['http_errors' => false]); + + + if (!is_null($this->option('property_id'))) { + $propertyWebComponentMapping = PropertyWebComponentMapping::where('status', 1)->where('component_id', 3)->where('property_id', $this->option('property_id'))->get()->toArray(); + } else { + $propertyWebComponentMapping = PropertyWebComponentMapping::where('status', 1)->where('component_id', 3)->get()->toArray(); + } + + foreach ($propertyWebComponentMapping as $propertyWebComponent) { + + if (!isset($propertyWebComponent['parameterArray']['placeId'])) { + continue; + } + + $googlePlaceId = trim($propertyWebComponent['parameterArray']['placeId']); + + Log::debug('google-review: '.$googlePlaceId); + + + $params = [ + 'query' => [ + 'place_id' => $googlePlaceId, + 'key' => $apiKey, + 'fields' => 'name,rating,reviews', + 'reviews_sort' => 'newest', + 'reviews_no_translations' => true, + //'language' => 'tr' + ] + ]; + + $requestReview = $this->restClient->request('GET', $baseUrl, $params); + + $getResponseBody = $requestReview->getBody(); + $getResponse = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + $reviews = []; + if(isset($getResponse['result']['reviews'])) { + $reviews = collect($getResponse['result']['reviews'])->sortBy('time')->toArray(); + } + + foreach ($reviews as $review) { + + if(!in_array(fillOnUndefined($review, 'rating'),$availableRating)) { + continue; + } + + try { + + $code = md5($propertyWebComponent['property_id'].'-'.$review['author_name'] . '-' . $review['time']); + + $propertyWebReviewCheck = PropertyWebReview::where('code', $code)->first(); + if ($propertyWebReviewCheck) { + $this->error(date('Y-m-d H:i:s') . ' : ' . $propertyWebComponent['property_id'] . ' - ' . $review['author_name'] . ' : ' . $review['rating']); + continue; + } + + $propertyWebWeatherParam = [ + 'property_id' => $propertyWebComponent['property_id'], + 'channel' => 'google', + 'code' => $code, + 'language_code' => fillOnUndefined($review, 'language') ? substr($review['language'],0,2) : null, + 'author' => $review['author_name'], + 'profile_photo' => fillOnUndefined($review, 'profile_photo_url'), + 'rating' => fillOnUndefined($review, 'rating'), + 'review' => fillOnUndefined($review, 'text'), + 'time' => fillOnUndefined($review, 'time'), + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + ]; + + $propertyWebReviewCreate = PropertyWebReview::create($propertyWebWeatherParam); + + $this->info(date('Y-m-d H:i:s') . ' : ' . $propertyWebComponent['property_id'] . ' - ' . $review['author_name'] . ' : ' . $review['rating']); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + + } + + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' FINISHED'); + + } + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/Google/GoogleStaticMap.php b/app/Console/Commands/Google/GoogleStaticMap.php new file mode 100644 index 0000000..6e8d0ae --- /dev/null +++ b/app/Console/Commands/Google/GoogleStaticMap.php @@ -0,0 +1,107 @@ +restClient = $restClient; + } + + public function handle() + { + try { + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' START'); + + $baseUrl = 'https://maps.googleapis.com/maps/api/staticmap'; + $apiKey = 'AIzaSyAPsKZ_XasAieKEhbmpOLUpArY2u2UcXuk'; + + $imageSizes = []; + $imageSizes[] = ['name' => 'square','size' => '250x250']; + $imageSizes[] = ['name' => 'rectangle','size' => '1000x200']; + + $this->restClient = new Client(['http_errors' => false]); + + if (!is_null($this->option('property_id'))) { + $propertyList = Property::where('status', 1)->where('id', $this->option('property_id'))->with('propertyContact')->get()->toArray(); + } else { + $propertyList = Property::where('status', 1)->where('commission','>=', 1)->with('propertyContact')->get()->toArray(); + } + + foreach ($propertyList as $property) { + + if (!isset($property['property_contact'])) { + continue; + } + + $folderPath = Config::get('app.fileSystemDriver') . '/property-map/' . $property['id']; + if(!is_dir($folderPath)) { + mkdir($folderPath); + } + + foreach ($imageSizes as $imageSize) { + + $imageName = 'map-'.$property['id'].'-'.$imageSize['name'].'.png'; + + $imagePath = Config::get('app.fileSystemDriver') . '/property-map/' . $property['id'] . '/'.$imageName; + + if(is_file($imagePath)) { + $this->line(date('Y-m-d H:i:s') . ' : ' . $imageName); + continue; + } + + $params = [ + 'center' => $property['property_contact']['latitude'] . ',' . $property['property_contact']['longitude'], + 'size' => $imageSize['size'], //300x300 1000x200 + 'zoom' => 18, + 'format' => 'png', + 'scale' => 2, + 'markers' => 'color:red|' . $property['property_contact']['latitude'] . ',' . $property['property_contact']['longitude'], + 'key' => $apiKey, + 'language' => 'en' + ]; + + $imageUrl = $baseUrl . '?' . http_build_query($params, '', '&'); + $imageSave = file_put_contents($imagePath, file_get_contents($imageUrl)); + + + $this->info(date('Y-m-d H:i:s') . ' : ' . $imageName); + + } + + } + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' FINISHED'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/Google/GoogleVisioLabel.php b/app/Console/Commands/Google/GoogleVisioLabel.php new file mode 100644 index 0000000..e70053d --- /dev/null +++ b/app/Console/Commands/Google/GoogleVisioLabel.php @@ -0,0 +1,72 @@ +info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' START'); + + $googleVisionAuthentication = base_path().'/resources/data/google-vision-authentication.json'; + + putenv('GOOGLE_APPLICATION_CREDENTIALS='.$googleVisionAuthentication); + $imageAnnotator = new ImageAnnotatorClient(); + + $fileName = 'https://image.rezervasyon.com/hotel/301356/lavin-otel-10-9566879.jpg'; + $fileName = 'https://image.rezervasyon.com/hotel/301356/lavin-otel-10-9566886.jpg'; + $fileName = 'https://image.rezervasyon.com/hotel/301356/lavin-otel-10-9566881.jpg'; + $fileName = 'https://image.rezervasyon.com/hotel/301356/lavin-otel-10-9566878.jpg'; + $image = file_get_contents($fileName); + $response = $imageAnnotator->labelDetection($image); + $labels = $response->getLabelAnnotations(); + + $labelList = []; + + if ($labels) { + foreach ($labels as $label) { + $labelList[] = [ + 'description' => $label->getDescription(), + 'score' => $label->getScore(), + 'topicality' => $label->getTopicality(), + ]; + } + } else { + echo('No label found' . PHP_EOL); + } + + $imageAnnotator->close(); + + print_r($labelList); + + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' FINISHED'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Commands/Jobs/BookingCommissionService.php b/app/Console/Commands/Jobs/BookingCommissionService.php new file mode 100644 index 0000000..8495352 --- /dev/null +++ b/app/Console/Commands/Jobs/BookingCommissionService.php @@ -0,0 +1,122 @@ +bookingService = $bookingService; + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' START'); + + try { + + + $bookingListParam = [ + 'criteria' => [ + //['field' => 'id', 'condition' => '=', 'value' => 600], + //['field' => 'property_id', 'condition' => '=', 'value' => 313], + ['field' => 'commission', 'condition' => '=', 'value' => null], + ['field' => 'commission_rate', 'condition' => '=', 'value' => null] + ], + 'with' => ['bookingChannel.propertyChannelCategory', 'bookingProperty'], + "take" => 10000, + "orderBy" => [ + ["field" => "id", "value" => "ASC"] + ], + ]; + + $bookingList = $this->bookingService->select($bookingListParam); + + if ($bookingList['status'] != 'success' || ($bookingList['status'] == 'success' && empty($bookingList['data']))) { + throw new ApiErrorException('Property list not found!'); + } + + foreach ($bookingList['data'] as $booking) { + + + $commission = null; + $commissionRate = null; + //$booking['booking_property']['commission'] = 15;//TOOD: DEL + switch ($booking['booking_channel']['channel_category_id']) { + case "3" : + + if (fillOnUndefined($booking['booking_property'], 'commission') && $booking['booking_property']['commission'] > 0) { + $commissionRate = $booking['booking_property']['commission']; + $commission = moneyDoubleFormatDecimal($booking['booking_property']['commission'] * $booking['total'] / 100); + } + + break; + case "2" : + + if (fillOnUndefined($booking['booking_property'], 'commission_offline') && $booking['booking_property']['commission_offline'] > 0) { + $commissionRate = $booking['booking_property']['commission_offline']; + $commission = moneyDoubleFormatDecimal($booking['booking_property']['commission_offline'] * $booking['total'] / 100); + } + + break; + case "4" : + + if (fillOnUndefined($booking['booking_property'], 'commission_channel') && $booking['booking_property']['commission_channel'] > 0) { + $commissionRate = $booking['booking_property']['commission_channel']; + $commission = moneyDoubleFormatDecimal($booking['booking_property']['commission_channel'] * $booking['total'] / 100); + } + + break; + case "7" : + + if (fillOnUndefined($booking['booking_property'], 'commission_wholesaler') && $booking['booking_property']['commission_wholesaler'] > 0) { + $commissionRate = $booking['booking_property']['commission_wholesaler']; + $commission = moneyDoubleFormatDecimal($booking['booking_property']['commission_wholesaler'] * $booking['total'] / 100); + } + + break; + } + + if (!empty($commission) && !empty($commissionRate)) { + $this->bookingService->update($booking['id'], ['commission' => $commission, 'commission_rate' => $commissionRate]); + $this->info(date('Y-m-d H:i:s') . ' Property: ' . $booking['booking_property']['name'] . ' Code: ' . $booking['booking_code'] . ' Commission: ' . $commission . ' Commission Rate: ' . $commissionRate); + } else { + + $this->bookingService->update($booking['id'], ['commission' => 0, 'commission_rate' => 0]); + $this->error(date('Y-m-d H:i:s') . ' Property: ' . $booking['booking_property']['name'] . ' Code: ' . $booking['booking_code'] . ' Commission: ' . $commission . ' Commission Rate: ' . $commissionRate); + } + + //sleep(1); + + } + + + } catch (ApiErrorException | Exception $e) { + $this->error(date('Y-m-d H:i:s') . ' ERROR:' . $e->getMessage()); + } + + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/Jobs/BookingEngineSearchReportService.php b/app/Console/Commands/Jobs/BookingEngineSearchReportService.php new file mode 100644 index 0000000..b173a3c --- /dev/null +++ b/app/Console/Commands/Jobs/BookingEngineSearchReportService.php @@ -0,0 +1,194 @@ +mailer = $mailer; + $this->propertyService = $propertyService; + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' START'); + + $reportPropertyList = [506, 529, 1606]; + //506 Green Nature Diamond Hotel + //529 Green Nature Resort & Spa Otel + //1606 Green Nature Sarıgerme + + $propertyListCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyUser.user'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + 'whereIn' => + [ + ['field' => 'id', 'value' => $reportPropertyList] + ] + ]; + + $propertyListData = $this->propertyService->select($propertyListCriteria); + + $propertyList = []; + if ($propertyListData['status'] == 'success' && !empty($propertyListData['data'])) { + $propertyList = $propertyListData['data']; + } + + + $reportData = []; + $reportDate = Carbon::now()->subDay()->toDateString(); + //$reportDate = '2023-02-28'; + $reportDateFormatted = Carbon::parse($reportDate)->format('d.m.Y'); + + $countryList = Country::all()->toArray(); + $languageList = Language::all()->toArray(); + + + $this->info(date('Y-m-d H:i:s') . ' Date: ' . $reportDate); + + foreach ($propertyList as $property) { + + $reportData = []; + + $reportData['propertyId'] = $property['id']; + $reportData['propertyName'] = $property['name']; + $reportData['propertyCountry'] = $property['country']; + $reportData['logo'] = $property['property_brand']['logoUrl']; + $reportData['date'] = $reportDateFormatted; + $reportData['propertyUserEmail'] = collect($property['property_user'])->where('user.email','<>', null)->pluck('user.email')->toArray(); + + $searchData = vwBookingEngineSearch::where('property_id', $property['id']) + ->where('date', $reportDate) + ->get()->toArray(); + + $this->info(date('Y-m-d H:i:s') . ' Property: ' . $reportData['propertyName']); + + if (!empty($searchData)) { + + $reportData['transaction']['search'] = collect($searchData)->count(); + $reportData['transaction']['roomFound'] = collect($searchData)->where('status', 1)->count(); + $reportData['transaction']['roomNotFound'] = collect($searchData)->where('status', 0)->count(); + $reportData['transaction']['preBooking'] = collect($searchData)->where('status', 2)->count(); + $reportData['transaction']['booking'] = collect($searchData)->where('status', 3)->count(); + + + $countryCodes = collect($searchData)->groupBy('country_code')->keys()->toArray(); + foreach ($countryCodes as $countryCode) { + $countryText = $countryCode; + $countryTextCheck = collect($countryList)->where('country_code', mb_strtoupper($countryCode))->first(); + if (!empty($countryTextCheck)) { + $countryText = $countryTextCheck['name']; + } + $reportData['country'][$countryCode]['text'] = $countryText; + $reportData['country'][$countryCode]['search'] = collect($searchData)->where('country_code', $countryCode)->count(); + $reportData['country'][$countryCode]['roomFound'] = collect($searchData)->where('country_code', $countryCode)->where('status', 1)->count(); + $reportData['country'][$countryCode]['roomNotFound'] = collect($searchData)->where('country_code', $countryCode)->where('status', 0)->count(); + $reportData['country'][$countryCode]['preBooking'] = collect($searchData)->where('country_code', $countryCode)->where('status', 2)->count(); + $reportData['country'][$countryCode]['booking'] = collect($searchData)->where('country_code', $countryCode)->where('status', 3)->count(); + } + + $reportData['country'] = collect($reportData['country'])->sortByDesc('search')->toArray(); + + $languageCodes = collect($searchData)->groupBy('language_code')->keys()->toArray(); + foreach ($languageCodes as $languageCode) { + $languageText = $languageCode; + $languageTextCheck = collect($languageList)->where('code', $languageCode)->first(); + if (!empty($languageTextCheck)) { + $languageText = $languageTextCheck['name']; + } + $reportData['language'][$languageCode]['text'] = $languageText; + $reportData['language'][$languageCode]['search'] = collect($searchData)->where('language_code', $languageCode)->count(); + $reportData['language'][$languageCode]['roomFound'] = collect($searchData)->where('language_code', $languageCode)->where('status', 1)->count(); + $reportData['language'][$languageCode]['roomNotFound'] = collect($searchData)->where('language_code', $languageCode)->where('status', 0)->count(); + $reportData['language'][$languageCode]['preBooking'] = collect($searchData)->where('language_code', $languageCode)->where('status', 2)->count(); + $reportData['language'][$languageCode]['booking'] = collect($searchData)->where('language_code', $languageCode)->where('status', 3)->count(); + } + + $reportData['language'] = collect($reportData['language'])->sortByDesc('search')->toArray(); + + + $occupancyCodes = collect($searchData)->groupBy('pax')->keys()->toArray(); + foreach ($occupancyCodes as $occupancyCode) { + $reportData['occupancy'][$occupancyCode]['text'] = occupancyCodeFormatted($occupancyCode); + $reportData['occupancy'][$occupancyCode]['count'] = collect($searchData)->where('pax', $occupancyCode)->count(); + } + + $reportData['occupancy'] = collect($reportData['occupancy'])->sortByDesc('count')->toArray(); + + //Daily Intensity + $dailyIntensity = []; + foreach ($searchData as $data) { + $checkInDate = $data['checkin_date']; + $checkOutDate = $data['checkout_date']; + $dateDiff = Carbon::parse($data['checkout_date'])->diffInDays(Carbon::parse($data['checkin_date'])); + + for ($i = 0; $i < $dateDiff; $i++) { + $date = Carbon::parse($checkInDate)->addDays($i)->toDateString(); + + if (!isset($dailyIntensity[$date])) { + $dailyIntensity[$date]['text'] = Carbon::parse($date)->format('d.m.Y'); + $dailyIntensity[$date]['search'] = 0; + $dailyIntensity[$date]['roomFound'] = 0; + $dailyIntensity[$date]['roomNotFound'] = 0; + $dailyIntensity[$date]['preBooking'] = 0; + $dailyIntensity[$date]['booking'] = 0; + } + + $dailyIntensity[$date]['search']++; + $dailyIntensity[$date]['roomFound'] += $data['status'] == 1 ? 1 : 0; + $dailyIntensity[$date]['roomNotFound'] += $data['status'] == 0 ? 1 : 0; + $dailyIntensity[$date]['preBooking'] += $data['status'] == 2 ? 1 : 0; + $dailyIntensity[$date]['booking'] += $data['status'] == 3 ? 1 : 0; + + } + } + ksort($dailyIntensity); + $reportData['dailyIntensity'] = $dailyIntensity; + //Daily Intensity + + } + + if (!empty($searchData)) { + $this->mailer->onQueue('bookingEngineSearchReportMail', new BookingEngineSearchReportMail($reportData)); + $this->info(date('Y-m-d H:i:s') . ' Property: ' . $reportData['propertyName'] . ' - SENT MAIL'); + } + + } + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/Jobs/DataFetch.php b/app/Console/Commands/Jobs/DataFetch.php new file mode 100644 index 0000000..0392e3a --- /dev/null +++ b/app/Console/Commands/Jobs/DataFetch.php @@ -0,0 +1,92 @@ +where('commission', '>', 1) + //->where('id', 1456) + ->with('propertyContractUser') + ->with('propertyExecutive.executiveType') + ->with('propertyContact') + ->with('propertyAdditionalInfos.propertyAdditionalInfoKey') + ->with('propertyType') + ->with('propertyStatus') + ->get(); + + $propertyList = $propertyList ? $propertyList->toArray() : null; + + + $propertyListToExcel = []; + foreach ($propertyList as $property) { + + $numberOfRooms = collect($property['property_additional_infos'])->where('additional_info_key_id',3)->first(); + $numberOfRooms = $numberOfRooms['value']; + + $propertyListToExcel[] = [ + 'contract_user' => $property['property_contract_user']['nameSurname'], + 'executive_name_surname' => reset($property['property_executive'])['name_surname'], + 'name' => $property['name'], + 'phone' => $property['property_contact']['view_full_phone'], + 'email' => $property['property_contact']['email'], + 'executive_position' => reset($property['property_executive'])['executive_type']['name'], + 'executive_phone' => reset($property['property_executive'])['view_full_phone'], + 'executive_email' => reset($property['property_executive'])['email'], + 'web' => $property['property_contact']['web'], + 'address' => $property['property_contact']['address'], + 'location' => null, //Bölge + 'number_of_rooms' => $numberOfRooms, + 'category' => $property['property_type']['name'], + 'property_pms' => null, + 'property_cm' => null, + 'commission' => $property['commission'], + 'commission_period' => $property['invoice_type'], + 'contract_start' => Carbon::createFromTimestamp($property['created_at'])->format('d.m.Y'), + 'contract_finish' => null, + 'content' => null, + 'dns' => null, + 'ssl' => null, + 'cm' => null, + 'third_party' => null, + 'golive' => Carbon::createFromTimestamp($property['created_at'])->format('d.m.Y'), + 'training' => null, + 'panel' => null, + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'tax_address' => $property['property_contact']['address'], + 'status' => $property['property_status']['name'], + 'id' => $property['id'], + ]; + + } + + $f = fopen("C:\www\api.extranetwork.com\storage/tmp.csv", "w"); + foreach ($propertyListToExcel as $property) { + fputcsv($f, $property); + } + + + } + +} diff --git a/app/Console/Commands/Jobs/MailJobs.php b/app/Console/Commands/Jobs/MailJobs.php new file mode 100644 index 0000000..2d0f0cb --- /dev/null +++ b/app/Console/Commands/Jobs/MailJobs.php @@ -0,0 +1,97 @@ +jobsService = $jobsService; + } + + public function handle() + { + + $mailJobsCriteria = + [ + "whereIn" => + [ + ["field" => "queue", "value" => $this->mailJobs] + ], + "orderBy" => + [ + ["field" => "id", "value" => "ASC"] + ], + "take" => 20 + ]; + + $mailJobs = $this->jobsService->getJobsList($mailJobsCriteria, ["queue"]); + + foreach ($mailJobs as $perJob) { + + try { + Artisan::call("queue:work --queue=" . $perJob["queue"]." --tries=5"); + } catch (Exception $e) { + $message = "Mail Job Fail --- Failed Mail : " . $perJob . " ErrorDetail : " . $e->getLine() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + continue; + } + sleep(1); + } + } + +} diff --git a/app/Console/Commands/Jobs/PriceComparisonService.php b/app/Console/Commands/Jobs/PriceComparisonService.php new file mode 100644 index 0000000..6a8cbaa --- /dev/null +++ b/app/Console/Commands/Jobs/PriceComparisonService.php @@ -0,0 +1,252 @@ +toDateString(); + + $today = Carbon::parse($today)->startOfWeek(Carbon::MONDAY); + + $year = Carbon::parse($today)->format('Y'); + $week = Carbon::parse($today)->isoWeek(); + + $daysCount = [1, 15, 45, 90]; + $exchangeCurrency = 'EUR'; + //'BookingEngine' + $comparisonChannels = ['Agoda.com', 'etstur.com', 'Booking.com']; + + $exchangeRateUSD = CurrencyRates::where('currency_code', 'USD')->where('exc_currency_code', $exchangeCurrency)->orderBy('date', 'DESC')->first()->toArray(); + $exchangeRateUSD = $exchangeRateUSD['rate']; + + $competitorPriceAnalysisController = App::make('App\Http\Controllers\V1\CompetitorPriceAnalysisController'); + + + $this->info(date('Y-m-d H:i:s') . ' START'); + + $queryWeekPrices = []; + $queryWeekPricesDaily = []; + $queryKeyHash = md5($year . '-' . $week); + + + $propertyList = vwActiveProperty::where('commission', '>', 1) + //->whereIn('id', [1433,1441,1551,1574]) + ->get()->toArray(); + + foreach ($propertyList as $property) { + + try { + + $channelManagerProperty = ChannelManagerPropertyMapping::where('channel_manager_id', 12) + ->where('property_id', $property['id'])->first(); + $channelManagerProperty = $channelManagerProperty ? $channelManagerProperty->toArray() : null; + $channelManagerPropertyId = $channelManagerProperty ? $channelManagerProperty['channel_manager_property_id'] : null; + + $this->info(date('Y-m-d H:i:s') . ' ' . $property['id'] . ' - ' . $property['name']); + + if (empty($channelManagerPropertyId)) { + $this->error(date('Y-m-d H:i:s') . ' ' . $property['name'] . ': channelManagerPropertyId!'); + continue; + } + + + foreach ($daysCount as $day) { + + + //Booking Engine + $queryDate = Carbon::parse($today)->addDays($day)->toDateString(); + + //FIRST Trigger + $paramCompetitorPrice = ['date' => $queryDate, 'competitor_property_key' => $channelManagerPropertyId, 'currency' => $exchangeCurrency,]; + $getPropertyCompetitorPrice = $competitorPriceAnalysisController->getPropertyCompetitorPrice($paramCompetitorPrice); + + $this->info(date('Y-m-d H:i:s') . ' ' . $property['id'] . ' - ' . $queryDate); + + sleep(1); + + + $bestPriceOfDay = 0; + $queryWeekPrices[$queryKeyHash][$property['id']]['BookingEngine'][$day] = [ + 'channel' => 'BookingEngine', + 'date' => $queryDate, + 'amount' => null, + 'currency' => null + ]; + + $propertyPrices = PropertyRoomRatePrice::where('property_id', $property['id']) + ->where('channel_id', 5) + ->where('stop_sell', 0) + ->where('status', 1) + ->where('amount', '<>', 0) + ->where('amount', '<>', null) + ->where('date', $queryDate) + ->with('roomRateMapping.propertyRoomRate') + ->get(); + + $propertyPrices = $propertyPrices ? $propertyPrices->toArray() : null; + + if (empty($propertyPrices)) { + //continue; + } + + $bestPrice = collect($propertyPrices) + ->where('room_rate_mapping.property_room_rate.name', 'Best Available Rate') + ->sortBy('amount')->first(); + + + + if (empty($bestPrice)) { + //continue; + } else { + + + if ($bestPrice['currency'] != $exchangeCurrency) { + $exchangeRate = CurrencyRates::where('currency_code', $bestPrice['currency']) + ->where('exc_currency_code', $exchangeCurrency) + ->orderBy('date', 'DESC')->first()->toArray(); + $exchangeRate = $exchangeRate['rate']; + + $bestPrice['amount'] = $bestPrice['amount'] * $exchangeRate; + } + + $bestPrice['amount'] = moneyDoubleFormatDecimal($bestPrice['amount']); + $queryWeekPrices[$queryKeyHash][$property['id']]['BookingEngine'][$day] = [ + 'channel' => 'BookingEngine', + 'date' => $queryDate, + 'amount' => $bestPrice['amount'], + 'currency' => $exchangeCurrency, + ]; + + $queryWeekPricesDaily[$queryKeyHash][$property['id']][$day] = $bestPrice['amount']; + } + //Booking Engine + + + //OTHER Channels + + $paramCompetitorPrice = ['date' => $queryDate, 'competitor_property_key' => $channelManagerPropertyId, 'currency' => $exchangeCurrency]; + $getPropertyCompetitorPrice = $competitorPriceAnalysisController->getPropertyCompetitorPrice($paramCompetitorPrice); + + foreach ($comparisonChannels as $comparisonChannel) { + + $queryWeekPrices[$queryKeyHash][$property['id']][$comparisonChannel][$day] = [ + 'channel' => $comparisonChannel, + 'date' => $queryDate, + 'amount' => null, + 'currency' => null + ]; + + if(!isset($queryWeekPricesDaily[$queryKeyHash][$property['id']][$day])) { + $queryWeekPricesDaily[$queryKeyHash][$property['id']][$day] = $queryWeekPrices[$queryKeyHash][$property['id']][$comparisonChannel][$day]['amount']; + } + + if ($getPropertyCompetitorPrice['status'] && !empty($getPropertyCompetitorPrice['data']['all'])) { + + $getPropertyCompetitorPriceChannel = $getPropertyCompetitorPrice['data']['all']; + $getPropertyCompetitorPriceChannelCheck = collect($getPropertyCompetitorPriceChannel)->where('provider', $comparisonChannel)->first(); + + if ($getPropertyCompetitorPriceChannelCheck) { + $queryWeekPrices[$queryKeyHash][$property['id']][$comparisonChannel][$day]['amount'] = moneyDoubleFormatDecimal($getPropertyCompetitorPriceChannelCheck['amount'] * $exchangeRateUSD); + $queryWeekPrices[$queryKeyHash][$property['id']][$comparisonChannel][$day]['currency'] = 'EUR'; + + //BestPriceCheck + + + if(empty($queryWeekPricesDaily[$queryKeyHash][$property['id']][$day])) { + $queryWeekPricesDaily[$queryKeyHash][$property['id']][$day] = $queryWeekPrices[$queryKeyHash][$property['id']][$comparisonChannel][$day]['amount']; + }elseif ($queryWeekPrices[$queryKeyHash][$property['id']][$comparisonChannel][$day]['amount'] < $queryWeekPricesDaily[$queryKeyHash][$property['id']][$day]) { + $queryWeekPricesDaily[$queryKeyHash][$property['id']][$day] = $queryWeekPrices[$queryKeyHash][$property['id']][$comparisonChannel][$day]['amount']; + } + + } + + } + + } + } + + //dd($queryWeekPrices[$queryKeyHash][$property['id']],$queryWeekPricesDaily[$queryKeyHash][$property['id']]); + + foreach ($queryWeekPrices[$queryKeyHash][$property['id']] as $channelKey => $channelDays) { + foreach ($channelDays as $channelDayKey => $channelDay) { + + //dd($channelDay,$queryWeekPricesDaily[$queryKeyHash][$property['id']][$channelDayKey]); + + + if ($queryWeekPricesDaily[$queryKeyHash][$property['id']][$channelDayKey] == $channelDay['amount'] && !is_null($channelDay['amount'])) { + $queryWeekPrices[$queryKeyHash][$property['id']][$channelKey][$channelDayKey]['bestPrice'] = true; + } else { + $queryWeekPrices[$queryKeyHash][$property['id']][$channelKey][$channelDayKey]['bestPrice'] = false; + } + + } + } + + $this->info(date('Y-m-d H:i:s') . ' ' . $property['id'] . ' - ' . $property['name'] . ' OK!'); + + + $propertyComparisonPrice = [ + 'property_id' => $property['id'], + 'week_key' => $queryKeyHash, + 'year' => $year, + 'week' => $week, + 'date' => $today, + 'data' => json_encode($queryWeekPrices[$queryKeyHash][$property['id']]), + 'status' => 1, + 'created_at' => Carbon::now()->unix(), + 'updated_at' => Carbon::now()->unix(), + + ]; + + $propertyPriceComparisonCheck = PropertyPriceComparison::where('week_key', $queryKeyHash)->where('property_id', $property['id'])->first(); + + if ($propertyPriceComparisonCheck) { + $propertyPriceComparisonCheck = $propertyPriceComparisonCheck->toArray(); + $propertyPriceComparison = PropertyPriceComparison::where('id', $propertyPriceComparisonCheck['id'])->update($propertyComparisonPrice); + } else { + $propertyPriceComparison = PropertyPriceComparison::insert($propertyComparisonPrice); + } + + unset($queryWeekPrices[$queryKeyHash]); + + } catch (ApiErrorException|Exception $e) { + $this->error(date('Y-m-d H:i:s') . ' ERROR: L: ' . $e->getLine() . ' - ' . $e->getMessage()); + } + + } + + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/Jobs/PropertyCatalogService.php b/app/Console/Commands/Jobs/PropertyCatalogService.php new file mode 100644 index 0000000..c9b24c3 --- /dev/null +++ b/app/Console/Commands/Jobs/PropertyCatalogService.php @@ -0,0 +1,113 @@ +pdf = $pdf; + $this->mailer = $mailer; + $this->propertyService = $propertyService; + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' START'); + + $pdfDataRequest = [ + 'property_id' => $this->argument('property_id'), + 'locale' => $this->argument('language') + ]; + + $pdfContentService = App::make('App\Core\Service\PdfContentService'); + + $factSheetDataResponse = $pdfContentService->factSheetData($pdfDataRequest); + if ($factSheetDataResponse['status'] != 'success') { + throw new Exception($factSheetDataResponse['message']); + } + + $pageContent = $factSheetDataResponse['data']; + $pdfLanguage = $pdfDataRequest['locale']; + + $this->info(date('Y-m-d H:i:s') . ' PDF'); + + app('translator')->setLocale($pdfLanguage); + $pdfService = $this->pdf->loadView('pdf.propertyCatalog', compact('pageContent'), [], 'UTF8'); + $pdfService->setOptions([ + 'dpi' => 100, + 'isHtml5ParserEnabled' => true, + 'isRemoteEnabled' => true, + 'chroot', base_path(), + 'enable_html5_parser' => true, + 'enable_css_float' => true + ]); + + + $hotelName = Str::slug($pageContent['name'], '_', 'en') . '_' . $pdfLanguage; + $pathStorage = Config::get('app.fileSystemDriver') . '/property-catalog/' . $hotelName . '.pdf'; + + file_put_contents($pathStorage, $pdfService->output()); + + if ($this->option('email')) { + + $this->info(date('Y-m-d H:i:s') . ' E-Mail'); + + $languageDetail = Language::where('code', $pdfLanguage)->first(); + $languageDetail = $languageDetail ? $languageDetail->toArray() : null; + + $mailParams = [ + 'email' => $this->option('email'), + 'catalogUrl' => Config::get('app.imageUrl') . '/property-catalog/' . $hotelName . '.pdf', + 'propertyName' => $pageContent['name'], + 'language' => $pdfLanguage, + 'languageText' => __($languageDetail['language_key'],[],'tr'), + 'pathStorage' => $pathStorage + ]; + + + $this->mailer->send('emails.propertyCatalogMail', ['mailParams' => $mailParams], function ($message) use ($mailParams) { + $message->to($mailParams['email'], 'Extranetwork Property Katalog') + ->bcc(Config::get('app.logMailAddress')) + ->subject($mailParams['propertyName'] . ' Catalog - ' . $mailParams['languageText']) + //->attach($mailParams['pathStorage']) + ->from(Config::get('app.mailSenderAddress'), 'Extranetwork'); + }); + + } + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/Jobs/PropertyInvoiceService.php b/app/Console/Commands/Jobs/PropertyInvoiceService.php new file mode 100644 index 0000000..bcf73fd --- /dev/null +++ b/app/Console/Commands/Jobs/PropertyInvoiceService.php @@ -0,0 +1,117 @@ +firstOfMonth()->toDateString(); + $lastDayOfYear = Carbon::parse($lastAllOfYear)->firstOfMonth()->toDateString(); + + $diffInPeriod = Carbon::parse($startDayOfYear)->diffInMonths(Carbon::parse($lastDayOfYear)); + + $periodList = []; + for ($i = 0; $i < $diffInPeriod; $i++) { + $periodList[] = Carbon::parse($firstAllOfYear)->addMonths($i)->format('Y-m'); + }*/ + //$periodList[] = '2025-09'; + //BULK + + //$invoiceWithPeriod = $propertyInvoiceService->invoiceWithPeriod(623, '2025-10'); + //dd($invoiceWithPeriod); + //$periodList = ['2025-09']; + + //$lastOfMonth = '2025-10-30'; + //$dayOfMonth = '2025-10-30'; + + //$lastOfMonth = Carbon::now()->lastOfMonth()->format('Y-m-d'); + //$dayOfMonth = Carbon::now()->format('Y-m-d'); + + //if ($lastOfMonth != $dayOfMonth) { + //$this->alert(date('Y-m-d H:i:s') . ' : ' . $lastOfMonth . ' - ' . $dayOfMonth); + //return false; + //} + + + //$period = Carbon::parse($dayOfMonth)->lastOfMonth()->format('Y-m'); + //$periodList = [$period]; + + $year = Carbon::now()->year; + $currentMonth = Carbon::now()->month; + $periodList = []; + for ($month = $currentMonth; $month <= 12; $month++) { + $periodList[] = Carbon::create($year, $month, 1)->format('Y-m'); + } + + + $this->info(date('Y-m-d H:i:s') . ' START'); + + $propertyList = Property::where('commission', '>', 1) + ->whereIn('status', [1,3]) + //->whereIn('id', [1810]) + ->get()->toArray(); + + foreach ($periodList as $period) { + + $this->alert('PERIOD: ' . $period); + + foreach ($propertyList as $property) { + + try { + + $invoiceWithPeriod = $propertyInvoiceService->invoiceWithPeriod($property['id'], $period); + + if ($invoiceWithPeriod['status'] == 'success') { + $this->info(date('Y-m-d H:i:s') . ': ' . $period . ' ' . $property['id'] . ' - ' . $property['name'] . ' : ' . $period . ' - ' . $invoiceWithPeriod['data']['total'] . ' ' . $invoiceWithPeriod['data']['currency']); + } else { + $this->error(date('Y-m-d H:i:s') . ': ' . $period . ' ' . $property['id'] . ' - ' . $invoiceWithPeriod['message']); + } + + + } catch (ApiErrorException | Exception $e) { + $this->error(date('Y-m-d H:i:s') . ' ERROR: L: ' . $e->getLine() . ' - ' . $e->getMessage() . ' - ' . $property['id'] . ' - ' . $property['name']); + } + + } + } + + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/Jobs/PropertySummaryService.php b/app/Console/Commands/Jobs/PropertySummaryService.php new file mode 100644 index 0000000..95c6eda --- /dev/null +++ b/app/Console/Commands/Jobs/PropertySummaryService.php @@ -0,0 +1,124 @@ +firstOfMonth()->toDateString(); + $lastDayOfYear = Carbon::parse($lastAllOfYear)->firstOfMonth()->toDateString(); + + $diffInPeriod = Carbon::parse($startDayOfYear)->diffInMonths(Carbon::parse($lastDayOfYear)); + + $periodList = []; + for ($i = 0; $i < $diffInPeriod; $i++) { + $periodList[] = Carbon::parse($firstAllOfYear)->addMonths($i)->format('Y-m'); + }*/ + //$periodList[] = '2025-09'; + //BULK + + //$invoiceWithPeriod = $propertyInvoiceService->invoiceWithPeriod(623, '2025-10'); + //dd($invoiceWithPeriod); + //$periodList = ['2025-09']; + + //$lastOfMonth = '2025-10-30'; + //$dayOfMonth = '2025-10-30'; + + //$lastOfMonth = Carbon::now()->lastOfMonth()->format('Y-m-d'); + //$dayOfMonth = Carbon::now()->format('Y-m-d'); + + //if ($lastOfMonth != $dayOfMonth) { + //$this->alert(date('Y-m-d H:i:s') . ' : ' . $lastOfMonth . ' - ' . $dayOfMonth); + //return false; + //} + + + //$period = Carbon::parse($dayOfMonth)->lastOfMonth()->format('Y-m'); + //$periodList = [$period]; + + $year = Carbon::now()->year; + $currentMonth = Carbon::now()->month; + $periodList = []; + for ($month = $currentMonth; $month <= 12; $month++) { + $periodList[] = Carbon::create($year, $month, 1)->format('Y-m'); + } + + //$periodList = ['2025-03']; + + $this->info(date('Y-m-d H:i:s') . ' START'); + + $propertyList = Property::where('commission', '>', 1) + ->where('status', 1) + //->whereIn('id', [71]) + ->get()->toArray(); + + $types[1] = 'Checkout'; + $types[2] = 'Transaction'; + foreach ($periodList as $period) { + + $this->alert('PERIOD: ' . $period); + + foreach ($propertyList as $property) { + + try { + + foreach ($types as $typeCode => $type) { + + $invoiceWithPeriod = $propertySummaryService->summaryWithPeriod($property['id'], $period, $typeCode); + + if ($invoiceWithPeriod['status'] == 'success') { + $this->info(date('Y-m-d H:i:s') . ': ' . $period . ' ' . $property['id'] . ' - ' . $property['name'] . ' : ' . $type . ' : ' . $period . ' - ' . $invoiceWithPeriod['data']['total'] . ' ' . $invoiceWithPeriod['data']['currency']); + } else { + $this->error(date('Y-m-d H:i:s') . ': ' . $period . ' ' . $property['id'] . ' - ' . $invoiceWithPeriod['message']); + } + + } + + + } catch (ApiErrorException | Exception $e) { + $this->error(date('Y-m-d H:i:s') . ' ERROR: L: ' . $e->getLine() . ' - ' . $e->getMessage() . ' - ' . $property['id'] . ' - ' . $property['name']); + } + + } + } + + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/Jobs/SummaryReportMail.php b/app/Console/Commands/Jobs/SummaryReportMail.php new file mode 100644 index 0000000..151e199 --- /dev/null +++ b/app/Console/Commands/Jobs/SummaryReportMail.php @@ -0,0 +1,386 @@ +mailer = $mailer; + $this->propertyService = $propertyService; + $this->currencyService = $currencyService; + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' START'); + + $today = Carbon::now()->subDay()->toDateString(); + + $report['daily']['data'] = []; + $report['daily']['title'] = 'Daily Report'; + $report['daily']['period'] = Carbon::parse($today)->format('d.m.Y'); + $daily = vwBookingSummaryAll::where('time', '>', Carbon::parse($today)->toDateString()) + ->where('time', '<', Carbon::parse($today)->addDay()->toDateString()) + ->where('commission', '>', 0) + ->get()->toArray(); + + if ($daily) { + $dataCollect = collect($daily); + $dataCollect = $dataCollect->filter(function ($item) { + if ($item['property_id'] == 362) { + //Exclude Commission - Barın Hotel, 34 Hotelbeds, 103 World2Meet + if (!in_array($item['channel_id'], [34, 103])) { + return $item; + } + } else if ($item['property_id'] == 712) { + //Exclude Commission - G Hotels Skopje + if (!in_array($item['id'], [18122, 18091, 18090, 18089, 18088, 18087, 18086])) { + return $item; + } + } else { + return $item; + } + }); + + $dataCollectGroup = $dataCollect->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroup as $currencyCode => $currencyGroup) { + $report['daily']['data'][$currencyCode]['count'] = count($currencyGroup); + $report['daily']['data'][$currencyCode]['total'] = array_sum(pickItemFromArray('total', $currencyGroup)); + $report['daily']['data'][$currencyCode]['commission'] = array_sum(pickItemFromArray('commission', $currencyGroup)); + } + + $report['daily']['summary'] = ['count' => 0, 'total' => 0, 'commission' => 0]; + foreach ($report['daily']['data'] as $currentCurrency => $dailyData) { + $lastExchangeRate = $this->currencyService->lastExchangeRate($currentCurrency, 'EUR'); + $report['daily']['summary']['count'] += $dailyData['count']; + $report['daily']['summary']['total'] += $dailyData['total'] * $lastExchangeRate['data']; + $report['daily']['summary']['commission'] += $dailyData['commission'] * $lastExchangeRate['data']; + } + } + + + $report['monthly']['data'] = []; + $report['monthly']['title'] = 'Monthly Report'; + $report['monthly']['period'] = Carbon::parse($today)->firstOfMonth()->format('m.Y'); + $monthly = vwBookingSummaryAll::where('time', '>', Carbon::parse($today)->firstOfMonth()->toDateString()) + ->where('time', '<', Carbon::parse($today)->addMonth()->firstOfMonth()->toDateString()) + ->where('commission', '>', 0) + ->get()->toArray(); + + if ($monthly) { + $dataCollect = collect($monthly); + $dataCollect = $dataCollect->filter(function ($item) { + if ($item['property_id'] == 362) { + //Exclude Commission - Barın Hotel, 34 Hotelbeds, 103 World2Meet + if (!in_array($item['channel_id'], [34, 103])) { + return $item; + } + } else if ($item['property_id'] == 712) { + //Exclude Commission - G Hotels Skopje + if (!in_array($item['id'], [18122, 18091, 18090, 18089, 18088, 18087, 18086])) { + return $item; + } + } else { + return $item; + } + }); + + $dataCollectGroup = $dataCollect->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroup as $currencyCode => $currencyGroup) { + $report['monthly']['data'][$currencyCode]['count'] = count($currencyGroup); + $report['monthly']['data'][$currencyCode]['total'] = array_sum(pickItemFromArray('total', $currencyGroup)); + $report['monthly']['data'][$currencyCode]['commission'] = array_sum(pickItemFromArray('commission', $currencyGroup)); + } + + $report['monthly']['summary'] = ['count' => 0, 'total' => 0, 'commission' => 0]; + foreach ($report['monthly']['data'] as $currentCurrency => $dailyData) { + $lastExchangeRate = $this->currencyService->lastExchangeRate($currentCurrency, 'EUR'); + $report['monthly']['summary']['count'] += $dailyData['count']; + $report['monthly']['summary']['total'] += $dailyData['total'] * $lastExchangeRate['data']; + $report['monthly']['summary']['commission'] += $dailyData['commission'] * $lastExchangeRate['data']; + } + + + //summaryCheckout + $monthlyCheckout = vwBookingSummaryAll::where('checkout_period', Carbon::parse($today)->firstOfMonth()->format('Y-m')) + ->where('commission', '>', 0) + ->get()->toArray(); + + $dataCollect = collect($monthlyCheckout); + $dataCollect = $dataCollect->filter(function ($item) { + if ($item['property_id'] == 362) { + //Exclude Commission - Barın Hotel, 34 Hotelbeds, 103 World2Meet + if (!in_array($item['channel_id'], [34, 103])) { + return $item; + } + } else if ($item['property_id'] == 712) { + //Exclude Commission - G Hotels Skopje + if (!in_array($item['id'], [18122, 18091, 18090, 18089, 18088, 18087, 18086])) { + return $item; + } + } else { + return $item; + } + }); + + $dataCollectGroup = $dataCollect->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroup as $currencyCode => $currencyGroup) { + $report['monthly']['summaryCheckoutData'][$currencyCode]['count'] = count($currencyGroup); + $report['monthly']['summaryCheckoutData'][$currencyCode]['total'] = array_sum(pickItemFromArray('total', $currencyGroup)); + $report['monthly']['summaryCheckoutData'][$currencyCode]['commission'] = array_sum(pickItemFromArray('commission', $currencyGroup)); + } + + $report['monthly']['summaryCheckout'] = ['count' => 0, 'total' => 0, 'commission' => 0]; + foreach ($report['monthly']['summaryCheckoutData'] as $currentCurrency => $dailyData) { + $lastExchangeRate = $this->currencyService->lastExchangeRate($currentCurrency, 'EUR'); + $report['monthly']['summaryCheckout']['count'] += $dailyData['count']; + $report['monthly']['summaryCheckout']['total'] += $dailyData['total'] * $lastExchangeRate['data']; + $report['monthly']['summaryCheckout']['commission'] += $dailyData['commission'] * $lastExchangeRate['data']; + } + + $monthlySummary = []; + $annuallyMonths = []; + for ($i = 0; $i < 12; $i++) { + $annuallyMonths[] = Carbon::parse($today)->firstOfYear()->addMonths($i)->format('Y-m'); + } + + $vwBookingSummaryTransaction = vwBookingSummaryAll::whereIn('transaction_period',$annuallyMonths) + ->where('commission', '>', 0) + ->get()->toArray(); + + $vwBookingSummaryCheckout = vwBookingSummaryAll::whereIn('checkout_period',$annuallyMonths) + ->where('commission', '>', 0) + ->get()->toArray(); + + + $vwBookingSummaryTransactionGrouped = collect($vwBookingSummaryTransaction)->groupBy('transaction_period')->toArray(); + $vwBookingSummaryCheckoutGrouped = collect($vwBookingSummaryCheckout)->groupBy('checkout_period')->toArray(); + + + $monthlySummary['month'] = []; + foreach ($annuallyMonths as $annuallyMonth) { + //$annuallyMonth = Carbon::parse($annuallyMonth)->format('Y-m'); + + $monthlySummary['month'][$annuallyMonth] = $annuallyMonth; + + $monthlySummary['transaction'][$annuallyMonth]['count'] = 0; + $monthlySummary['transaction'][$annuallyMonth]['total'] = 0; + $monthlySummary['transaction'][$annuallyMonth]['commission'] = 0; + if(isset($vwBookingSummaryTransactionGrouped[$annuallyMonth])) { + $dataCollectGroupCurrency = collect($vwBookingSummaryTransactionGrouped[$annuallyMonth])->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroupCurrency as $currencyCode => $currencyGroup) { + + $lastExchangeRate = $this->currencyService->lastExchangeRate($currencyCode, 'EUR'); + $monthlySummary['transaction'][$annuallyMonth]['count'] += collect($currencyGroup)->count(); + $monthlySummary['transaction'][$annuallyMonth]['total'] += collect($currencyGroup)->sum('total') * $lastExchangeRate['data']; + $monthlySummary['transaction'][$annuallyMonth]['commission'] += collect($currencyGroup)->sum('commission') * $lastExchangeRate['data']; + + } + } + + $monthlySummary['checkout'][$annuallyMonth]['count'] = 0; + $monthlySummary['checkout'][$annuallyMonth]['total'] = 0; + $monthlySummary['checkout'][$annuallyMonth]['commission'] = 0; + if(isset($vwBookingSummaryCheckoutGrouped[$annuallyMonth])) { + $dataCollectGroupCurrency = collect($vwBookingSummaryCheckoutGrouped[$annuallyMonth])->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroupCurrency as $currencyCode => $currencyGroup) { + + $lastExchangeRate = $this->currencyService->lastExchangeRate($currencyCode, 'EUR'); + $monthlySummary['checkout'][$annuallyMonth]['count'] += collect($currencyGroup)->count(); + $monthlySummary['checkout'][$annuallyMonth]['total'] += collect($currencyGroup)->sum('total') * $lastExchangeRate['data']; + $monthlySummary['checkout'][$annuallyMonth]['commission'] += collect($currencyGroup)->sum('commission') * $lastExchangeRate['data']; + + } + } + + } + + $report['monthlySummary'] = $monthlySummary; + + } + + $report['annually']['data'] = []; + $report['annually']['title'] = 'Annually Report'; + $report['annually']['period'] = Carbon::parse($today)->firstOfYear()->format('Y'); + $annually = vwBookingSummaryAll::where('time', '>', Carbon::parse($today)->firstOfYear()->toDateString()) + ->where('time', '<', Carbon::parse($today)->addYear()->firstOfYear()->toDateString()) + ->where('commission', '>', 0) + ->get()->toArray(); + + if ($annually) { + $dataCollect = collect($annually); + $dataCollect = $dataCollect->filter(function ($item) { + if ($item['property_id'] == 362) { + //Exclude Commission - Barın Hotel, 34 Hotelbeds, 103 World2Meet + if (!in_array($item['channel_id'], [34, 103])) { + return $item; + } + } else if ($item['property_id'] == 712) { + //Exclude Commission - G Hotels Skopje + if (!in_array($item['id'], [18122, 18091, 18090, 18089, 18088, 18087, 18086])) { + return $item; + } + } else { + return $item; + } + }); + + $dataCollectGroup = $dataCollect->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroup as $currencyCode => $currencyGroup) { + $report['annually']['data'][$currencyCode]['count'] = count($currencyGroup); + $report['annually']['data'][$currencyCode]['total'] = array_sum(pickItemFromArray('total', $currencyGroup)); + $report['annually']['data'][$currencyCode]['commission'] = array_sum(pickItemFromArray('commission', $currencyGroup)); + } + + $report['annually']['summary'] = ['count' => 0, 'total' => 0, 'commission' => 0]; + foreach ($report['annually']['data'] as $currentCurrency => $dailyData) { + $lastExchangeRate = $this->currencyService->lastExchangeRate($currentCurrency, 'EUR'); + $report['annually']['summary']['count'] += $dailyData['count']; + $report['annually']['summary']['total'] += $dailyData['total'] * $lastExchangeRate['data']; + $report['annually']['summary']['commission'] += $dailyData['commission'] * $lastExchangeRate['data']; + } + + + //summaryCheckout Annually + + $annuallyCheckout = vwBookingSummaryAll::where('checkout_date', '>=', Carbon::parse($today)->firstOfYear()->format('Y-m-d')) + ->where('checkout_date', '<=', Carbon::parse($today)->endOfYear()->format('Y-m-d')) + ->where('commission', '>', 0) + ->get()->toArray(); + + $dataCollect = collect($annuallyCheckout); + $dataCollect = $dataCollect->filter(function ($item) { + if ($item['property_id'] == 362) { + //Exclude Commission - Barın Hotel, 34 Hotelbeds, 103 World2Meet + if (!in_array($item['channel_id'], [34, 103])) { + return $item; + } + } else if ($item['property_id'] == 712) { + //Exclude Commission - G Hotels Skopje + if (!in_array($item['id'], [18122, 18091, 18090, 18089, 18088, 18087, 18086])) { + return $item; + } + } else { + return $item; + } + }); + + $dataCollectGroup = $dataCollect->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroup as $currencyCode => $currencyGroup) { + $report['annually']['summaryCheckoutData'][$currencyCode]['count'] = count($currencyGroup); + $report['annually']['summaryCheckoutData'][$currencyCode]['total'] = array_sum(pickItemFromArray('total', $currencyGroup)); + $report['annually']['summaryCheckoutData'][$currencyCode]['commission'] = array_sum(pickItemFromArray('commission', $currencyGroup)); + } + + $report['annually']['summaryCheckout'] = ['count' => 0, 'total' => 0, 'commission' => 0]; + foreach ($report['annually']['summaryCheckoutData'] as $currentCurrency => $dailyData) { + $lastExchangeRate = $this->currencyService->lastExchangeRate($currentCurrency, 'EUR'); + $report['annually']['summaryCheckout']['count'] += $dailyData['count']; + $report['annually']['summaryCheckout']['total'] += $dailyData['total'] * $lastExchangeRate['data']; + $report['annually']['summaryCheckout']['commission'] += $dailyData['commission'] * $lastExchangeRate['data']; + } + + } + + //Hotel LIST + $vwActivePropertySummary = []; + $vwActiveProperty = vwActiveProperty::where('commission', '>', 1)->orderByDesc('id')->get()->toArray(); + $vwActivePropertySummary['count'] = count($vwActiveProperty); + $vwActivePropertySummary['groupByMonth'] = collect($vwActiveProperty)->sortByDesc('month')->groupBy('month')->toArray(); + $vwActivePropertySummary['lastMonth'] = collect($vwActivePropertySummary['groupByMonth'])->take(12)->toArray(); + + if ($vwActivePropertySummary['lastMonth']) { + foreach ($vwActivePropertySummary['lastMonth'] as $lastMonthKey => $lastMonth) { + $vwActivePropertySummary['lastMonth'][$lastMonthKey] = collect($vwActivePropertySummary['lastMonth'][$lastMonthKey])->sortByDesc('id')->toArray(); + if ($vwActivePropertySummary['lastMonth'][$lastMonthKey]) { + $vwActivePropertySummary['lastMonth'][$lastMonthKey] = array_values($vwActivePropertySummary['lastMonth'][$lastMonthKey]); + } + } + } + + //Hotel LIST + + $report['activeProperty'] = $vwActivePropertySummary; + + //Comparative Summary Data + //Carbon::setLocale('tr'); + for ($month = 1; $month <= 12; $month++) { + $months[$month] = Carbon::create(null, $month, 1)->translatedFormat('F'); + } + $report['comparative']['monthList'] = $months; + + $currentYear = Carbon::parse($today)->firstOfYear()->format('Y'); + $lastYear = Carbon::parse($today)->subYear()->firstOfYear()->format('Y'); + + $report['comparative']['yearList'][] = $lastYear; + $report['comparative']['yearList'][] = $currentYear; + + $periodList = []; + foreach ([$currentYear,$lastYear] as $periodYear) { + for ($month = 1; $month <= 12; $month++) { + $periodList[] = $periodYear . '-' . str_pad((integer)$month, 2, '0', STR_PAD_LEFT); + } + } + + $propertyInvoice = PropertyInvoice::where('status',1)->whereIn('period', $periodList); + $propertyInvoice = $propertyInvoice ? $propertyInvoice->get()->toArray() : []; + + foreach ($propertyInvoice as $invoice) { + $periodYear = Carbon::parse($invoice['period'])->format('Y'); + $periodMonth = (int)Carbon::parse($invoice['period'])->format('m'); + if(!isset($report['comparative']['data'][$periodYear][$periodMonth])) { + $report['comparative']['data'][$periodYear][$periodMonth] = 0; + } + $report['comparative']['data'][$periodYear][$periodMonth]+=$invoice['total']; + } + + $comparativeDiff = []; + foreach ($months as $monthKey => $month) { + $comparativeDiff[$monthKey]['amount'] = $report['comparative']['data'][$currentYear][$monthKey] - $report['comparative']['data'][$lastYear][$monthKey]; + } + $report['comparative']['diff'] = $comparativeDiff; + //Comparative Summary Data + + + //$json = json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + //file_put_contents(resource_path('data/data.json'), $json); + //die(); + + $this->mailer->onQueue('dailyReportMail', new DailyReportMail($report)); + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/Jobs/SummaryReportMailSales.php b/app/Console/Commands/Jobs/SummaryReportMailSales.php new file mode 100644 index 0000000..0129ff7 --- /dev/null +++ b/app/Console/Commands/Jobs/SummaryReportMailSales.php @@ -0,0 +1,310 @@ +mailer = $mailer; + $this->propertyService = $propertyService; + $this->currencyService = $currencyService; + $this->userService = $userService; + } + + public function reportCalculate($userId, $contractUserProperty, $type) + { + + $today = Carbon::now()->subDay()->toDateString(); + + $report = ['data' => null, 'summary' => null]; + + + switch ($type) { + + case 'daily': + $vwBookingSummaryAll = vwBookingSummaryAll::where('time', '>', Carbon::parse($today)->toDateString()) + ->where('time', '<', Carbon::parse($today)->addDay()->toDateString()) + ->where('commission', '>', 0) + ->where('contract_user_id', $userId) + ->whereIn('property_id', $contractUserProperty) + ->get()->toArray(); + break; + + case 'monthly': + + $monthlySummary = []; + $annuallyMonths = []; + for ($i = 0; $i < 12; $i++) { + $annuallyMonths[] = Carbon::parse($today)->firstOfYear()->addMonths($i)->format('Y-m'); + } + + $vwBookingSummaryAll = vwBookingSummaryAll::whereIn('transaction_period', [Carbon::parse($today)->firstOfMonth()->format('Y-m')]) + ->where('commission', '>', 0) + ->where('contract_user_id', $userId) + ->whereIn('property_id', $contractUserProperty) + ->get()->toArray(); + + $vwBookingSummaryTransaction = vwBookingSummaryAll::whereIn('transaction_period', $annuallyMonths) + ->where('commission', '>', 0) + ->where('contract_user_id', $userId) + ->whereIn('property_id', $contractUserProperty) + ->get()->toArray(); + + $vwBookingSummaryCheckout = vwBookingSummaryAll::whereIn('checkout_period', $annuallyMonths) + ->where('commission', '>', 0) + ->where('contract_user_id', $userId) + ->whereIn('property_id', $contractUserProperty) + ->get()->toArray(); + + + $vwBookingSummaryTransactionGrouped = collect($vwBookingSummaryTransaction)->groupBy('transaction_period')->toArray(); + $vwBookingSummaryCheckoutGrouped = collect($vwBookingSummaryCheckout)->groupBy('checkout_period')->toArray(); + + $monthlySummary['month'] = []; + foreach ($annuallyMonths as $annuallyMonth) { + $annuallyMonth = Carbon::parse($annuallyMonth)->format('Y-m'); + + $monthlySummary['month'][$annuallyMonth] = $annuallyMonth; + + $monthlySummary['transaction'][$annuallyMonth]['count'] = 0; + $monthlySummary['transaction'][$annuallyMonth]['total'] = 0; + $monthlySummary['transaction'][$annuallyMonth]['commission'] = 0; + if(isset($vwBookingSummaryTransactionGrouped[$annuallyMonth])) { + $dataCollectGroupCurrency = collect($vwBookingSummaryTransactionGrouped[$annuallyMonth])->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroupCurrency as $currencyCode => $currencyGroup) { + + $lastExchangeRate = $this->currencyService->lastExchangeRate($currencyCode, 'EUR'); + $monthlySummary['transaction'][$annuallyMonth]['count'] += collect($currencyGroup)->count(); + $monthlySummary['transaction'][$annuallyMonth]['total'] += collect($currencyGroup)->sum('total') * $lastExchangeRate['data']; + $monthlySummary['transaction'][$annuallyMonth]['commission'] += collect($currencyGroup)->sum('commission') * $lastExchangeRate['data']; + + } + } + + $monthlySummary['checkout'][$annuallyMonth]['count'] = 0; + $monthlySummary['checkout'][$annuallyMonth]['total'] = 0; + $monthlySummary['checkout'][$annuallyMonth]['commission'] = 0; + if(isset($vwBookingSummaryCheckoutGrouped[$annuallyMonth])) { + $dataCollectGroupCurrency = collect($vwBookingSummaryCheckoutGrouped[$annuallyMonth])->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroupCurrency as $currencyCode => $currencyGroup) { + + $lastExchangeRate = $this->currencyService->lastExchangeRate($currencyCode, 'EUR'); + $monthlySummary['checkout'][$annuallyMonth]['count'] += collect($currencyGroup)->count(); + $monthlySummary['checkout'][$annuallyMonth]['total'] += collect($currencyGroup)->sum('total') * $lastExchangeRate['data']; + $monthlySummary['checkout'][$annuallyMonth]['commission'] += collect($currencyGroup)->sum('commission') * $lastExchangeRate['data']; + + } + } + + } + + $report['monthlySummary'] = $monthlySummary; + + break; + + case 'annually': + $vwBookingSummaryAll = vwBookingSummaryAll::where('time', '>', Carbon::parse($today)->firstOfYear()->toDateString()) + ->where('time', '<', Carbon::parse($today)->addYear()->firstOfYear()->toDateString()) + ->where('commission', '>', 0) + ->where('contract_user_id', $userId) + ->whereIn('property_id', $contractUserProperty) + ->get()->toArray(); + break; + } + + + if ($vwBookingSummaryAll) { + $dataCollect = collect($vwBookingSummaryAll); + + $dataCollectGroup = $dataCollect->groupBy('property_id')->toArray(); + foreach ($dataCollectGroup as $propertyId => $propertyList) { + + $property = reset($propertyList); + $report['data'][$propertyId]['id'] = $property['property_id']; + $report['data'][$propertyId]['name'] = $property['property_name']; + + $report['data'][$propertyId]['count'] = 0; + $report['data'][$propertyId]['total'] = 0; + $report['data'][$propertyId]['commission'] = 0; + + $dataCollectGroupCurrency = collect($propertyList)->groupBy('currency_code')->toArray(); + + foreach ($dataCollectGroupCurrency as $currencyCode => $currencyGroup) { + + $lastExchangeRate = $this->currencyService->lastExchangeRate($currencyCode, 'EUR'); + + $count = collect($currencyGroup)->count(); + $total = collect($currencyGroup)->sum('total'); + $commission = collect($currencyGroup)->sum('commission'); + + $report['data'][$propertyId]['count'] += $count; + $report['data'][$propertyId]['total'] += $total * $lastExchangeRate['data']; + $report['data'][$propertyId]['commission'] += $commission * $lastExchangeRate['data']; + + } + } + + $report['data'] = collect($report['data'])->sortByDesc('commission')->toArray(); + + $report['summary']['count'] = collect($report['data'])->sum('count'); + $report['summary']['total'] = collect($report['data'])->sum('total'); + $report['summary']['commission'] = collect($report['data'])->sum('commission'); + + } + + + return $report; + + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' START'); + + $today = Carbon::now()->subDay()->toDateString(); + + $userListIds = [904, 941, 1485];//41, 22, 883 + + $userListCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'whereIn' => [ + ['field' => 'id', 'value' => $userListIds] + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + $userList = $this->userService->select($userListCriteria); + + foreach ($userList['data'] as $user) { + + $contractUserProperty = vwActiveProperty::where('contract_user_id', $user['id']) + //->where('month', '>', '2024-10') + ->get()->toArray(); + + $contractUserProperty = $contractUserProperty ? pickItemFromArray('id',$contractUserProperty) : []; + + $this->info(date('Y-m-d H:i:s') . ' : '.$user['nameSurname']); + + $report['name'] = $user['nameSurname']; + $report['email'] = $user['email']; + + + //DAILY + $report['daily']['data'] = []; + $report['daily']['title'] = 'Daily Report'; + $report['daily']['type'] = 'daily'; + $report['daily']['period'] = Carbon::parse($today)->format('d.m.Y'); + + $reportCalculate = $this->reportCalculate($user['id'], $contractUserProperty, $report['daily']['type']); + + $report['daily']['data'] = $reportCalculate['data']; + $report['daily']['summary'] = $reportCalculate['summary']; + + + + + //MONTHLY + $report['monthly']['data'] = []; + $report['monthly']['title'] = 'Monthly Report'; + $report['monthly']['type'] = 'monthly'; + $report['monthly']['period'] = Carbon::parse($today)->firstOfMonth()->format('m.Y'); + + $reportCalculate = $this->reportCalculate($user['id'], $contractUserProperty, $report['monthly']['type']); + + $report['monthly']['data'] = $reportCalculate['data']; + $report['monthly']['summary'] = $reportCalculate['summary']; + $report['monthlySummary'] = $reportCalculate['monthlySummary']; + + + //ANNUALLY + $report['annually']['data'] = []; + $report['annually']['title'] = 'Annually Report'; + $report['annually']['type'] = 'annually'; + $report['annually']['period'] = Carbon::parse($today)->firstOfYear()->format('Y'); + + $reportCalculate = $this->reportCalculate($user['id'], $contractUserProperty, $report['annually']['type']); + + $report['annually']['data'] = $reportCalculate['data']; + $report['annually']['summary'] = $reportCalculate['summary']; + + $report['activeProperty'] = vwActiveProperty::where('commission', '>', 1) + ->where('contract_user_id', $user['id']) + ->whereIn('id', $contractUserProperty) + ->orderByDesc('id') + ->get()->toArray(); + + + //Property Price Comparison + $priceComparisonLink = null; + $todayComparison = Carbon::now()->toDateString(); + $firstDayOfWeek = Carbon::parse($todayComparison)->startOfWeek(Carbon::MONDAY); + //$firstDayOfWeek = Carbon::parse($today)->startOfWeek(Carbon::MONDAY)->toDateString(); + + //if(Carbon::now()->toDateString() == $firstDayOfWeek) { + $year = Carbon::parse($firstDayOfWeek)->format('Y'); + $week = Carbon::parse($firstDayOfWeek)->isoWeek(); + $queryKeyHash = md5($year . '-' . $week); + $priceComparisonLink = config('app.url').'/property-comparison/'.$queryKeyHash.'/'.$user['id']; + //} + $report['priceComparisonLink'] = $priceComparisonLink; + //Property Price Comparison + + + $this->mailer->onQueue('dailyReportSalesMail', new DailyReportMailSales($report)); + + + } + + //Hotel LIST + /*$vwActivePropertySummary = []; + $vwActiveProperty = vwActiveProperty::where('commission', '>', 1)->get()->toArray(); + $vwActivePropertySummary['count'] = count($vwActiveProperty); + $vwActivePropertySummary['groupByMonth'] = collect($vwActiveProperty)->sortByDesc('month')->groupBy('month')->toArray(); + $vwActivePropertySummary['lastMonth'] = collect($vwActivePropertySummary['groupByMonth'])->take(3)->toArray(); + //Hotel LIST + + $report['activeProperty'] = $vwActivePropertySummary;*/ + + + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/PropertyReviewService/PropertyReviewAnalyzeService.php b/app/Console/Commands/PropertyReviewService/PropertyReviewAnalyzeService.php new file mode 100644 index 0000000..df250f7 --- /dev/null +++ b/app/Console/Commands/PropertyReviewService/PropertyReviewAnalyzeService.php @@ -0,0 +1,239 @@ +restClient = new Client(['http_errors' => false]); + + } + + protected function requestService($params = []) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $client = new \GuzzleHttp\Client([ + 'max' => 5, + 'strict' => false, + 'referer' => false, + 'protocols' => ['https'], + 'timeout' => 30, + 'headers' => [ + 'Authorization' => 'Bearer ' . config('app.openAISecretKey'), + 'Content-Type' => 'application/json', + 'Cache-Control' => 'no-cache', + 'Connection' => 'keep-alive', + 'Accept-Encoding' => 'gzip' + ] + ] + ); + + + $result = $client->post('https://api.openai.com/v1/chat/completions', [ + 'body' => json_encode($params['query']) + ]); + + $result = $result->getBody()->getContents(); + $result = json_decode($result, 1); + + $choiceMessage = reset($result['choices']); + + if (isset($choiceMessage['message']['content'])) { + $choiceMessage = json_decode($choiceMessage['message']['content'], 1); + $data = $choiceMessage; + } + + $response = [ + 'status' => true, + 'data' => $data, + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return $response; + + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' START'); + + + //420 + + $propertyReview = PropertyReview::where('id', $this->argument('review_id')) + ->with('channel') + ->with('property') + ->first(); + + $propertyReview = $propertyReview ? $propertyReview->toArray() : null; + + if (!$propertyReview) { + Log::error('Property Review: ' . $propertyReview['property']['name'] . ' - ' . $propertyReview['channel']['name']); + return false; + } + + + $propertyReviewCategory = PropertyReviewCategory::where('status', 1)->select(['id', 'name'])->get()->toArray(); + $propertyReviewCategoryText = implode(', ', collect($propertyReviewCategory)->pluck('name')->toArray()); + + + $params['query'] = [ + 'model' => 'gpt-4o', + 'response_format' => [ + 'type' => 'json_object' + ], + 'messages' => [ + [ + 'role' => 'system', + 'content' => 'Sen bir otel gelir yönetimi uzmanısın ve otellere yapılan yorumları değerlendiriyosun. Cevaplar SADECE JSON formatında. Yorumu genel olarak değerlendirip, misafir gibi düşünüp 0: olumsuz,1: olumlu olacak şekilde sentiment alanında işaretleyelim. Gelen yorumları \"' . $propertyReviewCategoryText . '\" kategorilerine göre categories alanında işaretleyip gruplayalım. Her bir gruplanan kategori için de 0: olumsuz,1: olumlu olacak şekilde işaretleyelim, ilgisiz kategorileri eklemeyelim. keywords alanında bu yorum ile ilgili kilit kelimeleri, ileride kelime bulutu yapmak için ingilizce çıkaralım,kelimeler otel ve otelcilik ile ilgili olsun, kelimeler normal metin şeklinde, aralıkları boşluklu ve sadece ilk harfleri büyük olsun, bu kelimeler için de 0: olumsuz,1: olumlu olacak şekilde işaretleyelim. Yorumu da language parametresi altında 2 haneli ISO koduna göre verelim.' + ], + [ + 'role' => 'user', + 'content' => $propertyReview['review'] + ] + ] + + ]; + + $propertyReviewAnalyze = $this->requestService($params); + //$propertyReviewAnalyze = json_decode('{"status":true,"data":{"sentiment":0,"categories":{"Location":0,"Breakfast":0,"Service":1,"Cleanliness":1,"Room Facilities":0},"keywords":{"Location":0,"Breakfast":0,"Service Quality":1,"Cleaning":1,"Room Amenities":0}}}', 1); + + if ($propertyReviewAnalyze['status']) { + + + DB::beginTransaction(); + + $transactionSave = false; + + try { + + + $this->info(date('Y-m-d H:i:s') . ': Property Review: ' . $propertyReview['property']['name'] . ' - ' . $propertyReview['channel']['name']); + + PropertyReview::where('id', $propertyReview['id'])->update(['sentiment' => $propertyReviewAnalyze['data']['sentiment'], 'language' => $propertyReviewAnalyze['data']['language']]); + + //PropertyReviewCategoryMapping + $this->info(date('Y-m-d H:i:s') . ': Property Review Category Mapping: ' . $propertyReview['property']['name'] . ' - ' . $propertyReview['channel']['name']); + $bulkReviewCategoryMapping = []; + PropertyReviewCategoryMapping::where('review_id', $propertyReview['id'])->delete(); + foreach ($propertyReviewAnalyze['data']['categories'] as $category => $categorySentiment) { + + $categorySelected = collect($propertyReviewCategory)->where('name', $category)->first(); + + if (isset($categorySelected['id'])) { + + $bulkReviewCategoryMapping[] = [ + 'review_id' => $propertyReview['id'], + 'property_id' => $propertyReview['property_id'], + 'channel_id' => $propertyReview['channel_id'], + 'category_id' => $categorySelected['id'], + 'sentiment' => $categorySentiment, + 'review_date' => $propertyReview['review_date'], + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + } + } + + if (!empty($bulkReviewCategoryMapping)) { + PropertyReviewCategoryMapping::insert($bulkReviewCategoryMapping); + } + //PropertyReviewCategoryMapping + + + //PropertyReviewKeywordMapping + if (is_array($propertyReviewAnalyze['data']['keywords'])) { + $this->info(date('Y-m-d H:i:s') . ': Property Review Keyword Mapping: ' . $propertyReview['property']['name'] . ' - ' . $propertyReview['channel']['name']); + $bulkReviewKeywordMapping = []; + PropertyReviewKeywordMapping::where('review_id', $propertyReview['id'])->delete(); + foreach ($propertyReviewAnalyze['data']['keywords'] as $keyword => $keywordSentiment) { + + $bulkReviewKeywordMapping[] = [ + 'review_id' => $propertyReview['id'], + 'property_id' => $propertyReview['property_id'], + 'channel_id' => $propertyReview['channel_id'], + 'keyword' => $keyword, + 'sentiment' => $keywordSentiment, + 'review_date' => $propertyReview['review_date'], + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + } + + if (!empty($bulkReviewKeywordMapping)) { + PropertyReviewKeywordMapping::insert($bulkReviewKeywordMapping); + } + } + //PropertyReviewKeywordMapping + + + //throw new Exception('Booking Room could not be made'); + + $transactionSave = true; + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $transactionSave = false; + } + + if ($transactionSave) { + DB::commit(); + } else { + DB::rollBack(); + } + } + + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/PropertyReviewService/PropertyReviewScheduleService.php b/app/Console/Commands/PropertyReviewService/PropertyReviewScheduleService.php new file mode 100644 index 0000000..eb88d54 --- /dev/null +++ b/app/Console/Commands/PropertyReviewService/PropertyReviewScheduleService.php @@ -0,0 +1,62 @@ +info(date('Y-m-d H:i:s') . ' START'); + + + $propertyReviewChannelMapping = PropertyReviewChannelMapping::where('status', 1) + ->with('channel') + ->with('property') + ->get(); + + $propertyReviewChannelMapping = $propertyReviewChannelMapping ? $propertyReviewChannelMapping->toArray() : null; + + foreach ($propertyReviewChannelMapping as $propertyReview) { + + if (Carbon::createFromTimestamp($propertyReview['created_at'])->dayName != Carbon::now()->dayName) { + continue; + } + + $this->info(date('Y-m-d H:i:s') . ' Property Review: ' . $propertyReview['property']['name'] . ' - ' . $propertyReview['channel']['name']); + + dispatch(new PropertyReviewServiceJob($propertyReview['property_id'], $propertyReview['channel_id'])); + + } + + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/PropertyReviewService/PropertyReviewService.php b/app/Console/Commands/PropertyReviewService/PropertyReviewService.php new file mode 100644 index 0000000..06e2b31 --- /dev/null +++ b/app/Console/Commands/PropertyReviewService/PropertyReviewService.php @@ -0,0 +1,738 @@ +restClient = new Client(['http_errors' => false]); + + } + + protected function requestService($params) + { + + try { + + $requestUrl = $params['requestUrl']; + $requestParams['headers']['x-api-key'] = $this->xApiKey; + $requestParams['headers']['Content-Type'] = 'application/json'; + + unset($params['requestUrl']); + $requestParams['query'] = $params; + + $result = $this->restClient->request('GET', $requestUrl, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + //$getResponseData = json_decode(file_get_contents(storage_path() . '/response.json'), 1); + //$getResponseData = json_decode(file_get_contents(storage_path() . '/google.json'), 1); + //$getResponseData = json_decode(file_get_contents(storage_path() . '/Tripadvisor.json'), 1); + //$getResponseData = json_decode(file_get_contents(storage_path() . '/Tripadvisor.json'), 1); + + if (isset($getResponseData['error'])) { + throw new ApiErrorException($getResponseData['detail']); + } + + $response['status'] = true; + $response['data'] = $getResponseData; + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + dd($response); + + } + + return $response; + + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' START'); + + $propertyReviewChannelMapping = PropertyReviewChannelMapping::where('property_id', $this->argument('property_id')) + ->where('channel_id', $this->argument('channel_id')) + ->with('channel') + ->with('property') + ->first(); + + $propertyReviewChannelMapping = $propertyReviewChannelMapping ? $propertyReviewChannelMapping->toArray() : null; + + Log::debug('Property Review: ' . $propertyReviewChannelMapping['property']['name'] . ' - ' . $propertyReviewChannelMapping['channel']['name']); + + switch ($propertyReviewChannelMapping['channel_id']) { + + //BookingCom + case '1': + + $this->info(date('Y-m-d H:i:s') . ' START : ' . $propertyReviewChannelMapping['property']['name'] . ' - ' . $propertyReviewChannelMapping['channel']['name']); + + $maxPageSize = 1; + + if ($propertyReviewChannelMapping['fetch_status'] == 3) { + $maxPageSize = $this->maxPageSize; + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 2]); + } + + + for ($i = 0; $i < $maxPageSize; $i++) { + + + $requestParams = [ + 'requestUrl' => 'https://api.stayapi.com/v1/booking/hotel/reviews', + 'hotel_id' => $propertyReviewChannelMapping['parameterArray']['hotelId'], + 'sort' => 'newest', + 'per_page' => 20, + 'page' => ($i + 1) + ]; + + $requestReview = $this->requestService($requestParams); + + //$bulkReview = []; + if ($requestReview['status']) { + + foreach ($requestReview['data']['data']['reviews'] as $review) { + + try { + + if (empty($review['review']['positive']) && empty($review['review']['negative'])) { + continue; + } + + $bulkReview = [ + 'property_id' => $propertyReviewChannelMapping['property_id'], + 'channel_id' => $propertyReviewChannelMapping['channel_id'], + 'channel_review_id' => $review['id'], + 'author' => $review['guest']['name'], + 'title' => fillOnUndefined($review['review'], 'title'), + 'review' => $review['review']['positive'] . ' ' . $review['review']['negative'], + 'review_date' => Carbon::parse($review['reviewed_date'])->toDateTimeString(), + 'rating' => $review['score'], + 'top_rating' => 10, + 'score' => number_format($review['score'] / 10 * 100, 2), + 'language' => $review['review']['language'], + 'detail' => json_encode($review), + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + $propertyReviewCheck = PropertyReview::where('property_id', $bulkReview['property_id']) + ->where('channel_id', $bulkReview['channel_id']) + ->where('channel_review_id', $bulkReview['channel_review_id']) + ->first(); + + if (!$propertyReviewCheck) { + $propertyReviewCreate = PropertyReview::create($bulkReview); + + dispatch(new PropertyReviewAnalyzeServiceJob($propertyReviewCreate['id'])); + + $this->info(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } else { + $this->error(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + + //dd($message); + } + + } + + } + + + } + + + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 1]); + + break; + + //Google + case '2': + + $this->info(date('Y-m-d H:i:s') . ' START : ' . $propertyReviewChannelMapping['property']['name'] . ' - ' . $propertyReviewChannelMapping['channel']['name']); + + $maxPageSize = 1; + if ($propertyReviewChannelMapping['fetch_status'] == 3) { + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 2]); + } + + + for ($i = 0; $i < $maxPageSize; $i++) { + + $requestParams = [ + 'requestUrl' => 'https://api.stayapi.com/v1/google_reviews/reviews-paginated', + 'data_id' => $propertyReviewChannelMapping['parameterArray']['dataId'], + 'sort_by' => 'newest', + 'pages' => 10 + ]; + + $requestReview = $this->requestService($requestParams); + + + if ($requestReview['status']) { + + foreach ($requestReview['data']['reviews'] as $review) { + + if (empty($review['text'])) { + continue; + } + + try { + + $bulkReview = [ + 'property_id' => $propertyReviewChannelMapping['property_id'], + 'channel_id' => $propertyReviewChannelMapping['channel_id'], + 'channel_review_id' => $review['review_id'], + 'author' => $review['reviewer']['name'], + 'title' => null, + 'review' => $review['text'], + 'review_date' => Carbon::parse($review['iso_date'])->toDateTimeString(), + 'rating' => $review['rating'], + 'top_rating' => 5, + 'score' => number_format($review['rating'] / 5 * 100, 2), + 'language' => null, + 'detail' => json_encode($review), + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + $propertyReviewCheck = PropertyReview::where('property_id', $bulkReview['property_id']) + ->where('channel_id', $bulkReview['channel_id']) + ->where('channel_review_id', $bulkReview['channel_review_id']) + ->first(); + + if (!$propertyReviewCheck) { + $propertyReviewCreate = PropertyReview::create($bulkReview); + + dispatch(new PropertyReviewAnalyzeServiceJob($propertyReviewCreate['id'])); + + $this->info(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } else { + $this->error(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + + //dd($message); + } + + } + + } + + + } + + + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 1]); + + break; + + //Tripadvisor + case '3': + + $this->info(date('Y-m-d H:i:s') . ' START : ' . $propertyReviewChannelMapping['property']['name'] . ' - ' . $propertyReviewChannelMapping['channel']['name']); + + $maxPageSize = 1; + + if ($propertyReviewChannelMapping['fetch_status'] == 3) { + $maxPageSize = $this->maxPageSize; + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 2]); + } + + + for ($i = 0; $i < $maxPageSize; $i++) { + + $requestParams = [ + 'requestUrl' => 'https://api.stayapi.com/v1/tripadvisor/hotel/reviews/' . $propertyReviewChannelMapping['parameterArray']['hotelId'], + 'per_page' => 20, + 'page' => ($i + 1) + ]; + + $requestReview = $this->requestService($requestParams); + + if ($requestReview['status']) { + + foreach ($requestReview['data']['reviews'] as $review) { + + if (!fillOnUndefined($review, 'text')) { + continue; + } + + try { + + $bulkReview = [ + 'property_id' => $propertyReviewChannelMapping['property_id'], + 'channel_id' => $propertyReviewChannelMapping['channel_id'], + 'channel_review_id' => $review['id'], + 'author' => $review['user']['display_name'], + 'title' => fillOnUndefined($review, 'title'), + 'review' => fillOnUndefined($review, 'text'), + 'review_date' => Carbon::parse($review['created_date'])->toDateTimeString(), + 'rating' => $review['rating'], + 'top_rating' => 5, + 'score' => number_format($review['rating'] / 5 * 100, 2), + 'language' => fillOnUndefined($review, 'language'), + 'detail' => json_encode($review), + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + $propertyReviewCheck = PropertyReview::where('property_id', $bulkReview['property_id']) + ->where('channel_id', $bulkReview['channel_id']) + ->where('channel_review_id', $bulkReview['channel_review_id']) + ->first(); + + if (!$propertyReviewCheck) { + $propertyReviewCreate = PropertyReview::create($bulkReview); + + dispatch(new PropertyReviewAnalyzeServiceJob($propertyReviewCreate['id'])); + + $this->info(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } else { + $this->error(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + + //dd($message); + } + + } + + } + + + } + + + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 1]); + + break; + + //Agoda + case '4': + + $this->info(date('Y-m-d H:i:s') . ' START : ' . $propertyReviewChannelMapping['property']['name'] . ' - ' . $propertyReviewChannelMapping['channel']['name']); + + $maxPageSize = 1; + + if ($propertyReviewChannelMapping['fetch_status'] == 3) { + $maxPageSize = $this->maxPageSize; + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 2]); + } + + + for ($i = 0; $i < $maxPageSize; $i++) { + + $requestParams = [ + 'requestUrl' => 'https://api.stayapi.com/v1/agoda/hotel/reviews/' . $propertyReviewChannelMapping['parameterArray']['hotelId'], + 'page' => ($i + 1) + ]; + + $requestReview = $this->requestService($requestParams); + + + if ($requestReview['status']) { + + foreach ($requestReview['data']['reviews'] as $review) { + + if (!fillOnUndefined($review, 'comment')) { + continue; + } + + try { + + $bulkReview = [ + 'property_id' => $propertyReviewChannelMapping['property_id'], + 'channel_id' => $propertyReviewChannelMapping['channel_id'], + 'channel_review_id' => $review['review_id'], + 'author' => null, + 'title' => fillOnUndefined($review, 'title'), + 'review' => fillOnUndefined($review, 'comment'), + 'review_date' => Carbon::parse($review['date'])->toDateTimeString(), + 'rating' => $review['rating'], + 'top_rating' => 10, + 'score' => number_format($review['rating'] / 10 * 100, 2), + 'language' => fillOnUndefined($review, 'language'), + 'detail' => json_encode($review), + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + $propertyReviewCheck = PropertyReview::where('property_id', $bulkReview['property_id']) + ->where('channel_id', $bulkReview['channel_id']) + ->where('channel_review_id', $bulkReview['channel_review_id']) + ->first(); + + if (!$propertyReviewCheck) { + $propertyReviewCreate = PropertyReview::create($bulkReview); + + dispatch(new PropertyReviewAnalyzeServiceJob($propertyReviewCreate['id'])); + + $this->info(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } else { + $this->error(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + + //dd($message); + } + + } + + } + + + } + + + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 1]); + + break; + + //Trip.com + case '5': + + $this->info(date('Y-m-d H:i:s') . ' START : ' . $propertyReviewChannelMapping['property']['name'] . ' - ' . $propertyReviewChannelMapping['channel']['name']); + + $maxPageSize = 1; + + if ($propertyReviewChannelMapping['fetch_status'] == 3) { + $maxPageSize = $this->maxPageSize; + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 2]); + } + + + for ($i = 0; $i < $maxPageSize; $i++) { + + $requestParams = [ + 'requestUrl' => 'https://api.stayapi.com/v1/tripcom/hotel/reviews/' . $propertyReviewChannelMapping['parameterArray']['hotelId'], + 'page' => ($i + 1), + 'page_size' => 50 + ]; + + $requestReview = $this->requestService($requestParams); + + if ($requestReview['status']) { + + foreach ($requestReview['data']['data']['reviews'] as $review) { + + if (!fillOnUndefined($review, 'text')) { + continue; + } + + /* if ($review['source'] != 1) { + continue; + }*/ + + try { + + $bulkReview = [ + 'property_id' => $propertyReviewChannelMapping['property_id'], + 'channel_id' => $propertyReviewChannelMapping['channel_id'], + 'channel_review_id' => $review['id'], + 'author' => isset($review['reviewer_name']) ? $review['reviewer_name'] : null, + 'title' => fillOnUndefined($review, 'title'), + 'review' => fillOnUndefined($review, 'text'), + 'review_date' => Carbon::parse($review['date'])->toDateTimeString(), + 'rating' => $review['rating'], + 'top_rating' => 10, + 'score' => number_format($review['rating'] / 10 * 100, 2), + 'language' => fillOnUndefined($review, 'language'), + 'detail' => json_encode($review), + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + + $propertyReviewCheck = PropertyReview::where('property_id', $bulkReview['property_id']) + ->where('channel_id', $bulkReview['channel_id']) + ->where('channel_review_id', $bulkReview['channel_review_id']) + ->first(); + + if (!$propertyReviewCheck) { + $propertyReviewCreate = PropertyReview::create($bulkReview); + + dispatch(new PropertyReviewAnalyzeServiceJob($propertyReviewCreate['id'])); + + $this->info(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } else { + $this->error(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + + //dd($message); + } + + } + + } + + + } + + + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 1]); + + break; + + //Expedia.com + case '6': + + $this->info(date('Y-m-d H:i:s') . ' START : ' . $propertyReviewChannelMapping['property']['name'] . ' - ' . $propertyReviewChannelMapping['channel']['name']); + + $maxPageSize = 1; + + if ($propertyReviewChannelMapping['fetch_status'] == 3) { + $maxPageSize = $this->maxPageSize; + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 2]); + } + + + for ($i = 0; $i < $maxPageSize; $i++) { + + $requestParams = [ + 'requestUrl' => 'https://api.stayapi.com/v1/expedia/hotel/reviews', + 'property_id' => $propertyReviewChannelMapping['parameterArray']['hotelId'], + 'page' => $i, + 'page_size' => 5 + ]; + + $requestReview = $this->requestService($requestParams); + + if ($requestReview['status']) { + + foreach ($requestReview['data']['reviews'] as $review) { + + if (!fillOnUndefined($review, 'text')) { + continue; + } + + try { + + preg_match('/(\d+)\s*\/\s*(\d+)/', $review['rating'], $matches); + + $bulkReview = [ + 'property_id' => $propertyReviewChannelMapping['property_id'], + 'channel_id' => $propertyReviewChannelMapping['channel_id'], + 'channel_review_id' => $review['id'], + 'author' => isset($review['reviewer_name']) ? $review['reviewer_name'] : null, + 'title' => fillOnUndefined($review, 'title'), + 'review' => fillOnUndefined($review, 'text'), + 'review_date' => Carbon::parse($review['review_date'])->toDateTimeString(), + 'rating' => (int)$matches[1], + 'top_rating' => 10, + 'score' => number_format((int)$matches[1] / 10 * 100, 2), + 'language' => fillOnUndefined($review, 'language'), + 'detail' => json_encode($review), + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + + $propertyReviewCheck = PropertyReview::where('property_id', $bulkReview['property_id']) + ->where('channel_id', $bulkReview['channel_id']) + ->where('channel_review_id', $bulkReview['channel_review_id']) + ->first(); + + if (!$propertyReviewCheck) { + $propertyReviewCreate = PropertyReview::create($bulkReview); + + dispatch(new PropertyReviewAnalyzeServiceJob($propertyReviewCreate['id'])); + + $this->info(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } else { + $this->error(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + + //dd($message); + } + + } + + } + + + } + + + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 1]); + + break; + + //HolidayCheck + case '7': + + $this->info(date('Y-m-d H:i:s') . ' START : ' . $propertyReviewChannelMapping['property']['name'] . ' - ' . $propertyReviewChannelMapping['channel']['name']); + + $maxPageSize = 1; + + if ($propertyReviewChannelMapping['fetch_status'] == 3) { + $maxPageSize = $this->maxPageSize; + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 2]); + } + + + for ($i = 0; $i < $maxPageSize; $i++) { + + $hotelId = $propertyReviewChannelMapping['parameterArray']['hotelId']; + $limit = 10; + $offset = $i * $limit; + + $requestParams = [ + 'requestUrl' => "https://www.holidaycheck.de/api/hotel-reviews;filter=hotel.id:{$hotelId};limit={$limit};offset={$offset};select=id,user,title,texts,textAdvice,hotel,travelDate,recommendation,ratings,ranking,proofedReservation,ownerComment,additional,entryDate;sort=entryDate:desc", + ]; + + $requestReview = $this->requestService($requestParams); + + if ($requestReview['status']) { + + $reviews = isset($requestReview['data']['data']['items']) ? $requestReview['data']['data']['items'] : []; + + foreach ($reviews as $review) { + + $reviewText = fillOnUndefined($review, 'textAdvice'); + if (empty($reviewText)) { + $reviewText = fillOnUndefined($review, 'texts.GENERAL'); + } + + if (empty($reviewText)) { + continue; + } + + try { + + $rating = fillOnUndefined($review, 'ratings.GENERAL.GENERAL'); + + $author = fillOnUndefined($review, 'user.firstName'); + if (empty($author)) { + $author = fillOnUndefined($review, 'user.name'); + } + + $bulkReview = [ + 'property_id' => $propertyReviewChannelMapping['property_id'], + 'channel_id' => $propertyReviewChannelMapping['channel_id'], + 'channel_review_id' => fillOnUndefined($review, 'id'), + 'author' => $author, + 'title' => fillOnUndefined($review, 'title'), + 'review' => $reviewText, + 'review_date' => Carbon::createFromTimestampMs(fillOnUndefined($review, 'entryDate'))->toDateTimeString(), + 'rating' => $rating, + 'top_rating' => 6, + 'score' => number_format($rating / 6 * 100, 2), + 'language' => fillOnUndefined($review, 'originalLocale'), + 'detail' => json_encode($review), + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + $propertyReviewCheck = PropertyReview::where('property_id', $bulkReview['property_id']) + ->where('channel_id', $bulkReview['channel_id']) + ->where('channel_review_id', $bulkReview['channel_review_id']) + ->first(); + + if (!$propertyReviewCheck) { + $propertyReviewCreate = PropertyReview::create($bulkReview); + + dispatch(new PropertyReviewAnalyzeServiceJob($propertyReviewCreate['id'])); + + $this->info(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } else { + $this->error(date('Y-m-d H:i:s') . ' - ' . $bulkReview['channel_review_id']); + } + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + + //dd($message); + } + + } + + } + + + } + + + PropertyReviewChannelMapping::where('id', $propertyReviewChannelMapping['id'])->update(['fetch_status' => 1]); + + break; + } + + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/PropertyTrialCheck/PropertyTrialCheck.php b/app/Console/Commands/PropertyTrialCheck/PropertyTrialCheck.php new file mode 100644 index 0000000..c2f9b56 --- /dev/null +++ b/app/Console/Commands/PropertyTrialCheck/PropertyTrialCheck.php @@ -0,0 +1,125 @@ +propertyService = $propertyService; + $this->productMappingService = $productMappingService; + } + + public function handle() + { + + $response = ['status' => false, 'message' => '']; + + $propertySelectCriteria = [ + 'criteria' => [ + //['field' => 'id', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'commission', 'condition' => '=', 'value' => null], + ['field' => 'created_at', 'condition' => '>', 'value' => Carbon::parse('2021-10-01')->timestamp], + + ], + 'with' => ['propertyProductMapping'], + 'orderBy' => [ + ["field" => "id", "value" => "ASC"] + ], + 'take' => 100 + ]; + + $propertyData = $this->propertyService->select($propertySelectCriteria); + + if ($propertyData['status'] == 'success') { + + foreach ($propertyData['data'] as $property) { + + + if (empty($property['property_product_mapping'])) { + continue; + } + + $expireDay = Carbon::now()->diff(Carbon::createFromTimestamp($property['created_at']))->days; + + $this->info(date('Y-m-d H:i:s') . ' Property: ' . $property['id'] . ' - ' . $property['name'] . ' - ' . $expireDay. ' Day'); + + if (in_array($expireDay, [30])) { + + DB::beginTransaction(); + + try { + + $this->info(date('Y-m-d H:i:s') . ' : Property: ' . $property['id'] . ' - ' . $property['name']); + + //Product remove from property + foreach ($property['property_product_mapping'] as $productMapping) { + $this->productMappingService->update($productMapping['id'], ['status' => 0]); + } + + //Trial period status + $propertyData = $this->propertyService->update($property['id'], ['status' => 2]); + + //Burada otel user larına mail atılabilir, trial süresi biti diye + + $response['status'] = true; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + } + + + //Expiration Check + //Product remove from property via expiration date + foreach ($property['property_product_mapping'] as $productMapping) { + + if($productMapping['status'] == 1 && !is_null($productMapping['expiration_date'])) { + if(Carbon::now()->greaterThanOrEqualTo(Carbon::parse($productMapping['expiration_date']))) { + $this->productMappingService->update($productMapping['id'], ['status' => 3]);} + + } + + } + + + + } + + + } + + } +} diff --git a/app/Console/Commands/PropertyTrialCheck/PropertyTrialMail.php b/app/Console/Commands/PropertyTrialCheck/PropertyTrialMail.php new file mode 100644 index 0000000..3c67bb8 --- /dev/null +++ b/app/Console/Commands/PropertyTrialCheck/PropertyTrialMail.php @@ -0,0 +1,83 @@ +mailer = $mailer; + $this->propertyService = $propertyService; + } + + public function handle() + { + + $this->info(date('Y-m-d H:i:s') . ' START'); + + $propertySelectCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'commission', 'condition' => '=', 'value' => null], + + ], + 'orderBy' => [ + ["field" => "id", "value" => "ASC"] + ], + ]; + + $propertyData = $this->propertyService->select($propertySelectCriteria); + + if ($propertyData['status'] == 'success') { + + foreach ($propertyData['data'] as $property) { + + $createDate = Carbon::createFromTimestamp($property['created_at'])->toDateTimeString(); + $expireDay = Carbon::now()->diff(Carbon::createFromTimestamp($property['created_at']))->days; + + if (in_array($expireDay, [4, 10])) { + + if ($expireDay == 4) { + $this->mailer->onQueue('trialFirstMail', new TrialFirstMail(['propertyId' => $property['id']])); + $this->info(date('Y-m-d H:i:s') . ' SENT Day 4: ' . $property['id'] . ' - ' . $property['name'] . ' - ' . $createDate); + } + + if ($expireDay == 10) { + $this->mailer->onQueue('trialSecondMail', new TrialSecondMail(['propertyId' => $property['id']])); + $this->info(date('Y-m-d H:i:s') . ' SENT Day 10: ' . $property['id'] . ' - ' . $property['name'] . ' - ' . $createDate); + } + + + } + + } + + } + + $this->info(date('Y-m-d H:i:s') . ' FINISHED'); + + + } +} diff --git a/app/Console/Commands/PropertyWebCheckDns/PropertyWebCheckDns.php b/app/Console/Commands/PropertyWebCheckDns/PropertyWebCheckDns.php new file mode 100644 index 0000000..7ce49c8 --- /dev/null +++ b/app/Console/Commands/PropertyWebCheckDns/PropertyWebCheckDns.php @@ -0,0 +1,57 @@ +propertyWebService = $propertyWebService; + parent::__construct(); + } + + public function handle() + { + + + try { + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' Check DNS Status start'); + $process = $this->propertyWebService->checkDnsStatus(); + if($process['status'] != 'success'){ + throw new Exception($process['message']) ; + + } + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' Check DNS Status finished'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + + + + + + + } + +} diff --git a/app/Console/Commands/PropertyWebMenuMapping/PropertyWebMenuMappingConsole.php b/app/Console/Commands/PropertyWebMenuMapping/PropertyWebMenuMappingConsole.php new file mode 100644 index 0000000..909d84a --- /dev/null +++ b/app/Console/Commands/PropertyWebMenuMapping/PropertyWebMenuMappingConsole.php @@ -0,0 +1,64 @@ +info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' Start'); + + $webMenus = PropertyWebMenu::all(); + $webs = PropertyWeb::all(); + + $datas = []; + foreach ($webs as $key => $web){ + foreach ($webMenus as $key1 => $menu){ + $datas[$key][$key1]['property_id'] = $web->property_id; + $datas[$key][$key1]['property_web_id'] = $web->id; + $datas[$key][$key1]['property_web_menu_id'] = $menu->id; + $datas[$key][$key1]['order_number'] = $menu->order_number; + $datas[$key][$key1]['status'] = 1; + $datas[$key][$key1]['created_by'] = 1; + $datas[$key][$key1]['updated_by'] = 1; + $datas[$key][$key1]['created_at'] = 1; + $datas[$key][$key1]['updated_at'] = 1; + } + } + + PropertyWebMenuMapping::insert(collect($datas)->flatten(1)->toArray()); + + $this->info(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' Finished'); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $this->error(date('Y-m-d H:i:s') . ' : ' . date('Y-m-d') . ' ERROR: ' . $message); + } + + } + +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php new file mode 100644 index 0000000..a2cf7e0 --- /dev/null +++ b/app/Console/Kernel.php @@ -0,0 +1,161 @@ +command('cron:mail-jobs') + ->withoutOverlapping() + ->everyMinute() + ->sendOutputTo(storage_path() . '/logs/cron-mail-jobs.log');*/ + + $schedule->command('cron:mail-jobs')->everyMinute(); + + $schedule->command('cron:currency-rates') + ->withoutOverlapping() + ->twiceDaily(2, 14) + //->everyMinute() + //->emailOutputTo('byumak@rezervasyon.com') + ->sendOutputTo(storage_path() . '/logs/cron-currency-rates.log'); + + $schedule->command('cron:roomrate-push-service')->everyMinute(); + $schedule->command('cron:roomavailability-push-service')->everyMinute(); + $schedule->command('cron:reservation-pull-service')->everyMinute(); + $schedule->command('cron:reservation-push-service')->everyMinute(); + + $schedule->command('cron:property-trial-check')->withoutOverlapping()->twiceDaily(9, 15); + $schedule->command('cron:property-trial-mail')->withoutOverlapping()->dailyAt('10:00'); + + //$schedule->command('queue:work')->everyMinute()->withoutOverlapping(); + $schedule->command('queue:restart')->hourly()->withoutOverlapping(); + + $schedule->command('cron:summary-report-mail')->dailyAt('00:05'); + $schedule->command('cron:summary-report-mail-sales')->dailyAt('09:00'); + $schedule->command('cron:remove-payment-token-service')->withoutOverlapping()->dailyAt('02:00'); + $schedule->command('cron:bookingengine-search-report-mail')->withoutOverlapping()->dailyAt('09:00'); + + $schedule->command('cron:propertymeta-roomrate-service')->withoutOverlapping()->dailyAt('03:00'); + $schedule->command('cron:propertymeta-roomrateprice-service')->withoutOverlapping()->dailyAt('05:00'); + $schedule->command('cron:propertymeta-roomratepush-service')->withoutOverlapping()->dailyAt('07:00'); + + //$schedule->command('cron:bar-sync-service')->withoutOverlapping()->cron('0 */2 * * *'); + $schedule->command('cron:bar-sync-service')->cron('0 */2 * * *'); + + $schedule->command('cron:price-comparison-service')->withoutOverlapping()->weekly()->mondays()->at('02:00'); + + $schedule->command('cron:commission-service')->everyTenMinutes(); + + $schedule->command('cron:weather')->withoutOverlapping()->twiceDaily(2, 14); + $schedule->command('cron:dashboard-cache')->withoutOverlapping()->dailyAt('03:00'); + $schedule->command('cron:google-static-map')->withoutOverlapping()->dailyAt('04:00'); + + $schedule->command('cron:google-review')->withoutOverlapping()->weekly()->mondays()->at('14:00'); + + $schedule->command('cron:property-invoice-service')->withoutOverlapping()->dailyAt('04:00'); + $schedule->command('cron:property-summary-service')->withoutOverlapping()->dailyAt('04:30'); + + $schedule->command('cron:property-review-schedule-service')->withoutOverlapping()->dailyAt('05:00'); + + $schedule->command('cron:meta-cancellation-service')->withoutOverlapping()->dailyAt('05:30'); + } +} diff --git a/app/Core/Contracts/MailInterface.php b/app/Core/Contracts/MailInterface.php new file mode 100644 index 0000000..533392b --- /dev/null +++ b/app/Core/Contracts/MailInterface.php @@ -0,0 +1,8 @@ +flip()->except($except)->flip()->toArray(); + } + + public static function getAllLangPackages() + { + $langPackages = []; + $validSystemLanguages = self::getValidLanguages(); + foreach ($validSystemLanguages as $perLanguage) + { + $langPackages[$perLanguage] = self::getCurrentLangPack($perLanguage); + } + return $langPackages; + } + + public static function convertAllLanguageSettingsAndCache() + { + $cacheParam = []; + $wordIndexCounter = 0; + $allLangPackages = self::getAllLangPackages(); + $systemDefaultLang = self::getDefaultLang(); + + foreach ($allLangPackages[$systemDefaultLang] as $perLangKey => $perLangWord) + { + $cacheParam[$wordIndexCounter]["md5"] = self::convertToMd5ForMultiLanguage($perLangWord); + + foreach ($allLangPackages as $perLangPackKey => $perLangPack) + { + $cacheParam[$wordIndexCounter][$perLangPackKey] = $perLangPack[$wordIndexCounter]; + } + $wordIndexCounter++; + } + + $cacheParam = collect($cacheParam)->keyBy("md5")->toArray(); + Cache::put(self::$languageSupportCacheName,$cacheParam,120); + return $cacheParam; + } + + public static function getLanguageSupportList() + { + $cachedData = self::$languageSupportList; + if ( !$cachedData) + { + $cachedData = Cache::get(self::$languageSupportCacheName); + self::$languageSupportList = $cachedData; + } + $cachedData = $cachedData ? $cachedData : self::convertAllLanguageSettingsAndCache(); + return $cachedData; + } + + + private static function makeSlugForMultiLanguage( $word ) + { + $word = lowerCase($word); + preg_match_all("#[\pL\d+]+#iu",$word,$results); + $results = $results[0]; + $converted = implode("",$results); + return $converted; + } + + public static function convertToMd5ForMultiLanguage($word) + { + /*Clear The Parameters*/ + $word = preg_replace('/\{\#\d+\}/',"",$word); + /**/ + /*Clear From Html*/ + $word = strip_tags($word); + /**/ + $word = self::makeSlugForMultiLanguage($word); + return md5($word); + } + + + public static function findWordForLanguage($word,$lang,array $parameters = []) + { + $md5Key = self::convertToMd5ForMultiLanguage($word); + $langSupportList = self::getLanguageSupportList(); + $converted = fillOnUndefined($langSupportList,$md5Key.".".$lang); + $converted = $converted ? $converted : fillOnUndefined($langSupportList,$md5Key.".".self::getDefaultLang()); + $converted = $converted ? $converted : $word; + + /*Inject Parameters*/ + $parameters = reIndexArrayKeys($parameters,1); + foreach ($parameters as $perParameterKey => $perParameter) + { + $converted = preg_replace('/\{\#'.$perParameterKey.'\}/',$perParameter,$converted); + } + /**/ + + return $converted; + } + + public static function lang ( $word,array $parameters = [],$lang = null ) + { + $lang = $lang ? : self::$currentLanguage; + return self::findWordForLanguage($word,$lang,$parameters); + } + + public static function langArrayStack ( array $langParamStack ) + { + $result = []; + $validCases = ["lower","title","upper"]; + + foreach ($langParamStack as $perLangParam) + { + $word = castToString(fillOnUndefined($perLangParam,"word")); + $parameters = fillOnUndefined($perLangParam,"parameters",[]); + $lang = castToString(fillOnUndefined($perLangParam,"lang")); + $case = lowerCase(castToString(fillOnUndefined($perLangParam,"case"))); + $case = in_array($case,$validCases) ? $case : null; + $caseFunction = $case ? camel_case("lang".$case."case") : "lang"; + $result[] = self::$caseFunction($word,$parameters,$lang); + } + + return $result; + } + + public static function langArray ( array $words, $lang = null ) + { + $result = []; + foreach ($words as $perWord) + { + $result[] = self::lang($perWord, [], $lang); + } + return $result; + } + + public static function langLowerCase($word,array $parameters = [],$lang = null) + { + $lang = $lang ? : self::getCurrentLang(); + $translated = self::lang($word, $parameters,$lang); + return $lang == "tr" ? lowerCase($translated) : mb_strtolower($translated); + } + + public static function langTitleCase($word,array $parameters = [],$lang = null) + { + $lang = $lang ? : self::getCurrentLang(); + $translated = self::lang($word, $parameters,$lang); + return $lang == "tr" ? uCase($translated) : mb_convert_case($translated,MB_CASE_TITLE); + } + + public static function langUpperCase($word,array $parameters = [],$lang = null) + { + $lang = $lang ? : self::getCurrentLang(); + $translated = self::lang($word, $parameters,$lang); + return $lang == "tr" ? upperCase($translated) : mb_strtoupper($translated); + } + + +} diff --git a/app/Core/Helper/PhpHelper.php b/app/Core/Helper/PhpHelper.php new file mode 100644 index 0000000..073b528 --- /dev/null +++ b/app/Core/Helper/PhpHelper.php @@ -0,0 +1,570 @@ +'; + print_r($input); + echo ''; + } + + /** + * Display the difference between two dates + * (30 years, 9 months, 25 days, 21 hours, 33 minutes, 3 seconds). + * + * @param string $start starting date + * @param string $end=false ending date + * + * @return string formatted date difference + */ + public static function dateDiff($start, $end = false) {$return = []; + + try { + $start = new DateTime($start); + $end = new DateTime($end); + $form = $start->diff($end); + } catch (Exception $e) { + return $e->getMessage(); + } + + $display = ['y' => 'Yıl', 'm' => 'Ay', 'd' => 'Gün', 'h' => 'Saat', 'i' => 'Dakika', 's' => 'Saniye']; + + foreach ($display as $key => $value) { + if ($form->$key > 0) { + $return[] = $form->$key . ' ' . $value; + } + } + + return implode($return, ', '); + } + + /** + * Display the day count between two dates + * (3 days). + * + * @param string $start starting date + * @param string $end=false ending date + * + * @return string day count + */ + public static function dayCount($end, $start) { + $return = []; + + try { + $start = new DateTime($start); + $end = new DateTime($end); + $form = $start->diff($end); + } catch (Exception $e) { + return $e->getMessage(); + } + + return $form->days; + } + + /** + * Display the day and month name + * (16 July). + * + * @param string $date date + * + * @return string formatted date + */ + public static function dayNumberAndMonthWord($date) { + return date('d', strtotime($date)) . ' ' . self::$months[date('m', strtotime($date)) - 1]; + } + + /** + * Display the month name + * (July). + * + * @param string $date date + * + * @return string formatted date + */ + public static function monthName($date) { + return self::$months[date('m', strtotime($date)) - 1]; + } + + /** + * Display the day name + * (Sunday). + * + * @param string $date date + * + * @return string formatted date + */ + public static function dayName($date) { + return self::$days[date('N', strtotime($date)) - 1]; + } + + /** + * Display the day number + * 01. + * + * @param string $date date + * + * @return string formatted date + */ + public static function dayNumber($date) { + return date('d', strtotime($date)); + } + + /** + * Display the human readable day + * 01. + * + * @param string $date date + * + * @return string formatted date + */ + public static function humanReadableDateWithDay($date) { + return date('d', strtotime($date)) . ' ' . self::$months[date('m', strtotime($date)) - 1] . ' ' . date('Y', strtotime($date)) . ', ' . self::dayName($date); + } + + /** + * Display the human readable day + * 01. + * + * @param string $date date + * + * @return string formatted date + */ + public static function humanReadableDate($date) { + return date('d-m-Y H:i:s', strtotime($date)); + } + + public static function humanReadableDateWithoutClock($date) { + return date('d-m-Y', strtotime($date)); + } + + /** + * @param string $date date + * + * @return string formatted date + */ + public static function sqlDate($date) { + $date = date('Y-m-d', strtotime($date)); + + return $date; + } + + public static function getMonth() { + return self::$months; + } + + public static function isValidDate($date) { + if (!is_string($date)) { + return false; + } + $day = substr($date, 0, 2); + $mont = substr($date, 3, 2); + $year = substr($date, 6, 4); + return checkdate($mont, $day, $year); + } + + public static function fillNullIfEmpty(&$variable) { + if (!isset($variable)) { + $variable = null; + } + return $variable; + } + + public static function getWeekDays() { + $days = self::$days; + $result = array(); + $counter = 1; + foreach ($days as $x) { + $result[$counter] = $x; + $counter++; + } + return $result; + } + + public static function parseXmlToArray($xmlSource) { + try { + $xmlOut = simplexml_load_string($xmlSource, 'SimpleXMLElement', LIBXML_NOCDATA); + + $arrayOut = json_decode(json_encode($xmlOut), true); + } catch (\Exception $e) { + $arrayOut = false; + } + return $arrayOut; + } + + public static function encryptCodeNumber($string, $key) { + $key = is_null($key) ? self::$encryptionKey : $key; + + $result = ''; + for ($i = 0; $i < mb_strlen($string, "UTF-8"); $i++) { + $char = mb_substr($string, $i, 1, "UTF-8"); + $keychar = mb_substr($key, ($i % mb_strlen($key, "UTF-8")) - 1, 1, "UTF-8"); + $char = chr(ord($char) + ord($keychar)); + $result.=$char; + } + $result = base64_encode($result); + $result = str_replace("=", "", $result); + return urlencode($result); + } + + public static function decryptCodeNumber($string, $key) { + $key = is_null($key) ? self::$encryptionKey : $key; + + $string = urldecode($string); + $eqcount = strlen($string) % 4; + if ($eqcount == 2) + $string . "=="; + else if ($eqcount == 3) + $string . "="; + $result = ''; + $string = base64_decode($string); + for ($i = 0; $i < mb_strlen($string); $i++) { + $char = mb_substr($string, $i, 1); + $keychar = mb_substr($key, ($i % mb_strlen($key)) - 1, 1); + $char = chr(ord($char) - ord($keychar)); + $result.=$char; + } + return $result; + } + + public static function generateRandomString($length = 5) { + $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charactersLength = strlen($characters); + $randomString = ''; + for ($i = 0; $i < $length; $i++) { + $randomString .= $characters[rand(0, $charactersLength - 1)]; + } + return $randomString; + } + + + public static function convertArrayToCsv (array $valueArray ) + { + if ( !$valueArray ) + { + return false; + } + $csvResult = ""; + + $valueArray = singleElementArray($valueArray); + + /*Get The Column Names*/ + $firstArray = reset($valueArray); + + $csvResult.=implode(",",array_keys($firstArray))."\r\n"; + + foreach ($valueArray as $perArray) + { + $csvResult.=implode(",",$perArray)."\r\n"; + } + return $csvResult; + } + + private static function nextExcelColName ( $colName = null ) + { + $primary = range("A","Z"); + if ( !$colName) + { + return reset($primary); + } + + if(last($primary) == $colName) + { + return reset($primary); + } + + return $primary[array_search($colName,$primary)+1]; + + } + + /* If $params[keyNames] is Empty Function Gonna Take All The Keys Of The Array */ + public static function convertArrayToXls (array $valueArray,array $params = [] ) + { + try + { + $excel = App::make('excel'); + $params[ "fileName" ] = isset( $params[ "fileName" ] ) ? $params[ "fileName" ] : "xls_file"; + $params[ "pagePrefix" ] = isset( $params[ "pagePrefix" ] ) ? $params[ "pagePrefix" ] : "Page"; + $params[ "onlyTheseKeys" ] = isset( $params[ "onlyTheseKeys" ] ) ? $params[ "onlyTheseKeys" ] : []; + $params[ "keyNames" ] = isset( $params[ "keyNames" ] ) ? $params[ "keyNames" ] : []; + $result = $excel->create ( $params[ "fileName" ], function ( $excel ) use ( $valueArray, $params ) + { + $excel->sheet ( $params[ "pagePrefix" ], function ( $sheet ) use ( $valueArray, $params) + { + $rowCounter = 1; + $sheet->setOrientation ( 'landscape' ); + if ($params["keyNames"]) + { + $colCounter = 0; + + foreach ($params["keyNames"] as $perKey) + { + if ($params["onlyTheseKeys"] && !in_array($perKey,$params["onlyTheseKeys"])) + { + continue; + } + $sheet->setCellValueByColumnAndRow ( $colCounter, $rowCounter, $perKey ); + $style = array( + 'alignment' => array( + 'horizontal' => "center", + ) + ); + + $sheet->getDefaultStyle()->applyFromArray($style); + $colCounter++; + } + $rowCounter++; + } + + foreach ($valueArray as $perValue) + { + $colCounter = 0; + foreach ($perValue as $perCol) + { + if ($params["onlyTheseKeys"] && !in_array($perKey,$params["onlyTheseKeys"])) + { + continue; + } + if (is_array($perCol)) + { + $perCol = json_encode($perCol); + } + $sheet->setCellValueByColumnAndRow ( $colCounter, $rowCounter, $perCol ); + $colCounter++; + } + $rowCounter++; + } + + } ); + + }); + return $result; + } catch ( Exception $e ) + { + return null; + } + + } + + public static function singleElementArray ( $arrayElement,array $extraParams = null) + { + $isMultiArray = true; + + if ( !is_array($arrayElement)) + { + return []; + } + + foreach ($arrayElement as $perItem) + { + if (gettype($perItem)!="array") + { + $isMultiArray = false; + break; + } + } + + if(!$isMultiArray) + { + $isMultiArray = true; + foreach (array_keys ( $arrayElement ) as $perKey) + { + if (!is_numeric($perKey)) + { + $isMultiArray = false; + break; + } + } + } + + + return $isMultiArray?$arrayElement:array($arrayElement); + + } + + public static function checkWizardMenu($wizardArray, $menuCode) + { + return array_filter($wizardArray, function ($key) use ($menuCode) { + return $key['menu_code'] == $menuCode ? true : false; + }, ARRAY_FILTER_USE_BOTH); + } + + + /*If its an array enter the index of the array to $index variable*/ + public static function fillOnUndefined($variable,$index = null,$fillWith = null,$arrayDelimiter = ".") + { + if ( !$arrayDelimiter) + { + return null; + } + + $index = trim($index,$arrayDelimiter); + + if ( !$index && $index !== '0') + { + return false; + } + + $indexes = explode($arrayDelimiter,$index); + + $arrayDeep = $variable; + + foreach ($indexes as $perIndex) + { + if ( !isset($arrayDeep[$perIndex])) + { + return $fillWith; + } + + $arrayDeep = $arrayDeep[$perIndex]; + } + + return $arrayDeep; + } + public static function hexToBinary ( $hex ) + { + if ( strlen($hex)<=0) + { + return null; + } + $str = ""; + for($i=0;$i $perValue) + { + if (isset($perValue[$index])) + { + $result[] = $perValue[$index]; + } + + } + return $result; + } catch ( Exception $e ) + { + return []; + } + } + + public static function pickNodeFromArray($index, $value, array $targetArray) + { + try { + $result = []; + foreach ($targetArray as $perIndex => $perValue) { + if (isset($perValue[$index]) && $perValue[$index] == $value) { + $result = $perValue; + break; + } + } + return $result; + } catch (Exception $e) { + return []; + } + } + + public static function numberSortTurkish($number) + { + $returnString = ''; + $numberValue = [ + 'inci' => [1,5,8,20,70,80], + 'üncü' => [3,4,100], + 'nci' => [2,7,50], + 'ıncı' => [0,40,60,90], + 'ncı' => [6], + 'uncu' => [9,10,30] + ]; + + if(strlen($number) < 4) { + if ($number % 10 == 0) { + $numberKey = $number; + } else $numberKey = substr($number, -1,1); + } + + foreach ($numberValue as $orderKey => $numbers) { + if (array_search($numberKey, $numbers) !== false) { + $returnString = $orderKey; + } + } + + return $returnString; + + } + + public static function now ( array $param = [] ) + { + return date("Y-m-d H:i:s"); + } + + public static function todayDate ( array $param = [] ) + { + return date("Y-m-d"); + } + + public static function moneyFormatWithTwoDecimals($money) + { + $money = str_replace(",","",$money); + return number_format($money,2,'.',''); + } + + public static function getXmlResponse ($view,$data) + { + $responseObj = App::make("Response"); + return $responseObj::view($view,$data)->header('Content-Type', 'text/xml'); + } + + public static function preDie($args) + { + + echo '

';
+            print_r($args);
+            echo '
'; + + die; + } +} diff --git a/app/Core/Helper/compat_l5.php b/app/Core/Helper/compat_l5.php new file mode 100644 index 0000000..207d1f6 --- /dev/null +++ b/app/Core/Helper/compat_l5.php @@ -0,0 +1,264 @@ +getConfigurationPath(rtrim($path, ".php")); + } +} + +if(!function_exists('public_path')) +{ + + /** + * Return the path to public dir + * @param null $path + * @return string + */ + function public_path($path=null) + { + return rtrim(app()->basePath('public/'.$path), '/'); + } +} + +if(!function_exists('storage_path')) +{ + + /** + * Return the path to storage dir + * @param null $path + * @return string + */ + function storage_path($path=null) + { + return app()->storagePath($path); + } +} + +if(!function_exists('database_path')) +{ + + /** + * Return the path to database dir + * @param null $path + * @return string + */ + function database_path($path=null) + { + return app()->databasePath($path); + } +} + +if(!function_exists('resource_path')) +{ + + /** + * Return the path to resource dir + * @param null $path + * @return string + */ + function resource_path($path=null) + { + return app()->resourcePath($path); + } +} + +if(!function_exists('lang_path')) +{ + + /** + * Return the path to lang dir + * @param null $str + * @return string + */ + function lang_path($path=null) + { + return app()->getLanguagePath($path); + } +} + +if ( ! function_exists('asset')) +{ + /** + * Generate an asset path for the application. + * + * @param string $path + * @param bool $secure + * @return string + */ + function asset($path, $secure = null) + { + return app('url')->asset($path, $secure); + } +} + +if ( ! function_exists('elixir')) +{ + /** + * Get the path to a versioned Elixir file. + * + * @param string $file + * @return string + */ + function elixir($file) + { + static $manifest = null; + if (is_null($manifest)) + { + $manifest = json_decode(file_get_contents(public_path().'/build/rev-manifest.json'), true); + } + if (isset($manifest[$file])) + { + return '/build/'.$manifest[$file]; + } + throw new InvalidArgumentException("File {$file} not defined in asset manifest."); + } +} + +if ( ! function_exists('auth')) +{ + /** + * Get the available auth instance. + * + * @return \Illuminate\Contracts\Auth\Guard + */ + function auth() + { + return app('Illuminate\Contracts\Auth\Guard'); + } +} + +if ( ! function_exists('bcrypt')) +{ + /** + * Hash the given value. + * + * @param string $value + * @param array $options + * @return string + */ + function bcrypt($value, $options = array()) + { + return app('hash')->make($value, $options); + } +} + +if ( ! function_exists('redirect')) +{ + /** + * Get an instance of the redirector. + * + * @param string|null $to + * @param int $status + * @param array $headers + * @param bool $secure + * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse + */ + function redirect($to = null, $status = 302, $headers = array(), $secure = null) + { + if (is_null($to)) return app('redirect'); + return app('redirect')->to($to, $status, $headers, $secure); + } +} + +if ( ! function_exists('response')) +{ + /** + * Return a new response from the application. + * + * @param string $content + * @param int $status + * @param array $headers + * @return \Symfony\Component\HttpFoundation\Response|\Illuminate\Contracts\Routing\ResponseFactory + */ + function response($content = '', $status = 200, array $headers = array()) + { + $factory = app('Illuminate\Contracts\Routing\ResponseFactory'); + if (func_num_args() === 0) + { + return $factory; + } + return $factory->make($content, $status, $headers); + } +} + +if ( ! function_exists('secure_asset')) +{ + /** + * Generate an asset path for the application. + * + * @param string $path + * @return string + */ + function secure_asset($path) + { + return asset($path, true); + } +} + +if ( ! function_exists('secure_url')) +{ + /** + * Generate a HTTPS url for the application. + * + * @param string $path + * @param mixed $parameters + * @return string + */ + function secure_url($path, $parameters = array()) + { + return url($path, $parameters, true); + } +} + + +if ( ! function_exists('session')) +{ + /** + * Get / set the specified session value. + * + * If an array is passed as the key, we will assume you want to set an array of values. + * + * @param array|string $key + * @param mixed $default + * @return mixed + */ + function session($key = null, $default = null) + { + if (is_null($key)) return app('session'); + if (is_array($key)) return app('session')->put($key); + return app('session')->get($key, $default); + } +} + + +if ( ! function_exists('cookie')) +{ + /** + * Create a new cookie instance. + * + * @param string $name + * @param string $value + * @param int $minutes + * @param string $path + * @param string $domain + * @param bool $secure + * @param bool $httpOnly + * @return \Symfony\Component\HttpFoundation\Cookie + */ + function cookie($name = null, $value = null, $minutes = 0, $path = null, $domain = null, $secure = false, $httpOnly = true) + { + $cookie = app('Illuminate\Contracts\Cookie\Factory'); + if (is_null($name)) + { + return $cookie; + } + return $cookie->make($name, $value, $minutes, $path, $domain, $secure, $httpOnly); + } +} diff --git a/app/Core/Helper/helpers.php b/app/Core/Helper/helpers.php new file mode 100644 index 0000000..bb867b8 --- /dev/null +++ b/app/Core/Helper/helpers.php @@ -0,0 +1,463 @@ + "success", -1 => "exception", 0 => "error"]; + $exceptionClassArr = ["exception" => "\Exception", "error" => "App\Exceptions\ApiErrorException"]; + $statusCodeArr = [1 => 200, -1 => 500, 0 => 400]; + $param["status"] = isset($param["status"]) ? $param["status"] : 1; + $status = isset($statusArr[$param["status"]]) ? $statusArr[$param["status"]] : "success"; + $statusCode = isset($statusCodeArr[$param["status"]]) ? $statusCodeArr[$param["status"]] : 200; + $message = (isset($param['message']) ? $param['message'] : ''); + $data = (isset($param['data']) ? $param['data'] : ''); + $exceptionClass = isset($exceptionClassArr[$status]) ? new $exceptionClassArr[$status]($message) : null; + + return ['status' => $status, 'message' => $message, 'data' => $data, 'statusCode' => $statusCode, "exceptionClass" => $exceptionClass]; +} + +function apiResponse($status = -1, $message = "", $data = [], $statusCode = null, $errorCode = null) +{ + $genericMessage = "Bilinmeyen Bir Hata Oluştu"; + $error = $status <= 0 ? true : false; + $statusCode = $statusCode ? $statusCode : 200; + $errorCode = $errorCode ? $errorCode : null; + //$statusCode = $error ? 500 : $statusCode; + $message = $status != -1 ? $message : $genericMessage; + $message = $message ? $message : null; + $data = $data && is_array($data) ? $data : []; + + $responseData = [ + "status" => $statusCode, + "error" => $error, + "errorCode" => $errorCode, + "message" => $message, + "data" => $data + ]; + + /** ServiceLog **/ + $serviceLogId = app('request')->serviceLogId; + $serviceLogRequestTime = app('request')->serviceLogRequestTime; + if (!empty($serviceLogId)) { + $updateServiceLog = [ + 'response' => json_encode($responseData), + 'response_time' => microtime(true) - $serviceLogRequestTime, + 'status' => 1 + ]; + $serviceLog = App::make("App\Core\Service\ServiceLogService"); + $serviceLog->update($serviceLogId, $updateServiceLog); + } + /** ServiceLog **/ + + return response()->json($responseData, $statusCode); +} + +function fillOnUndefined($variable, $index = null, $fillWith = null, $arrayDelimiter = ".") +{ + if (!$arrayDelimiter) { + return null; + } + + + if (!$index && $index !== '0' && $index !== 0) { + return false; + } + + $indexes = explode($arrayDelimiter, $index); + + $arrayDeep = $variable; + + foreach ($indexes as $perIndex) { + if (!isset($arrayDeep[$perIndex])) { + return $fillWith; + } + + $arrayDeep = $arrayDeep[$perIndex]; + } + + + return $arrayDeep; + +} + +function fillOnUndefinedAndEmpty($variable, $index = null, $fillWith = null, $arrayDelimiter = ".") +{ + if (!$arrayDelimiter) { + return null; + } + + + if (!$index && $index !== '' && $index !== '0' && $index !== 0) { + return false; + } + + $indexes = explode($arrayDelimiter, $index); + + $arrayDeep = $variable; + + foreach ($indexes as $perIndex) { + if (!isset($arrayDeep[$perIndex]) || empty($arrayDeep[$perIndex])) { + return $fillWith; + } + + $arrayDeep = $arrayDeep[$perIndex]; + } + + + return $arrayDeep; + +} + +function createFolderAndSubFolder($structure, $path = __DIR__, &$createdPaths, $mode = 0777) +{ + + foreach ($structure as $folder => $subFolder) { + if (is_array($subFolder)) { + $newPath = "{$path}/{$folder}"; + //$createdPaths[$newPath] = $newPath; + $createdPaths[] = $newPath; + + if (!is_dir($newPath)) mkdir($newPath, $mode); + + createFolderAndSubFolder($subFolder, $newPath, $createdPaths); + } else { + $newPath = "{$path}/{$subFolder}"; + //$createdPaths[$newPath] = $newPath; + $createdPaths[] = $newPath; + if (!is_dir($newPath)) mkdir($newPath, $mode); + } + } + + return $createdPaths; +} + +function smartyModifierFileSize($size) +{ + $size = max(0, (int)$size); + $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'); + $power = $size > 0 ? floor(log($size, 1024)) : 0; + $convertSize = number_format($size / pow(1024, $power), 2, '.', ','); + $convertType = $units[$power]; + return [$convertSize, $convertType]; +} + +function pickItemFromArray($index, array $targetArray) +{ + try { + $result = []; + foreach ($targetArray as $perIndex => $perValue) { + if (isset($perValue[$index])) { + $result[] = $perValue[$index]; + } + + } + return $result; + } catch (Exception $e) { + return []; + } +} + +function lang($word, array $parameters = [], $lang = null) +{ + return LanguageService::lang($word, $parameters, $lang); +} + +function getSystemLang() +{ + return LanguageService::getCurrentLang(); +} + +function getSystemLangSession() +{ + return session("sysLang"); +} + +function setSystemLangSession($language) +{ + $validLanguages = LanguageService::getValidLanguages(); + if (!in_array($language, $validLanguages)) { + throw new Exception("Invalid Language Selected language parameter : " . json_encode($language)); + } + session()->set("sysLang", $language); + return $language; +} + +function getSystemDefaultLang() +{ + return LanguageService::getDefaultLang(); +} + +function getLanguageSupportList() +{ + return LanguageService::getLanguageSupportList(); +} + +function upperCase($word, $charset = "UTF-8") +{ + $word = str_replace("i", "İ", $word); + return mb_strtoupper($word, $charset); +} + +function lowerCase($word, $charset = "UTF-8") +{ + $word = str_replace("I", "ı", $word); + return mb_strtolower($word, $charset); +} + +function uCase($word, $charset = "UTF-8") +{ + $word = preg_replace("#(^\i)#", "İ", $word); + $word = preg_replace("#(^\ı)#", "I", $word); + return mb_convert_case($word, MB_CASE_TITLE, $charset); +} + +function reIndexArrayKeys(array $targetArray, $startNo = 0) +{ + if (!$targetArray) { + return []; + } + return array_combine(range($startNo, ($startNo - 1) + count($targetArray)), $targetArray); +} + +function singleElementXMLArray($array = []) +{ + + return fillOnUndefined($array, 0) && is_array($array) ? $array : [$array]; +} + +function toArray($value): array +{ + return is_array($value) ? $value : ($value !== null ? [$value] : []); +} + + +function singleElementArray($arrayElement, array $extraParams = null) +{ + + $isMultiArray = true; + + if (!is_array($arrayElement)) { + return []; + } + + foreach ($arrayElement as $perItem) { + if (gettype($perItem) != "array") { + $isMultiArray = false; + break; + } + } + + if (!$isMultiArray) { + $isMultiArray = true; + foreach (array_keys($arrayElement) as $perKey) { + if (!is_numeric($perKey)) { + $isMultiArray = false; + break; + } + } + } + + + return $isMultiArray ? $arrayElement : array($arrayElement); + +} + +function generateRandomString($length = 5) +{ + return \App\Core\Helper\PhpHelper::generateRandomString($length); +} + +function humanReadableDate($date) +{ + return date('d/m/Y', strtotime($date)); +} + +function language_key($title) +{ + return Str::slug($title, $separator = '_', $language = 'en'); +} + +function getGuid() +{ + $hash = md5(uniqid()); + $guid = substr($hash, 0, 8) . + '-' . + substr($hash, 8, 4) . + '-' . + substr($hash, 12, 4) . + '-' . + substr($hash, 16, 4) . + '-' . + substr($hash, 20, 12); + + return $guid; +} + +function moneyDoubleFormat($money) +{ + return number_format($money, 2, ',', ''); +} + + +function moneyDoubleFormatDecimal($money) +{ + return floatval(number_format($money, 2, '.', '')); +} + +function moneyFourFormatDecimal($money) +{ + return floatval(number_format($money, 4, '.', '')); +} + + +function getCodeGenerate($prefix = 'ENW') +{ + return $prefix . date('ymd') . '-' . upperCase(generateRandomString(8)); +} + +function getPaymentGenerate($prefix = 'PYM') +{ + $prefix = is_null($prefix) ? 'PYM' : $prefix; + return 'ENW' . $prefix . date('ymd') . '_' . upperCase(generateRandomString(8)); +} + +function getTicketCodeGenerate($prefix = 'TCK') +{ + return $prefix . upperCase(generateRandomString(8)); +} + +function creditCardMask($cardNumber) +{ + return substr($cardNumber, 0, 6) . '******' . substr($cardNumber, -4); +} + +function occupancyCodeFormatted($occupancyCode) +{ + + $occupancyFormatted = []; + + $adultCount = substr_count($occupancyCode, 'A'); + if ($adultCount > 0) { + $occupancyFormatted[] = $adultCount . ' Adult'; + } + + $childCount = substr_count($occupancyCode, 'C'); + if ($childCount > 0) { + + $childAges = []; + $roomsChildArray = explode('C', $occupancyCode); + for ($i = 1; $i < count($roomsChildArray); $i++) { + if (!empty($roomsChildArray[$i]) || $roomsChildArray[$i] == 0) { + $childAges[] = $roomsChildArray[$i]; + } + } + + if (!empty($childAges)) { + $childAges = implode(', ', $childAges); + } + + $occupancyFormatted[] = $childCount . ' Child' . (!empty($childAges) ? ' (' . $childAges . ')' : null); + } + + if (!empty($occupancyFormatted)) { + $occupancyFormatted = implode(', ', $occupancyFormatted); + } + + return $occupancyFormatted; +} + +function occupancyCodeParser($occupancyCode) +{ + + $occupancy = []; + $occupancy['ADT'] = 0; + $occupancy['CHD'] = 0; + $occupancy['AGE'] = []; + + $adultCount = substr_count($occupancyCode, 'A'); + if ($adultCount > 0) { + $occupancy['ADT'] = $adultCount; + } + + $childCount = substr_count($occupancyCode, 'C'); + if ($childCount > 0) { + + $childAges = []; + $roomsChildArray = explode('C', $occupancyCode); + for ($i = 1; $i < count($roomsChildArray); $i++) { + if (!empty($roomsChildArray[$i])) { + $childAges[] = $roomsChildArray[$i]; + } + } + + $occupancy['CHD'] = $childCount; + $occupancy['AGE'] = $childAges; + + //$occupancyFormatted[] = $childCount . ' Child' . (!empty($childAges) ? ' (' . $childAges . ')' : null); + } + + return $occupancy; +} + + +function occupancyGroup($maxAdult, $maxChild, $maxOccupancy, $excludeOccupancy = []) +{ + + $occupancyGroup = []; + //TODO: $excludeOccupancy + for ($i = 0; $i < $maxAdult; $i++) { + $occupancyCodeAdult = str_repeat('A', ($i + 1)); + if (strlen($occupancyCodeAdult) > $maxOccupancy) { + break; + } + $occupancyGroup[] = $occupancyCodeAdult; + + for ($j = 0; $j < $maxChild; $j++) { + $occupancyCodeChild = str_repeat('C', ($j + 1)); + + if (strlen($occupancyCodeChild) > $maxOccupancy) { + continue; + } + + if (strlen($occupancyCodeAdult . $occupancyCodeChild) > $maxOccupancy) { + break; + } + + $occupancyGroup[] = $occupancyCodeAdult . $occupancyCodeChild; + } + } + + return $occupancyGroup; +} + + +function cancellationPolicyTextFormatted($isNonRefundable, $isFreeCancellation, $beforeArrivalDay, $type, $value, $currency, $languageCode = 'en') +{ + + $formattedText = null; + if (!$isFreeCancellation) { + if ($isNonRefundable) { + $formattedText = __('btn-irrevocable', [], $languageCode); + } else { + + $formattedText = __('be-if_canceled_up_to_days', ['day' => $beforeArrivalDay], $languageCode) . ', ' . $value . ($type == 'PER' ? '%' : $currency) . ' ' . __('be-search-impose_penalty', [], $languageCode); + } + } else { + $formattedText = ($beforeArrivalDay > 0 ? __('be-up_to_days', ['day' => $beforeArrivalDay], $languageCode) : '') . ' ' . __('btn-free_cancellation', [], $languageCode); + } + + return $formattedText; + +} diff --git a/app/Core/Mail/AffiliateRequestMail.php b/app/Core/Mail/AffiliateRequestMail.php new file mode 100644 index 0000000..9a0fb5a --- /dev/null +++ b/app/Core/Mail/AffiliateRequestMail.php @@ -0,0 +1,60 @@ +param = $param; + } + + public function build () + { + try + { + $params = $this->param; + $mailParams = $params['mailViewParams'] ; + $mailData = $params['mailData'] ; + $mailSenderAddress = Config::get('app.mailSenderAddress') ; + $mailTitle = $mailData['to']["name"]. ' | Affiliate Request Mail'; + + app('translator')->setLocale(fillOnUndefined($params['mailViewParams'],'language', 'en')); + + + /*echo view('emails.affiliateRequestMail', compact('mailParams', 'mailTitle')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.affiliateRequestMail', compact('mailParams', 'mailTitle')) + ->to($mailData['to']["email"], $mailData['to']["name"]) + ->bcc($mailData['bcc']) + ->subject($mailTitle) + ->with(['message' => $this]); + + } + catch ( Exception $e ) + { + $message = $e->getFile()." ".$e->getLine()." ".$e->getMessage(); + Log::error($message); + return output( ['status' => -1, 'message' => $e->getMessage()] ); + } + } + +} diff --git a/app/Core/Mail/BaseMail.php b/app/Core/Mail/BaseMail.php new file mode 100644 index 0000000..f402bf5 --- /dev/null +++ b/app/Core/Mail/BaseMail.php @@ -0,0 +1,13 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + $bookingService = App::make('App\Core\Service\BookingService'); + $userService = App::make('App\Core\Service\UserService'); + + $requestData = [ + 'criteria' => [ + ['field' => 'booking_code', 'condition' => '=', 'value' => $params['bookingCode']] + ], + 'with' => ['bookingProperty.propertyBrand', 'bookingContact'], + 'firstRow' => true + ]; + + $bookingDetail = $bookingService->select($requestData); + + if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) { + throw new ApiErrorException('Booking not found.'); + } + + $bookingDetail = $bookingDetail['data']; + + $mailParams = [ + 'to' => $bookingDetail['booking_contact']['email'], + 'toNameSurname' => $bookingDetail['booking_contact']['nameSurname'], + 'bcc' => [], + 'confirmCode' => $params['confirmCode'], + 'bookingId' => fillOnUndefined($bookingDetail, 'id'), + 'propertyId' => fillOnUndefined($bookingDetail, 'property_id'), + 'bookingCode' => fillOnUndefined($bookingDetail, 'booking_code'), + 'locale' => fillOnUndefined($bookingDetail['booking_contact'], 'language_code', 'en'), + ]; + + /*echo view('emails.bookingCancellationConfirmCode', compact('mailParams')); + die();*/ + + $mailParams['bcc'][] = 'channel@extranetwork.com'; + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.bookingCancellationConfirmCode', compact('mailParams')) + ->to($mailParams['to'], $mailParams['toNameSurname']) + ->bcc($mailParams['bcc']) + ->subject(__('api-mailing-cancellation_confirm_code-subject', ['bookingCode' => $mailParams['bookingCode']], $mailParams['locale'])); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/BookingCancellationRequestMail.php b/app/Core/Mail/BookingCancellationRequestMail.php new file mode 100644 index 0000000..648fda1 --- /dev/null +++ b/app/Core/Mail/BookingCancellationRequestMail.php @@ -0,0 +1,123 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + $bookingService = App::make('App\Core\Service\BookingService'); + $userService = App::make('App\Core\Service\UserService'); + + $requestData = [ + 'criteria' => [ + ['field' => 'booking_code', 'condition' => '=', 'value' => $params['bookingCode']] + ], + 'with' => [ + 'bookingPayment', 'bookingRoom.roomRateMapping.propertyRoom', 'bookingRoom.roomRateMapping.propertyRoomRate', + 'bookingContact', 'bookingProperty.propertyBrand', 'bookingProperty.propertyExecutive', 'bookingProperty.propertyUser.user', + 'bookingPropertyWeb', 'propertyBookingEngine', 'propertyBookingChannel' + ], + 'firstRow' => true + ]; + + $bookingDetail = $bookingService->select($requestData); + + if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) { + throw new ApiErrorException('Booking not found.'); + } + + $bookingDetail = $bookingDetail['data']; + + + //Language + $userData = $bookingDetail['booking_property']['property_user']; + $firstUserData = reset($userData); + $bookingLanguageCode = fillOnUndefined($firstUserData['user'], 'language', 'en'); + + + //Mail Contact Data + $bcc = []; + foreach ($userData as $user) { + if ($user['user']['email'] != $firstUserData['user']['email']) { + $bcc[] = $user['user']['email']; + } + } + + $reservationExecutives = []; + if (isset($bookingDetail['booking_property']['property_executive'])) { + $reservationExecutives = collect($bookingDetail['booking_property']['property_executive']) + ->where('executive_type_id', '=', '7') + ->values()->all(); + } + + foreach ($reservationExecutives as $reservationExecutive) { + $bcc[] = $reservationExecutive['email']; + } + + $mailData = [ + 'to' => [ + 'name' => $bookingDetail['booking_property']['name'], + 'email' => $firstUserData['user']['email'] + ] + ]; + + + $mailParams = [ + 'to' => $firstUserData['user']['email'], + 'bcc' => $bcc, + 'toNameSurname' => $bookingDetail['booking_property']['name'], + 'bookingContactNameSurname' => $bookingDetail['booking_contact']['nameSurname'], + 'bookingCode' => fillOnUndefined($bookingDetail, 'booking_code'), + 'locale' => $bookingLanguageCode, + ]; + + $mailParams['detailUrl'] = config('app.client_server').'/app/network/reservation/'.$bookingDetail['id'].'?propertyid='.$bookingDetail['property_id']; + + /*echo view('emails.bookingCancellationRequest', compact('mailParams')); + die();*/ + + $mailParams['bcc'][] = 'channel@extranetwork.com'; + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.bookingCancellationRequest', compact('mailParams')) + ->to($mailParams['to'], $mailParams['toNameSurname']) + ->bcc($mailParams['bcc']) + ->subject(__('api-mailing-cancellation_request-subject', ['bookingCode' => $mailParams['bookingCode']], $mailParams['locale'])); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/BookingEngineSearchReportMail.php b/app/Core/Mail/BookingEngineSearchReportMail.php new file mode 100644 index 0000000..4896888 --- /dev/null +++ b/app/Core/Mail/BookingEngineSearchReportMail.php @@ -0,0 +1,54 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + /*echo view('emails.bookingEngineSearchReportMail', compact('params')); + die();*/ + + $bcc = $params['propertyUserEmail']; + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.bookingEngineSearchReportMail', compact('params')) + ->to($mailSenderAddress, 'Extranetwork') + ->bcc($bcc) + ->subject($params['propertyName'].' - Günlük İşlem Raporu : '. $params['date']); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/BookingInvoiceUpdateMail.php b/app/Core/Mail/BookingInvoiceUpdateMail.php new file mode 100644 index 0000000..28a6aa1 --- /dev/null +++ b/app/Core/Mail/BookingInvoiceUpdateMail.php @@ -0,0 +1,120 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + + + $bookingService = App::make("App\Core\Service\BookingService"); + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'booking_code', 'condition' => '=', 'value' => $params['bookingCode']], + ], + 'with' => ['property.propertyBrand', 'property.propertyExecutive', 'property.propertyUser.user','property.propertyBookingEngineToken'], + 'firstRow' => true + ]; + + $bookingDetail = $bookingService->select($bookingDetailParam); + if ($bookingDetail['status'] != 'success') { + throw new ApiErrorException($bookingDetail['message']); + } + + $bookingDetail = $bookingDetail['status'] == 'success' && !empty($bookingDetail['data']) ? $bookingDetail['data'] : null; + + + $property = $bookingDetail['property']; + + + //Mail Contact Data + $reservationExecutives = []; + if (isset($property['property_executive'])) { + $reservationExecutives = collect($property['property_executive']) + ->where('executive_type_id', '=', '7') + ->values()->all(); + } + + foreach ($reservationExecutives as $reservationExecutive) { + $bcc[] = $reservationExecutive['email']; + } + $bcc[] = "channel@extranetwork.com"; + + + $userData = $property['property_user']; + $firstUserData = reset($userData); + + foreach ($userData as $user) { + if ($user['user']['email'] != $firstUserData['user']['email']) { + $bcc[] = $user['user']['email']; + } + } + + $mailData = [ + 'to' => [ + 'name' => $firstUserData['user']['nameSurname'], + 'email' => $firstUserData['user']['email'], + ], + 'bcc' => $bcc + + ]; + + $locale = fillOnUndefined($firstUserData['user'], 'language', 'tr'); + $mailSenderAddress = Config::get('app.mailSenderAddress'); + app('translator')->setLocale($locale); + + + $mailParams = [ + 'propertyName' => $property['name'], + 'bookingCode' => $params['bookingCode'], + 'logo' => $property['property_brand']['logoUrl'], + 'btnUrl' => config('app.bookingEngineUrl') . '/' . $property['property_booking_engine_token']['token'] . '/' . $locale . '/booking-detail/' . $params['bookingCode'], + ]; + + /*echo view('emails.bookingInvoiceUpdateMail', compact('mailParams')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - ' . $mailParams['propertyName']) + ->view('emails.bookingInvoiceUpdateMail', compact('mailParams')) + ->to($mailData['to']["email"], $mailData['to']["name"]) + ->bcc($mailData['bcc']) + ->subject(__('api-mailing-booking-invoice_update-title', ['bookingCode' => $mailParams['bookingCode']])) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/BookingPaymentDataCodeMail.php b/app/Core/Mail/BookingPaymentDataCodeMail.php new file mode 100644 index 0000000..d7a84ac --- /dev/null +++ b/app/Core/Mail/BookingPaymentDataCodeMail.php @@ -0,0 +1,101 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + $bookingService = App::make('App\Core\Service\BookingService'); + $userService = App::make('App\Core\Service\UserService'); + + $requestData = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['booking_id']] + ], + //'with' => ['bookingProperty.propertyExecutive', 'bookingProperty.propertyBookingEngineToken', 'bookingProperty.propertyBrand', 'bookingContact', 'bookingChannel', 'bookingPayment', 'bookingPaymentType', 'bookingStatus'], + 'firstRow' => true + ]; + + $booking = $bookingService->select($requestData); + + if ($booking['status'] != 'success' || empty($booking['data'])) { + throw new ApiErrorException(lang('Booking not found')); + } + + $booking = $booking['data']; + + $requestData = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['user_id']] + ], + 'firstRow' => true + ]; + + $user = $userService->select($requestData); + + if ($user['status'] != 'success' || empty($user['data'])) { + throw new ApiErrorException(lang('User not found')); + } + + $user = $user['data']; + + $mailParams = [ + 'to' => $user['email'], + 'toNameSurname' => $user['nameSurname'], + 'bcc' => [], + 'unlockCode' => $params['unlock_code'], + 'bookingId' => fillOnUndefined($booking, 'id'), + 'propertyId' => fillOnUndefined($booking, 'property_id'), + 'bookingCode' => fillOnUndefined($booking, 'booking_code'), + 'locale' => fillOnUndefined($user, 'language', 'en'), + ]; + + + /*echo view('emails.bookingPaymentDataCode', compact('mailParams')); + die();*/ + + $mailParams['bcc'][] = 'burhan@extranetwork.com'; + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.bookingPaymentDataCode', compact('mailParams')) + ->to($mailParams['to'], $mailParams['toNameSurname']) + ->bcc($mailParams['bcc']) + ->subject(__('api-mailing-payment_data_code-subject',['bookingCode' => $mailParams['bookingCode']],$mailParams['locale'])); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/BookingPropertyAddonUpdateMail.php b/app/Core/Mail/BookingPropertyAddonUpdateMail.php new file mode 100644 index 0000000..52e6c0f --- /dev/null +++ b/app/Core/Mail/BookingPropertyAddonUpdateMail.php @@ -0,0 +1,113 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + + $propertyService = App::make("App\Core\Service\PropertyService"); + + $propertyParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['propertyId']], + ], + 'with' => ['propertyBrand', 'propertyExecutive', 'propertyUser.user','propertyBookingEngineToken'], + 'firstRow' => true + ]; + + $property = $propertyService->select($propertyParam); + if ($property['status'] != 'success') { + throw new ApiErrorException($property['message']); + } + + $property = $property['data']; + + //Mail Contact Data + $reservationExecutives = []; + if (isset($property['property_executive'])) { + $reservationExecutives = collect($property['property_executive']) + ->where('executive_type_id', '=', '7') + ->values()->all(); + } + + foreach ($reservationExecutives as $reservationExecutive) { + $bcc[] = $reservationExecutive['email']; + } + $bcc[] = "channel@extranetwork.com"; + + + $userData = $property['property_user']; + $firstUserData = reset($userData); + + foreach ($userData as $user) { + if ($user['user']['email'] != $firstUserData['user']['email']) { + $bcc[] = $user['user']['email']; + } + } + + $mailData = [ + 'to' => [ + 'name' => $firstUserData['user']['nameSurname'], + 'email' => $firstUserData['user']['email'], + ], + 'bcc' => $bcc + + ]; + + $locale = fillOnUndefined($firstUserData['user'], 'language', 'tr'); + $mailSenderAddress = Config::get('app.mailSenderAddress'); + app('translator')->setLocale($locale); + + + $mailParams = [ + 'propertyName' => $property['name'], + 'bookingCode' => $params['bookingCode'], + 'addonLanguageKey' => $params['addonLanguageKey'], + 'logo' => $property['property_brand']['logoUrl'], + 'btnUrl' => config('app.bookingEngineUrl') . '/' . $property['property_booking_engine_token']['token'] . '/' . $locale . '/booking-detail/' . $params['bookingCode'], + ]; + + return $this->from($mailSenderAddress, 'Extranetwork - ' . $mailParams['propertyName']) + ->view('emails.bookingPropertyAddonUpdateMail', compact('mailParams')) + ->to($mailData['to']["email"], $mailData['to']["name"]) + ->bcc($mailData['bcc']) + ->subject(__('api-mailing-booking-addon_update-title', ['bookingCode' => $mailParams['bookingCode'], 'addonLanguageKey' => __($mailParams['addonLanguageKey'])])) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/BookingTicketMail.php b/app/Core/Mail/BookingTicketMail.php new file mode 100644 index 0000000..5e6766e --- /dev/null +++ b/app/Core/Mail/BookingTicketMail.php @@ -0,0 +1,140 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $bookingService = App::make('App\Core\Service\BookingService'); + + $requestData = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['booking_id']] + ], + 'with' => ['bookingProperty.propertyExecutive', 'bookingProperty.propertyBookingEngineToken', 'bookingProperty.propertyBrand', 'bookingContact', 'bookingChannel', 'bookingPayment', 'bookingPaymentType', 'bookingStatus'], + 'firstRow' => true + ]; + + $booking = $bookingService->select($requestData); + + if ($booking['status'] != 'success' || empty($booking['data'])) { + throw new ApiErrorException(lang('Booking not found')); + } + + $booking = $booking['data']; + + $locale = fillOnUndefined($params, 'locale', 'en'); + if (!is_null($params['user'])) { + $locale = $booking['booking_contact']['language_code']; + } + app('translator')->setLocale($locale); + + $propertyLogo = 'https://www.extranetwork.com/assets/img/logo/mini-logo.png'; + if (isset($booking['booking_property']['property_brand']['logo_name'])) { + $propertyLogo = Config::get('app.imageUrl') . '/property-photos/' . $booking['booking_property']['id'] . "/logo/" . $booking['booking_property']['property_brand']['logo_name'] . '_250x250.' . $booking['booking_property']['property_brand']['logo_file_ext']; + } + + $bookingContact = [ + 'mail' => $booking['booking_contact']['email'], + 'nameSurname' => $booking['booking_contact']['nameSurname'], + ]; + + if (!isset($booking['booking_property']['property_executive'])) { + throw new ApiErrorException(lang('Property Executive not found')); + } + + $propertyExecutives = collect($booking['booking_property']['property_executive'])->where('status', 1)->where('executive_type_id', 7)->pluck('email')->toArray(); + + $params['subject'] = __('api-mailing-booking_ticket-subject', ['booking_code' => $booking['booking_code']]);//'Booking Ticket Message: ' . $booking['booking_code']; + $params['mailSenderName'] = 'Extranetwork - ' . $booking['booking_property']['name']; + + if (!is_null($params['user'])) { + + $mailParams = [ + 'to' => $bookingContact['mail'], + 'toNameSurname' => $bookingContact['mail'], + 'bcc' => [], + 'title' => __('general-hi') . ', ' . $bookingContact['nameSurname'], + 'logo' => $propertyLogo, + 'message' => __('api-mailing-booking_ticket-message-user', ['booking_code' => $booking['booking_code']]), + 'btnTitle' => __('api-mailing-booking_ticket-btn-message'), + 'btnUrl' => config('app.bookingEngineUrl') . '/' . $booking['booking_property']['property_booking_engine_token']['token'] . '/' . $locale . '/booking-detail/' . $booking['booking_code'], + ]; + + } else { + + if (empty($propertyExecutives)) { + throw new ApiErrorException(lang('Property Executive not found')); + } + + $mailParams = [ + 'to' => $propertyExecutives[0], + 'toNameSurname' => null, + 'bcc' => $propertyExecutives, + 'title' => __('general-hi') . ',', + 'logo' => $propertyLogo, + 'message' => __('api-mailing-booking_ticket-message-enw-user', ['booking_code' => $booking['booking_code'], 'property_name' => $booking['booking_property']['name']]), + 'btnTitle' => __('api-mailing-booking_ticket-btn-message'), + 'btnUrl' => config('app.client_server') . '/app/network/reservation/' . $booking['id'].'?propertyid='.$booking['property_id'], + ]; + + } + + //$bcc[] = "sales@extranetwork.com"; + $bcc = $mailParams['bcc']; + $bcc[] = "channel@extranetwork.com"; + + /*echo view('emails.bookingTicketMail', compact('mailParams')); + die();*/ + + return $this->from($mailSenderAddress, $params['mailSenderName']) + ->view('emails.bookingTicketMail', compact('mailParams')) + ->to($mailParams['to'], $mailParams['toNameSurname']) + ->bcc($bcc) + ->subject($params['subject']); + + + } catch (ApiErrorException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + die(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + die(); + } + + } + +} diff --git a/app/Core/Mail/CancelBookingMail.php b/app/Core/Mail/CancelBookingMail.php new file mode 100644 index 0000000..f40e959 --- /dev/null +++ b/app/Core/Mail/CancelBookingMail.php @@ -0,0 +1,277 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + + $propertyService = App::make("App\Core\Service\PropertyService"); + $bookingService = App::make("App\Core\Service\BookingService"); + $propertyBrandService = App::make("App\Core\Service\PropertyBrandService"); + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['booking_id']] + ], + 'with' => [ + 'bookingPayment', 'bookingRoom.roomRateMapping.propertyRoom','bookingRoom.roomRateMapping.propertyRoomRate', + 'bookingContact','bookingProperty.propertyBrand', 'bookingProperty.propertyExecutive', 'bookingProperty.propertyUser.user', + 'bookingPropertyWeb', 'propertyBookingEngine', 'propertyBookingChannel' + ], + 'firstRow' => true + ]; + + $bookingData = $bookingService->select($bookingDetailParam); + $bookingData = $bookingData['data']; + + + $hostAddress = Config::get('app.bookingEngineUrl') . '/' . $bookingData['property_booking_engine']['token'] . '/' . $bookingData['booking_contact']['language_code'] . '/booking-detail/' . $bookingData['booking_code']; + + $brandRequestData = ['property_id' => $bookingData['property_id']]; + $getPropertyBrand = $propertyBrandService->getPropertyBrand($brandRequestData); + if ($getPropertyBrand['status'] != "success") { + throw new Exception($getPropertyBrand['message']); + } + + $propertyBrand = $getPropertyBrand['data']; + $propertyBrandLogo = isset($propertyBrand['name']) + ? Config::get('app.fileSystemDriver') . '/property-photos/' . $propertyBrand['property_id'] . "/logo/" . $propertyBrand['name'] . '_250x250.' . $propertyBrand['logo_file_ext'] + : null; + + $roomOrder = 1; + $bookingChannelRoom = []; + foreach ($bookingData['booking_room'] as $bookingRoom) { + + if (!empty($bookingRoom['room_rate_mapping']['property_room'])) { + $bookingRoom['room_name'] = $bookingRoom['room_rate_mapping']['property_room']['name']; + } + if (!empty($bookingRoom['room_rate_mapping']['property_room_rate'])) { + $bookingRoom['room_rate_name'] = $bookingRoom['room_rate_mapping']['property_room_rate']['name']; + } + + $extraParam = null; + if (!empty($bookingRoom['extra_param'])) { + $extraParamDecode = json_decode($bookingRoom['extra_param'], 1); + + foreach ($extraParamDecode as $extraParamKey => $extraParamValue) { + + $extraParamTitle = explode('_', $extraParamKey); + foreach ($extraParamTitle as $extraParamTitleKey => $extraParamTitleValue) { + $extraParamTitle[$extraParamTitleKey] = ucwords($extraParamTitleValue); + } + $extraParamTitle = implode(' ', $extraParamTitle); + + $extraParam[$extraParamKey] = [ + 'title' => $extraParamTitle, + 'value' => $extraParamValue, + ]; + } + + } + + $additionalFee = null; + if (!empty($bookingRoom['rate_detail'])) { + $rateDetail = json_decode($bookingRoom['rate_detail'], 1); + if(isset($rateDetail['taxes']) && $bookingData['channel_manager_id'] == 2) { + $additionalFee = $rateDetail['taxes']; + } + } + + $bookingChannelRoom[] = [ + 'roomOrder' => $roomOrder, + 'roomName' => $bookingRoom['room_name'], + 'roomRateName' => $bookingRoom['room_rate_name'], + 'occupancyCode' => $bookingRoom['occupancy_code'], + 'occupancyText' => occupancyCodeFormatted($bookingRoom['occupancy_code']), + 'amount' => $bookingRoom['amount'], + 'discount_amount' => $bookingRoom['discount_amount'], + 'total' => $bookingRoom['total'], + 'currencyCode' => $bookingRoom['currency_code'], + 'dailyAmount' => !empty($bookingRoom['daily_amount']) ? json_decode($bookingRoom['daily_amount'],1) : null, + 'extraParam' => $extraParam, + 'occupancyFormatted' => occupancyCodeFormatted($bookingRoom['occupancy_code']), + 'additionalFee' => $additionalFee + ]; + + //$bookingChannelRoom[] = $roomOrder . '. ' . $bookingRoom['room_name'] . ' - ' . $bookingRoom['room_rate_name'] . ' - ' . occupancyCodeFormatted($bookingRoom['occupancy_code']); + $roomOrder++; + } + + $userData = $bookingData['booking_property']['property_user']; + $firstUserData = reset($userData); + + if (in_array($bookingData['property_booking_channel']['channel_category_id'], [2,3])) { + $bookingLanguageCode = fillOnUndefined($bookingData['booking_contact'], 'language_code', 'en'); + }else { + $bookingLanguageCode = fillOnUndefined($firstUserData['user'], 'language', 'en'); + } + + $bookingChannelPaymentType = __('property_booking_payment_type-pay_at_hotel',[],$bookingLanguageCode); + $bookingChannelPaymentSource = null; + if ($bookingData['booking_payment']['payment_type_code'] == 'CRD') { + $bookingChannelPaymentType = __('property_booking_payment_type-credit_card',[],$bookingLanguageCode); + if ($bookingData['booking_payment']['payment_source_code'] == 'OTA') { + $bookingChannelPaymentSource = __('enw-ota-credit_card',[],$bookingLanguageCode); + } + if ($bookingData['booking_payment']['payment_source_code'] == 'GST') { + $bookingChannelPaymentSource = __('enw-guest-credit_card',[],$bookingLanguageCode); + } + } + + $creditCardInformation = null; + if (!is_null($bookingData['booking_payment']['extra_param'])) { + $paymentData = json_decode($bookingData['booking_payment']['extra_param'], 1); + + if (isset($paymentData['attributes']['guarantee'])) { + $creditCardInformation['cardNumber'] = isset($paymentData['attributes']['guarantee']['card_number']) ? $paymentData['attributes']['guarantee']['card_number'] : null; + $creditCardInformation['cardHolderName'] = isset($paymentData['attributes']['guarantee']['cardholder_name']) ? $paymentData['attributes']['guarantee']['cardholder_name'] : null; + $creditCardInformation['expirationDate'] = isset($paymentData['attributes']['guarantee']['expiration_date']) ? $paymentData['attributes']['guarantee']['expiration_date'] : null; + $creditCardInformation['cvv'] = isset($paymentData['attributes']['guarantee']['cvv']) ? $paymentData['attributes']['guarantee']['cvv'] : null; + } + } + + //Genius Member + $bookingContactExtraParam = null; + if(!empty($bookingData['booking_contact']['extra_param'])) { + $bookingContactExtraParam = json_decode($bookingData['booking_contact']['extra_param'],1); + } + $isBookingGenius = false; + if(isset($bookingContactExtraParam['is_genius']) && $bookingContactExtraParam['is_genius']) { + $isBookingGenius = true; + } + + $mailParams = [ + 'bookingId' => $bookingData['id'], + 'bookingCode' => $bookingData['booking_code'], + 'propertyId' => $bookingData['booking_property']['id'], + 'booking_code' => $bookingData['booking_code'], + 'search_key' => $bookingData['search_key'], + 'channel_token' => $bookingData['channel_token'], + 'booking_engine_token' => $bookingData['booking_engine_token'], + 'checkin_date' => Carbon::parse($bookingData['checkin_date'])->format('d.m.Y'), + 'checkout_date' => Carbon::parse($bookingData['checkout_date'])->format('d.m.Y'), + 'payment_type_code' => $bookingData['booking_payment']['payment_type_code'], + 'payment_source_code' => $bookingData['booking_payment']['payment_source_code'], + 'room_amount' => $bookingData['room_amount'], + 'addon_amount' => $bookingData['addon_amount'], + 'discount_amount' => $bookingData['discount_amount'], + 'total' => $bookingData['total'], + 'currency_code' => $bookingData['currency_code'], + 'name_surname' => $bookingData['booking_contact']['nameSurname'], + 'countryCode' => upperCase($bookingData['booking_contact']['country_code']), + 'email' => $bookingData['booking_contact']['email'], + 'property_name' => $bookingData['booking_property']['name'], + 'url' => $hostAddress, + 'logo' => $bookingData['booking_property']['property_brand']['logoUrl'], + 'language_code' => $bookingData['booking_contact']['language_code'], + 'bookingChannelId' => $bookingData['channel_id'], + 'bookingChannelName' => fillOnUndefined($bookingData['property_booking_channel'], 'name'), + 'bookingChannelCategoryId' => $bookingData['property_booking_channel']['channel_category_id'], + 'bookingChannelCode' => $bookingData['channel_booking_code'], + 'bookingChannelContactNameSurname' => $bookingData['booking_contact']['nameSurname'], + 'bookingChannelContactEmail' => $bookingData['booking_contact']['email'], + 'bookingChannelContactPhone' => $bookingData['booking_contact']['phone_number'], + 'bookingChannelNote' => $bookingData['booking_contact']['note'], + 'bookingChannelRoom' => $bookingChannelRoom, + 'bookingChannelPaymentType' => $bookingChannelPaymentType, + 'bookingChannelPaymentSource' => $bookingChannelPaymentSource, + 'creditCardInformation' => $creditCardInformation, + 'isBookingGenius' => $isBookingGenius + ]; + + //Mail Contact Data + $reservationExecutives = []; + if (isset($bookingData['booking_property']['property_executive'])) { + $reservationExecutives = collect($bookingData['booking_property']['property_executive']) + ->where('executive_type_id', '=', '7') // sadece rezervasyon yetkisi olanlar ... + ->values()->all(); + } + + foreach ($reservationExecutives as $reservationExecutive) { + $bcc[] = $reservationExecutive['email']; + } + $bcc[] = "channel@extranetwork.com"; + + foreach ($userData as $user) { + if ($user['user']['email'] != $firstUserData['user']['email']) { + $bcc[] = $user['user']['email']; + } + } + + //Booking Engine ve Offline Channel to Customer Info's + if (in_array($bookingData['property_booking_channel']['channel_category_id'], [2,3])) { + $mailData = [ + 'to' => [ + 'name' => $bookingData['booking_contact']['nameSurname'], + 'email' => $bookingData['booking_contact']['email'] + ] + ]; + } else { + $mailData = [ + 'to' => [ + 'name' => $bookingData['booking_property']['name'], + 'email' => $firstUserData['user']['email'] + ] + ]; + } + + $mailData['bcc'] = $bcc; + + + $mailSenderAddress = Config::get('app.mailSenderAddress'); + //app('translator')->setLocale(fillOnUndefined($firstUserData['user'], 'language', 'tr')); + app('translator')->setLocale($bookingLanguageCode); + + $mailParams['showCreditCardUrl'] = null; + if(!empty($mailParams['creditCardInformation'])) { + $mailParams['showCreditCardUrl'] = Config::get('app.client_server').'/app/network/reservation/'.$mailParams['bookingId'].'?propertyid='.$mailParams['propertyId']; + } + + /*echo view('emails.cancelBookingMail', compact('mailParams')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - ' . $mailParams['property_name']) + ->view('emails.cancelBookingMail', compact('mailParams')) + ->to($mailData['to']["email"], $mailData['to']["name"]) + ->bcc($mailData['bcc']) + ->subject(__('api-malling-booking-cancel_booking-title', ['channel' => $mailParams['bookingChannelName']])) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/ChannelManagerNotificationMail.php b/app/Core/Mail/ChannelManagerNotificationMail.php new file mode 100644 index 0000000..ff90f71 --- /dev/null +++ b/app/Core/Mail/ChannelManagerNotificationMail.php @@ -0,0 +1,70 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $propertyService = App::make('App\Core\Service\PropertyService'); + + $requestData = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['propertyId']] + ], + 'with' => ['propertyUser.user'], + 'firstRow' => true + ]; + + $propertyDetail = $propertyService->select($requestData); + + $propertyUser = []; + if($propertyDetail['status'] == 'success' && !empty($propertyDetail['data'])) { + $propertyUser = collect($propertyDetail['data']['property_user'])->where('status',1)->pluck('user.email')->toArray(); + } + + /*echo view('emails.channelManagerNotificationMail', compact('params')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.channelManagerNotificationMail', compact('params')) + ->to('channel@extranetwork.com', 'Extranetwork Channel') + ->bcc($propertyUser) + ->subject('Channel Update Error: ' . $params['propertyName']); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/ContactFormMail.php b/app/Core/Mail/ContactFormMail.php new file mode 100644 index 0000000..1caa777 --- /dev/null +++ b/app/Core/Mail/ContactFormMail.php @@ -0,0 +1,58 @@ +param = $param; + } + + public function build () + { + try + { + $params = $this->param; + $mailParams = $params['mailViewParams'] ; + $mailData = $params['mailData'] ; + $mailSenderAddress = Config::get('app.mailSenderAddress') ; + $mailTitle = $mailData['to']["name"]. ' Contact Form (' . $mailParams['name']. ' '.$mailParams['surname']. ')' ; + + + app('translator')->setLocale('en'); + + return $this->from($mailSenderAddress, $mailParams['name']. ' '.$mailParams['surname']) + ->view('emails.contactFormMail', compact('mailParams', 'mailTitle')) + ->to($mailData['to']["email"], $mailData['to']["name"]) + ->bcc($mailData['bcc']) + ->subject($mailTitle) + ->with(['message' => $this]); + + + } + catch ( Exception $e ) + { + $message = $e->getFile()." ".$e->getLine()." ".$e->getMessage(); + Log::error($message); + return output( ['status' => -1, 'message' => $e->getMessage()] ); + } + } + +} diff --git a/app/Core/Mail/DailyReportMail.php b/app/Core/Mail/DailyReportMail.php new file mode 100644 index 0000000..b8ba479 --- /dev/null +++ b/app/Core/Mail/DailyReportMail.php @@ -0,0 +1,55 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + /*echo view('emails.dailyReportMail', compact('params')); + die();*/ + + $to = 'yoy@extranetwork.com'; + $bcc[] = 'burhan@extranetwork.com'; + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.dailyReportMail', compact('params')) + ->to($to, 'Extranetwork') + ->bcc($bcc) + ->subject('Extranetwork Summary Report - '. $params['daily']['period']); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/DailyReportMailSales.php b/app/Core/Mail/DailyReportMailSales.php new file mode 100644 index 0000000..ccdb9e6 --- /dev/null +++ b/app/Core/Mail/DailyReportMailSales.php @@ -0,0 +1,58 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + + /*echo view('emails.dailyReportMailSales', compact('params')); + die();*/ + + $to = $params['email']; + $bcc[] = 'yoy@extranetwork.com'; + $bcc[] = 'burhan@extranetwork.com'; + $bcc[] = 'cemile@extranetwork.com'; + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.dailyReportMailSales', compact('params')) + ->to($to, 'Extranetwork') + ->bcc($bcc) + ->subject('Extranetwork Summary Report - '. $params['daily']['period'].' - '. $params['name']); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/EnwContactFormMail.php b/app/Core/Mail/EnwContactFormMail.php new file mode 100644 index 0000000..fbbbc00 --- /dev/null +++ b/app/Core/Mail/EnwContactFormMail.php @@ -0,0 +1,54 @@ +param = $param; + } + + public function build () + { + try + { + $params = $this->param; + $mailParams = $params['mailViewParams'] ; + $mailData = $params['mailData'] ; + $mailTitle = __('myweb-contact-contact_form-title'); + + app('translator')->setLocale($mailParams['language']); + + return $this->view('emails.enwContactFormMail', compact('mailParams', 'mailTitle')) + ->to($mailData['to']["email"], $mailData['to']["name"]) + ->bcc($mailData['bcc']) + ->subject($mailTitle); + + } + catch ( Exception $e ) + { + $message = $e->getFile()." ".$e->getLine()." ".$e->getMessage(); + Log::error($message); + return output( ['status' => -1, 'message' => $e->getMessage()] ); + } + } + +} diff --git a/app/Core/Mail/InventoryActionMail.php b/app/Core/Mail/InventoryActionMail.php new file mode 100644 index 0000000..3c08820 --- /dev/null +++ b/app/Core/Mail/InventoryActionMail.php @@ -0,0 +1,60 @@ +param = $param; + } + + public function build() + { + try { + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + app('translator')->setLocale(fillOnUndefined($params, 'locale', 'en')); + + $params['title'] = $params['propertyName'] . ' - ' . __('enw-action-mail-title'); + $params['channelContact'] = $params['channelContact']; + + if(empty($params['channelContact'])) { + return false; + } + + /*echo view('emails.inventoryActionMail', compact('params')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - '.$params['propertyName']) + ->view('emails.inventoryActionMail', compact('params')) + //->to($logMailAddress, 'Development Team') + ->bcc($params['channelContact']) + ->subject($params['title']); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/InventoryPdfLinkMail.php b/app/Core/Mail/InventoryPdfLinkMail.php new file mode 100644 index 0000000..f60480c --- /dev/null +++ b/app/Core/Mail/InventoryPdfLinkMail.php @@ -0,0 +1,60 @@ +param = $param; + } + + public function build() + { + try { + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + app('translator')->setLocale(fillOnUndefined($params, 'locale', 'en')); + + $params['title'] = $params['propertyName'] . ' - ' . __('enw-inventory-mail-pdf-link'); + $params['channelContact'] = $params['channelContact']; + + if(empty($params['channelContact'])) { + return false; + } + + /*echo view('emails.inventoryActionMail', compact('params')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - '.$params['propertyName']) + ->view('emails.inventoryPdfLinkMail', compact('params')) + //->to($logMailAddress, 'Development Team') + ->bcc($params['channelContact']) + ->subject($params['title']); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/LogMail.php b/app/Core/Mail/LogMail.php new file mode 100644 index 0000000..217cbb6 --- /dev/null +++ b/app/Core/Mail/LogMail.php @@ -0,0 +1,52 @@ +param = $param; + } + + public function build() + { + try { + $params = $this->param; + $logMailAddress = Config::get('app.logMailAddress'); + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $params['title'] = 'ENW LOG - ' . $params['title']; + + app('translator')->setLocale('tr'); + + return $this->from($mailSenderAddress, 'Development Team') + ->view('emails.logMail', compact('params')) + ->to($logMailAddress, 'Development Team') + //->bcc(['bd@extranetwork.com']) + ->subject($params['title']); + + } catch (\Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/ManualPaymentMail.php b/app/Core/Mail/ManualPaymentMail.php new file mode 100644 index 0000000..42966d2 --- /dev/null +++ b/app/Core/Mail/ManualPaymentMail.php @@ -0,0 +1,64 @@ +param = $param; + } + + public function build () + { + try + { + $params = $this->param; + $mailParams = $params['mailViewParams'] ; + + $mailData = $params['mailData'] ; + $mailSenderAddress = Config::get('app.mailSenderAddress') ; + app('translator')->setLocale($mailParams["language_code"]); + + $description = __('api-manual_payment_mail-desc',[ + 'title' => $mailParams['process_title'] , + 'date' => $mailParams['date_time'], + 'amount' => $mailParams['amount'], + 'currency' => $mailParams['currency'] + ]); + + return $this->from($mailSenderAddress, 'Extranetwork - '.$mailParams['property_name']) + ->view('emails.manualPaymentMail', compact('mailParams', 'description')) + ->to($mailData['to']["email"]) + ->bcc($mailData['bcc']) + ->subject(__('api-manual_payment_mail-subject')) + ->with(['message' => $this]); + + + } + catch ( Exception $e ) + { + $message = $e->getFile()." ".$e->getLine()." ".$e->getMessage(); + Log::error($message); + return output( ['status' => -1, 'message' => $e->getMessage()] ); + } + } + +} diff --git a/app/Core/Mail/ModifiedBookingMail.php b/app/Core/Mail/ModifiedBookingMail.php new file mode 100644 index 0000000..ce45eb6 --- /dev/null +++ b/app/Core/Mail/ModifiedBookingMail.php @@ -0,0 +1,264 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + + $propertyService = App::make("App\Core\Service\PropertyService"); + $bookingService = App::make("App\Core\Service\BookingService"); + $propertyBrandService = App::make("App\Core\Service\PropertyBrandService"); + + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['booking_id']] + ], + 'with' => [ + 'bookingPayment', 'bookingRoom.roomRateMapping.propertyRoom','bookingRoom.roomRateMapping.propertyRoomRate', + 'bookingContact','bookingProperty.propertyBrand', 'bookingProperty.propertyExecutive', 'bookingProperty.propertyUser.user', + 'bookingPropertyWeb', 'propertyBookingEngine', 'propertyBookingChannel' + ], + 'firstRow' => true + ]; + + $bookingData = $bookingService->select($bookingDetailParam); + $bookingData = $bookingData['data']; + + + $hostAddress = Config::get('app.bookingEngineUrl') . '/' . $bookingData['property_booking_engine']['token'] . '/' . $bookingData['booking_contact']['language_code'] . '/booking-detail/' . $bookingData['booking_code']; + + $brandRequestData = ['property_id' => $bookingData['property_id']]; + $getPropertyBrand = $propertyBrandService->getPropertyBrand($brandRequestData); + if ($getPropertyBrand['status'] != "success") { + throw new Exception($getPropertyBrand['message']); + } + + $propertyBrand = $getPropertyBrand['data']; + $propertyBrandLogo = isset($propertyBrand['name']) + ? Config::get('app.fileSystemDriver') . '/property-photos/' . $propertyBrand['property_id'] . "/logo/" . $propertyBrand['name'] . '_250x250.' . $propertyBrand['logo_file_ext'] + : null; + + $roomOrder = 1; + $bookingChannelRoom = []; + foreach ($bookingData['booking_room'] as $bookingRoom) { + + if (!empty($bookingRoom['room_rate_mapping']['property_room'])) { + $bookingRoom['room_name'] = $bookingRoom['room_rate_mapping']['property_room']['name']; + } + if (!empty($bookingRoom['room_rate_mapping']['property_room_rate'])) { + $bookingRoom['room_rate_name'] = $bookingRoom['room_rate_mapping']['property_room_rate']['name']; + } + + $extraParam = null; + if (!empty($bookingRoom['extra_param'])) { + $extraParamDecode = json_decode($bookingRoom['extra_param'], 1); + + foreach ($extraParamDecode as $extraParamKey => $extraParamValue) { + + $extraParamTitle = explode('_', $extraParamKey); + foreach ($extraParamTitle as $extraParamTitleKey => $extraParamTitleValue) { + $extraParamTitle[$extraParamTitleKey] = ucwords($extraParamTitleValue); + } + $extraParamTitle = implode(' ', $extraParamTitle); + + $extraParam[$extraParamKey] = [ + 'title' => $extraParamTitle, + 'value' => $extraParamValue, + ]; + } + + } + + $additionalFee = null; + if (!empty($bookingRoom['rate_detail'])) { + $rateDetail = json_decode($bookingRoom['rate_detail'], 1); + if(isset($rateDetail['taxes']) && $bookingData['channel_manager_id'] == 2) { + $additionalFee = $rateDetail['taxes']; + } + } + + $bookingChannelRoom[] = [ + 'roomOrder' => $roomOrder, + 'roomName' => $bookingRoom['room_name'], + 'roomRateName' => $bookingRoom['room_rate_name'], + 'occupancyCode' => $bookingRoom['occupancy_code'], + 'occupancyText' => occupancyCodeFormatted($bookingRoom['occupancy_code']), + 'amount' => $bookingRoom['amount'], + 'discount_amount' => $bookingRoom['discount_amount'], + 'total' => $bookingRoom['total'], + 'currencyCode' => $bookingRoom['currency_code'], + 'dailyAmount' => !empty($bookingRoom['daily_amount']) ? json_decode($bookingRoom['daily_amount'],1) : null, + 'extraParam' => $extraParam, + 'occupancyFormatted' => occupancyCodeFormatted($bookingRoom['occupancy_code']), + 'additionalFee' => $additionalFee + ]; + + //$bookingChannelRoom[] = $roomOrder . '. ' . $bookingRoom['room_name'] . ' - ' . $bookingRoom['room_rate_name'] . ' - ' . occupancyCodeFormatted($bookingRoom['occupancy_code']); + $roomOrder++; + } + + $userData = $bookingData['booking_property']['property_user']; + $firstUserData = reset($userData); + + $bookingLanguageCode = fillOnUndefined($firstUserData['user'], 'language', 'en'); + $bookingChannelPaymentType = __('property_booking_payment_type-pay_at_hotel',[],$bookingLanguageCode); + $bookingChannelPaymentSource = null; + if ($bookingData['booking_payment']['payment_type_code'] == 'CRD') { + $bookingChannelPaymentType = __('property_booking_payment_type-credit_card',[],$bookingLanguageCode); + if ($bookingData['booking_payment']['payment_source_code'] == 'OTA') { + $bookingChannelPaymentSource = __('enw-ota-credit_card',[],$bookingLanguageCode); + } + if ($bookingData['booking_payment']['payment_source_code'] == 'GST') { + $bookingChannelPaymentSource = __('enw-guest-credit_card',[],$bookingLanguageCode); + } + } + + $creditCardInformation = null; + if (!is_null($bookingData['booking_payment']['extra_param'])) { + $paymentData = json_decode($bookingData['booking_payment']['extra_param'], 1); + + if (isset($paymentData['attributes']['guarantee'])) { + $creditCardInformation['cardNumber'] = isset($paymentData['attributes']['guarantee']['card_number']) ? $paymentData['attributes']['guarantee']['card_number'] : null; + $creditCardInformation['cardHolderName'] = isset($paymentData['attributes']['guarantee']['cardholder_name']) ? $paymentData['attributes']['guarantee']['cardholder_name'] : null; + $creditCardInformation['expirationDate'] = isset($paymentData['attributes']['guarantee']['expiration_date']) ? $paymentData['attributes']['guarantee']['expiration_date'] : null; + $creditCardInformation['cvv'] = isset($paymentData['attributes']['guarantee']['cvv']) ? $paymentData['attributes']['guarantee']['cvv'] : null; + } + } + + //Genius Member + $bookingContactExtraParam = null; + if(!empty($bookingData['booking_contact']['extra_param'])) { + $bookingContactExtraParam = json_decode($bookingData['booking_contact']['extra_param'],1); + } + $isBookingGenius = false; + if(isset($bookingContactExtraParam['is_genius']) && $bookingContactExtraParam['is_genius']) { + $isBookingGenius = true; + } + + $mailParams = [ + 'bookingId' => $bookingData['id'], + 'bookingCode' => $bookingData['booking_code'], + 'propertyId' => $bookingData['booking_property']['id'], + 'booking_code' => $bookingData['booking_code'], + 'search_key' => $bookingData['search_key'], + 'channel_token' => $bookingData['channel_token'], + 'booking_engine_token' => $bookingData['booking_engine_token'], + 'checkin_date' => Carbon::parse($bookingData['checkin_date'])->format('d.m.Y'), + 'checkout_date' => Carbon::parse($bookingData['checkout_date'])->format('d.m.Y'), + 'payment_type_code' => $bookingData['booking_payment']['payment_type_code'], + 'payment_source_code' => $bookingData['booking_payment']['payment_source_code'], + 'room_amount' => $bookingData['room_amount'], + 'addon_amount' => $bookingData['addon_amount'], + 'discount_amount' => $bookingData['discount_amount'], + 'total' => $bookingData['total'], + 'currency_code' => $bookingData['currency_code'], + 'name_surname' => $bookingData['booking_contact']['nameSurname'], + 'countryCode' => upperCase($bookingData['booking_contact']['country_code']), + 'email' => $bookingData['booking_contact']['email'], + 'property_name' => $bookingData['booking_property']['name'], + 'url' => $hostAddress, + 'logo' => $bookingData['booking_property']['property_brand']['logoUrl'], + 'language_code' => $bookingData['booking_contact']['language_code'], + 'bookingChannelId' => $bookingData['channel_id'], + 'bookingChannelName' => fillOnUndefined($bookingData['property_booking_channel'], 'name'), + 'bookingChannelCategoryId' => $bookingData['property_booking_channel']['channel_category_id'], + 'bookingChannelCode' => $bookingData['channel_booking_code'], + 'bookingChannelContactNameSurname' => $bookingData['booking_contact']['nameSurname'], + 'bookingChannelContactEmail' => $bookingData['booking_contact']['email'], + 'bookingChannelContactPhone' => $bookingData['booking_contact']['phone_number'], + 'bookingChannelNote' => $bookingData['booking_contact']['note'], + 'bookingChannelRoom' => $bookingChannelRoom, + 'bookingChannelPaymentType' => $bookingChannelPaymentType, + 'bookingChannelPaymentSource' => $bookingChannelPaymentSource, + 'creditCardInformation' => $creditCardInformation, + 'isBookingGenius' => $isBookingGenius + ]; + + //Mail Contact Data + $reservationExecutives = []; + if (isset($bookingData['booking_property']['property_executive'])) { + $reservationExecutives = collect($bookingData['booking_property']['property_executive']) + ->where('executive_type_id', '=', '7') // sadece rezervasyon yetkisi olanlar ... + ->values()->all(); + } + + foreach ($reservationExecutives as $reservationExecutive) { + $bcc[] = $reservationExecutive['email']; + } + $bcc[] = "channel@extranetwork.com"; + + + + foreach ($userData as $user) { + if ($user['user']['email'] != $firstUserData['user']['email']) { + $bcc[] = $user['user']['email']; + } + } + + $mailData = [ + 'to' => [ + 'name' => $firstUserData['user']['nameSurname'], + 'email' => $firstUserData['user']['email'], + ], + 'bcc' => $bcc + ]; + + $mailSenderAddress = Config::get('app.mailSenderAddress'); + //app('translator')->setLocale(fillOnUndefined($firstUserData['user'], 'language', 'tr')); + app('translator')->setLocale($bookingLanguageCode); + + $mailParams['showCreditCardUrl'] = null; + if(!empty($mailParams['creditCardInformation'])) { + $mailParams['showCreditCardUrl'] = Config::get('app.client_server').'/app/network/reservation/'.$mailParams['bookingId'].'?propertyid='.$mailParams['propertyId']; + } + + /*echo view('emails.modifiedBookingMail', compact('mailParams')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - ' . $mailParams['property_name']) + ->view('emails.modifiedBookingMail', compact('mailParams')) + ->to($mailData['to']["email"], $mailData['to']["name"]) + ->bcc($mailData['bcc']) + ->subject(__('api-mailing-booking-modified_booking-title', ['channel' => $mailParams['bookingChannelName']])) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/NewBookingMail.php b/app/Core/Mail/NewBookingMail.php new file mode 100644 index 0000000..4cbb0bb --- /dev/null +++ b/app/Core/Mail/NewBookingMail.php @@ -0,0 +1,68 @@ +param = $param; + } + + public function build() + { + try { + $params = $this->param; + $mailParams = $params['mailViewParams']; + + $mailData = $params['mailData']; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + app('translator')->setLocale($mailParams["language_code"]); + + $bookingData = __('api-mailling-booking-desc', [ + 'booking_code' => $mailParams['booking_code'], + 'property_name' => $mailParams['property_name'], + 'checkin_date' => $mailParams['checkin_date'], + 'checkout_date' => $mailParams['checkout_date'] + ]); + + $mailSubject = __('api-malling-booking-new_booking_info-title'); + if (isset($mailParams['bookingChannelCategoryId']) && $mailParams['bookingChannelCategoryId'] != 3) { + $mailSubject = $mailParams['bookingChannelName'] . ' - ' . $mailSubject; + } + + /*echo view('emails.newBookingMail', compact('mailParams', 'bookingData')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - ' . $mailParams['property_name']) + ->view('emails.newBookingMail', compact('mailParams', 'bookingData')) + ->to($mailData['to']["email"], $mailData['to']["name"]) + ->bcc($mailData['bcc']) + ->subject('🛎️ '.$mailSubject) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/OfferAcceptMail.php b/app/Core/Mail/OfferAcceptMail.php new file mode 100644 index 0000000..a6c48c4 --- /dev/null +++ b/app/Core/Mail/OfferAcceptMail.php @@ -0,0 +1,106 @@ +param = $param; + } + + public function build() + { + try { + $params = $this->param; + + $propertyService = App::make("App\Core\Service\OfferService"); + + $requestParams = [ + 'offer_id' => fillOnUndefined($params, 'offer_id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + + $offerDetail = $propertyService->findOffer($requestParams); + $offerDetail = fillOnUndefined($offerDetail, 'data'); + + app('translator')->setLocale($offerDetail["language"]); + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $mailParams['to'] = []; + $mailParams['to'] = $offerDetail['email']; + + $mailParams['cc'] = []; + if (!empty($offerDetail['create_user'])) { + $mailParams['cc'][] = $offerDetail['create_user']['email']; + } + + $mailParams['bcc'] = []; + $mailParams['bcc'][] = Config::get('app.logMailAddress'); + + + $mailParams['property_name'] = $offerDetail['property']['name']; + $mailParams['url'] = Config::get('app.client_server').'/offer/'.$offerDetail['offer_code']; + + + $mailParams['paymentUrl'] = null; + if(isset($params['paymentUrl']) && !is_null($params['paymentUrl'])) { + $mailParams['paymentUrl'] = $params['paymentUrl']; + } + + $description = []; + //$description[] = ''.$offerDetail['property']['name'].' tarafından oluşturulan '.$offerDetail['ticket_code'].' kodlu teklif onaylanmıştır.'; + $description[] = __('api-mailing-offer_accept_mail-description', ['propertyName' => $offerDetail['property']['name'], 'code' => $offerDetail['ticket_code']]); + + if(!is_null($offerDetail['payment_type_mapping_id']) && !empty($mailParams['paymentUrl'])) { + //$description[] = 'Teklife ait ödemenizi aşağıdaki Ödeme Linki üzerinden güvenle gerçekleştirebilirsiniz.'; + $description[] = __('api-mailing-offer_accept_mail-payment'); + } + + $description = implode('

', $description); + + $mailParams['logo'] = Config::get('app.webUrl').'/assets/img/logo/mini-logo.png'; + if(isset($offerDetail['property_brand']['logoUrl'])) { + $mailParams['logo'] = $offerDetail['property_brand']['logoUrl']; + } + + + /*echo view('emails.offerAcceptMail', compact('mailParams', 'description')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - ' . $mailParams['property_name']) + ->view('emails.offerAcceptMail', compact('mailParams', 'description')) + ->to($mailParams['to']) + ->cc($mailParams['cc']) + ->bcc($mailParams['bcc']) + ->subject(__('api-mailing-offer_accept_mail-title', ['code' => $offerDetail['ticket_code']])) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/OfferPreConfirmCustomerMail.php b/app/Core/Mail/OfferPreConfirmCustomerMail.php new file mode 100644 index 0000000..bfb3036 --- /dev/null +++ b/app/Core/Mail/OfferPreConfirmCustomerMail.php @@ -0,0 +1,91 @@ +param = $param; + } + + public function build() + { + try { + $params = $this->param; + + $propertyService = App::make("App\Core\Service\OfferService"); + + $requestParams = [ + 'offer_id' => fillOnUndefined($params, 'offer_id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $offerDetail = $propertyService->findOffer($requestParams); + $offerDetail = fillOnUndefined($offerDetail, 'data'); + + app('translator')->setLocale($offerDetail["language"]); + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $mailParams['to'] = []; + $mailParams['to'] = $offerDetail['email']; + + $mailParams['bcc'] = []; + $mailParams['bcc'][] = Config::get('app.logMailAddress'); + + + $mailParams['property_name'] = $offerDetail['property']['name']; + $mailParams['url'] = Config::get('app.client_server').'/offer/'.$offerDetail['offer_code']; + + $mailParams['logo'] = Config::get('app.webUrl').'/assets/img/logo/mini-logo.png'; + if(isset($offerDetail['property_brand']['logoUrl'])) { + $mailParams['logo'] = $offerDetail['property_brand']['logoUrl']; + } + + $description = []; + //$description[] = ''.$offerDetail['property']['name'].' tarafından oluşturulan '.$offerDetail['ticket_code'].' kodlu teklifi onayladınız.'; + $description[] = __('api-mailing-offer_preconfirm_customer_mail-description-one', ['propertyName' => $offerDetail['property']['name'], 'code' => $offerDetail['ticket_code']]); + //$description[] = 'Onayınız tesisimize bildirilmiştir. Tesisin teklifi onaylaması beklenmektedir, onaya istinaden size dönüş yapılacaktır.'; + $description[] = __('api-mailing-offer_preconfirm_customer_mail-description-two'); + + $description = implode('

', $description); + + + /*echo view('emails.offerPreConfirmCustomerMail', compact('mailParams', 'description')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - ' . $mailParams['property_name']) + ->view('emails.offerPreConfirmCustomerMail', compact('mailParams', 'description')) + ->to($mailParams['to']) + ->bcc($mailParams['bcc']) + //->subject($offerDetail['ticket_code'].' Kodlu Teklifiniz Tesis Onayı Bekliyor') + ->subject(__('api-mailing-offer_preconfirm_customer_mail-title', ['code' => $offerDetail['ticket_code']])) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/OfferPreConfirmPropertyMail.php b/app/Core/Mail/OfferPreConfirmPropertyMail.php new file mode 100644 index 0000000..232fb37 --- /dev/null +++ b/app/Core/Mail/OfferPreConfirmPropertyMail.php @@ -0,0 +1,91 @@ +param = $param; + } + + public function build() + { + try { + $params = $this->param; + + $propertyService = App::make("App\Core\Service\OfferService"); + + $requestParams = [ + 'offer_id' => fillOnUndefined($params, 'offer_id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $offerDetail = $propertyService->findOffer($requestParams); + $offerDetail = fillOnUndefined($offerDetail, 'data'); + + app('translator')->setLocale($offerDetail["language"]); + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $mailParams['to'] = []; + $mailParams['to'] = $offerDetail['create_user']['email']; + + $mailParams['bcc'] = []; + $mailParams['bcc'][] = Config::get('app.logMailAddress'); + + + $mailParams['property_name'] = $offerDetail['property']['name']; + $mailParams['url'] = Config::get('app.client_server').'/offer/'.$offerDetail['offer_code']; + + $mailParams['logo'] = Config::get('app.webUrl').'/assets/img/logo/mini-logo.png'; + if(isset($offerDetail['property_brand']['logoUrl'])) { + $mailParams['logo'] = $offerDetail['property_brand']['logoUrl']; + } + + $description = []; + //$description[] = 'Misafir, '.$offerDetail['ticket_code'].' kodlu teklifi onaylamıştır.'; + $description[] = __('api-mailing-offer_preconfirm_property_mail-description-one', ['code' => $offerDetail['ticket_code']]); + //$description[] = 'Teklif türü otel onaylı olduğu için, tesis onayı beklenmektedir. Tesis onayını Extranetwork üzerinden verebilirsiniz.'; + $description[] = __('api-mailing-offer_preconfirm_property_mail-description-two'); + + $description = implode('

', $description); + + + /*echo view('emails.offerPreConfirmPropertyMail', compact('mailParams', 'description')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - ' . $mailParams['property_name']) + ->view('emails.offerPreConfirmPropertyMail', compact('mailParams', 'description')) + ->to($mailParams['to']) + ->bcc($mailParams['bcc']) + //->subject($offerDetail['ticket_code'].' Kodlu Teklif Misafir Onayı') + ->subject(__('api-mailing-offer_preconfirm_property_mail-title', ['code' => $offerDetail['ticket_code']])) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/OfferSendMail.php b/app/Core/Mail/OfferSendMail.php new file mode 100644 index 0000000..7cc8b5f --- /dev/null +++ b/app/Core/Mail/OfferSendMail.php @@ -0,0 +1,86 @@ +param = $param; + } + + public function build() + { + try { + $params = $this->param; + + $propertyService = App::make("App\Core\Service\OfferService"); + + $requestParams = [ + 'offer_id' => fillOnUndefined($params, 'offer_id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $offerDetail = $propertyService->findOffer($requestParams); + $offerDetail = fillOnUndefined($offerDetail, 'data'); + + app('translator')->setLocale($offerDetail["language"]); + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $mailParams['to'] = []; + $mailParams['to'] = $offerDetail['email']; + + $mailParams['bcc'] = []; + $mailParams['bcc'][] = Config::get('app.logMailAddress'); + + + $mailParams['property_name'] = $offerDetail['property']['name']; + $mailParams['url'] = Config::get('app.client_server').'/offer/'.$offerDetail['offer_code']; + + $mailParams['logo'] = Config::get('app.webUrl').'/assets/img/logo/mini-logo.png'; + if(isset($offerDetail['property_brand']['logoUrl'])) { + $mailParams['logo'] = $offerDetail['property_brand']['logoUrl']; + } + + $description = []; + $description[] = __('api-mailing-offer_mail-description', ['propertyName' => $offerDetail['property']['name'], 'code' => $offerDetail['ticket_code']]); + + $description = implode('

', $description); + + /*echo view('emails.offerMail', compact('mailParams', 'description')); + die();*/ + + return $this->from($mailSenderAddress, 'Extranetwork - ' . $mailParams['property_name']) + ->view('emails.offerMail', compact('mailParams', 'description')) + ->to($mailParams['to']) + ->bcc($mailParams['bcc']) + ->subject(__('api-mailing-offer_mail-title', ['propertyName' => $offerDetail['property']['name'], 'code' => $offerDetail['ticket_code']])) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/PropertyProductOfferMail.php b/app/Core/Mail/PropertyProductOfferMail.php new file mode 100644 index 0000000..dbb2b31 --- /dev/null +++ b/app/Core/Mail/PropertyProductOfferMail.php @@ -0,0 +1,100 @@ +param = $param; + } + + public function build() + { + try { + + $params = $this->param; + + + $propertyProductOffer = PropertyProductOffer::where('offer_key', $params['offerKey'])->first(); + $propertyProductOffer = $propertyProductOffer ? $propertyProductOffer->toArray() : null; + + if (!$propertyProductOffer) { + throw new ApiErrorException('The offer was not found'); + } + + + $mailData = [ + 'to' => [ + 'name' => $propertyProductOffer['executive_name'], + 'email' => $propertyProductOffer['executive_email'], + ], + 'bcc' => ['sales@extranetwork.com'] + + ]; + + $mailData['cc'] = []; + if (isset($propertyProductOffer['account_manager_email'])) { + $mailData['cc'][] = $propertyProductOffer['account_manager_email']; + } + + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $mailParams = [ + 'property_name' => $propertyProductOffer['property_name'], + 'executive_name' => $propertyProductOffer['executive_name'], + 'executive_email' => $propertyProductOffer['executive_email'], + 'executive_phone' => $propertyProductOffer['executive_phone'], + 'offer_expire_date' => $propertyProductOffer['offerExpireTimeFormatted'], + 'logo' => 'https://www.extranetwork.com/assets/img/logo/mini-logo.png', + 'btnUrl' => config('app.url') . '/property-product-offer/' . $propertyProductOffer['offer_key'] + ]; + + if ($propertyProductOffer['version'] == 'v2') { + $mailParams['btnUrl'] = config('app.webUrl') . '/property-product-offer/' . $propertyProductOffer['offer_key']; + } + + /*echo view('emails.propertyProductOfferMail', compact('mailParams')); + die();*/ + + $subjectMail = 'Extranetwork ' . $mailParams['property_name'] . ' Fiyat Teklifi'; + if (isset($propertyProductOffer['detailArray']['paket'])) { + $subjectMail = 'Extranetwork ' . $mailParams['property_name'] . ' - ' . $propertyProductOffer['detailArray']['paket'] . ' Fiyat Teklifi'; + } + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.propertyProductOfferMail', compact('mailParams')) + ->to($mailData['to']['email'], $mailData['to']['name']) + ->cc($mailData['cc']) + ->bcc($mailData['bcc']) + ->subject($subjectMail) + ->with(['message' => $this]); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/TrialFirstMail.php b/app/Core/Mail/TrialFirstMail.php new file mode 100644 index 0000000..5448c0a --- /dev/null +++ b/app/Core/Mail/TrialFirstMail.php @@ -0,0 +1,104 @@ +param = $param; + } + + public function build() + { + try { + + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $propertyService = App::make('App\Core\Service\PropertyService'); + + $propertySelectCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['propertyId']], + + ], + 'with' => ['userPropertyMapping.user'], + 'firstRow' => true + ]; + + $propertyData = $propertyService->select($propertySelectCriteria); + + if($propertyData['status'] == 'success') { + + + $userPropertyMappingCollect = collect($propertyData['data']['user_property_mapping']); + $userPropertyMapping = $userPropertyMappingCollect->where('status',1)->where('user.status',1)->sortBy('id'); + $userPropertyMapping = $userPropertyMapping ? $userPropertyMapping->toArray() : []; + + + $defaultUser = reset($userPropertyMapping); + + $mailData['to'] = [ + 'email' => $defaultUser['user']['email'], + 'nameSurname' => $defaultUser['user']['nameSurname'], + ]; + + $mailData['bcc'] = []; + foreach ($userPropertyMapping as $user) { + //$mailData['bcc'][] = $user['user']['email']; + } + $mailData['bcc'] = 'channel@extranetwork.com';//TODO: Delete + + $mailParams= [ + 'email' => $mailData['to']['email'], + 'nameSurname' => $mailData['to']['nameSurname'], + 'propertyName' => $propertyData['data']['name'], + ]; + + + $mailTitle = 'Rezervasyonlarını ikiye katlamaya hazır mısın? 🚀'; + //app('translator')->setLocale('en'); + + //echo view('emails.reminder.trialFirstMail', compact('mailParams')); die(); + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.reminder.trialFirstMail', compact('mailParams')) + ->to($mailData['to']['email'], $mailData['to']['nameSurname']) + ->bcc($mailData['bcc']) + ->subject($mailTitle) + ->with(['message' => $this]); + + } + + + } catch (ApiErrorException $e) { + return output(['status' => -1, 'message' => $e->getMessage()]); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + + } + +} diff --git a/app/Core/Mail/TrialSecondMail.php b/app/Core/Mail/TrialSecondMail.php new file mode 100644 index 0000000..9802a09 --- /dev/null +++ b/app/Core/Mail/TrialSecondMail.php @@ -0,0 +1,104 @@ +param = $param; + } + + public function build() + { + try { + + + $params = $this->param; + $mailSenderAddress = Config::get('app.mailSenderAddress'); + + $propertyService = App::make('App\Core\Service\PropertyService'); + + $propertySelectCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['propertyId']], + + ], + 'with' => ['userPropertyMapping.user'], + 'firstRow' => true + ]; + + $propertyData = $propertyService->select($propertySelectCriteria); + + if($propertyData['status'] == 'success') { + + + $userPropertyMappingCollect = collect($propertyData['data']['user_property_mapping']); + $userPropertyMapping = $userPropertyMappingCollect->where('status',1)->where('user.status',1)->sortBy('id'); + $userPropertyMapping = $userPropertyMapping ? $userPropertyMapping->toArray() : []; + + + $defaultUser = reset($userPropertyMapping); + + $mailData['to'] = [ + 'email' => $defaultUser['user']['email'], + 'nameSurname' => $defaultUser['user']['nameSurname'], + ]; + + $mailData['bcc'] = []; + foreach ($userPropertyMapping as $user) { + //$mailData['bcc'][] = $user['user']['email']; + } + $mailData['bcc'] = 'channel@extranetwork.com';//TODO: Delete + + $mailParams= [ + 'email' => $mailData['to']['email'], + 'nameSurname' => $mailData['to']['nameSurname'], + 'propertyName' => $propertyData['data']['name'], + ]; + + + $mailTitle = 'Çok görünürlük, daha çok satış.😎'; + //app('translator')->setLocale('en'); + + //echo view('emails.reminder.trialSecondMail', compact('mailParams')); die(); + + return $this->from($mailSenderAddress, 'Extranetwork') + ->view('emails.reminder.trialSecondMail', compact('mailParams')) + ->to($mailData['to']['email'], $mailData['to']['nameSurname']) + ->bcc($mailData['bcc']) + ->subject($mailTitle) + ->with(['message' => $this]); + + } + + + } catch (ApiErrorException $e) { + return output(['status' => -1, 'message' => $e->getMessage()]); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + + } + +} diff --git a/app/Core/Mail/UserCreateMail.php b/app/Core/Mail/UserCreateMail.php new file mode 100644 index 0000000..5d6c45c --- /dev/null +++ b/app/Core/Mail/UserCreateMail.php @@ -0,0 +1,46 @@ +param = $param; + } + + + public function build() + { + try { + + $params = $this->param; + app('translator')->setLocale($params["language"]); + return $this->view('emails.createUserMail', compact('params')) + ->to($params["email"], $params["name_surname"]) + ->subject(__('api-mailing-new_user_subject')); + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Mail/UserForgotPassword.php b/app/Core/Mail/UserForgotPassword.php new file mode 100644 index 0000000..597e9ef --- /dev/null +++ b/app/Core/Mail/UserForgotPassword.php @@ -0,0 +1,48 @@ +param = $param; + } + + public function build () + { + try + { + $params = $this->param; + app('translator')->setLocale($params["language"]); + return $this->view('emails.userForgotPassword', compact('params')) + ->to($params["email"], $params["name_surname"]) + ->subject(__('api-mailing-user_forgot_password_subject')) + ->with(['message' => $this]); + + } + catch ( Exception $e ) + { + $message = $e->getFile()." ".$e->getLine()." ".$e->getMessage(); + Log::error($message); + return output( ['status' => -1, 'message' => $e->getMessage()] ); + } + } + +} diff --git a/app/Core/Mail/WellcomeMail.php b/app/Core/Mail/WellcomeMail.php new file mode 100644 index 0000000..2365bf6 --- /dev/null +++ b/app/Core/Mail/WellcomeMail.php @@ -0,0 +1,47 @@ +param = $param; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + + $val = $this->param; + Log::debug($val); + return $this->view('emails.wellCome', compact('val')) + ->to('ygundogdu@rezervasyon.com', 'yadican') + ->bcc('ygundogdu@rezervasyon.com', 'Burhan YUMAK') + ->bcc('ygundogdu@rezervasyon.com', 'Operasyon') + ->subject("WellCome Mail"); + } +} \ No newline at end of file diff --git a/app/Core/Payment/BankOfGeorgia/BankOfGeorgia.php b/app/Core/Payment/BankOfGeorgia/BankOfGeorgia.php new file mode 100644 index 0000000..3a86022 --- /dev/null +++ b/app/Core/Payment/BankOfGeorgia/BankOfGeorgia.php @@ -0,0 +1,224 @@ +restClient = new Client(); + + $this->requestUrl = 'https://ipay.ge/opay/api/v1'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://dev.ipay.ge/opay/api/v1/'; + } + + + $this->clientId = $paymentInitializeParam['clientId']; + $this->secretKey = $paymentInitializeParam['secretKey']; + + $getAccessToken = $this->getAccessToken(); + $this->accessToken = $getAccessToken['status'] ? $getAccessToken['token'] : null; + + } + + private function makeRequest($method, $payloadData, $methodType = 'POST') + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['Content-Type'] = 'application/json'; + $requestParams['headers']['Authorization'] = 'Bearer ' . $this->accessToken; + $requestParams['body'] = json_encode($payloadData); + + $result = $this->restClient->request($methodType, $this->requestUrl . '/' . $method, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if (isset($getResponseData['error_code'])) { + $response['message'] = $getResponseData['status_description']; + $response['serviceResponse'] = $getResponseData; + } else { + $response = [ + 'status' => true, + 'serviceResponse' => $getResponseData + ]; + } + + + } catch (ClientException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + Log::error($method); + Log::error($payloadData); + Log::error($response); + } + + return $response; + + } + + private function getAccessToken() + { + + $getTokenData = null; + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['Authorization'] = 'Basic ' . base64_encode($this->clientId . ':' . $this->secretKey); + $requestParams['headers']['Content-Type'] = 'application/x-www-form-urlencoded'; + $requestParams['form_params']['grant_type'] = 'client_credentials'; + + $result = $this->restClient->request('POST', $this->requestUrl . '/oauth2/token', $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if (isset($getResponseData['error_code'])) { + throw new ApiErrorException($getResponseData['error_message']); + } + + $response['status'] = true; + $response['token'] = $getResponseData['access_token']; + + } catch (ApiErrorException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function paymentInitialize($param) + { + + $response = ['status' => false, 'message' => '']; + try { + + $paymentInitializeParam = [ + 'intent' => 'CAPTURE', + 'items' => [ + [ + 'amount' => $param['amount'], + 'description' => $param['orderCode'], + 'quantity' => 1, + 'product_id' => $param['orderCode'], + ] + ], + 'locale' => 'en-US', + 'shop_order_id' => $param['orderId'], + 'redirect_url' => $param['paymentCheckUrl'], + 'capture_method' => 'AUTOMATIC', + 'purchase_units' => [ + [ + 'amount' => [ + 'currency_code' => $param['currencyCode'], + 'value' => $param['amount'], + ] + ] + ] + ]; + + $paymentInitialize = $this->makeRequest('checkout/orders', $paymentInitializeParam); + + if (!$paymentInitialize['status']) { + throw new ApiErrorException($paymentInitialize['message']); + } + + $response = [ + 'status' => true, + 'data' => $paymentInitialize['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + return $response; + + } + + public function checkoutOrderInfo($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $method = 'checkout/orders/' . $orderId; + $payloadData = []; + + $checkoutOrderInfoRequest = $this->makeRequest($method, $payloadData, 'GET'); + + if (!$checkoutOrderInfoRequest['status']) { + throw new ApiErrorException($checkoutOrderInfoRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $checkoutOrderInfoRequest['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($checkoutOrderInfoRequest['serviceResponse'])) { + $response['serviceResponse'] = $checkoutOrderInfoRequest['serviceResponse']; + } + + return $response; + + } + +} diff --git a/app/Core/Payment/Esnekpos/Esnekpos.php b/app/Core/Payment/Esnekpos/Esnekpos.php new file mode 100644 index 0000000..85e8b3d --- /dev/null +++ b/app/Core/Payment/Esnekpos/Esnekpos.php @@ -0,0 +1,239 @@ +restClient = new Client(); + + $this->requestUrl = 'https://posservice.esnekpos.com'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://posservicetest.esnekpos.com'; + } + + + $this->merchant = $paymentInitializeParam['merchant']; + $this->merchantKey = $paymentInitializeParam['merchantKey']; + $this->contactMail = $paymentInitializeParam['contactMail']; + $this->ipAddress = isset($paymentInitializeParam['ipAddress']) ? $paymentInitializeParam['ipAddress'] : '185.137.215.118'; + + $this->currencyMapping = [ + 'TRY' => 'TRY', + 'USD' => 'USD', + 'EUR' => 'EUR', + 'GBP' => 'GBP', + ]; + + } + + private function makeRequest($method, $payloadData) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['Content-Type'] = 'application/json'; + + $requestParams['body'] = json_encode($payloadData); + + $result = $this->restClient->post($this->requestUrl . '/' . $method, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if ($getResponseData['STATUS'] == 'SUCCESS') { + $response = [ + 'status' => true, + 'serviceResponse' => $getResponseData + ]; + } else { + throw new Exception($getResponseData['RETURN_MESSAGE']); + } + + } catch (ClientException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + $response['message'] = $message; + Log::debug($message); + + } catch (ServerException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } + + if (!$response['status']) { + Log::error($method); + Log::error($payloadData); + Log::error($response); + } + + if (isset($getResponseData)) { + $response['serviceResponse'] = $getResponseData; + } + + return $response; + + } + + + public function generateHashKey($total, $installment, $currency_code, $invoice_id) + { + + $data = $total . '|' . $installment . '|' . $currency_code . '|' . $this->merchantKey . '|' . $invoice_id; + + $iv = substr(sha1(mt_rand()), 0, 16); + $password = sha1($this->appSecret); + + $salt = substr(sha1(mt_rand()), 0, 4); + $saltWithPassword = hash('sha256', $password . $salt); + + $encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv); + + $msg_encrypted_bundle = "$iv:$salt:$encrypted"; + $msg_encrypted_bundle = str_replace('/', '__', $msg_encrypted_bundle); + + return $msg_encrypted_bundle; + } + + public function EYV3DPay($param) + { + + $response = ['status' => false, 'message' => '']; + try { + + $param['creditCard']['installment'] = $param['creditCard']['installment'] == 0 ? 1 : $param['creditCard']['installment']; + + $items = []; + $items[] = [ + 'PRODUCT_ID' => $param['orderCode'], + 'PRODUCT_NAME' => 'Booking', + 'PRODUCT_CATEGORY' => 'Booking', + 'PRODUCT_DESCRIPTION' => 'Booking', + 'PRODUCT_AMOUNT' => $param['amount'] + ]; + + $method = 'api/pay/EYV3DPay'; + $payloadData = [ + 'Config' => [ + 'MERCHANT' => $this->merchant, + 'MERCHANT_KEY' => $this->merchantKey, + 'BACK_URL' => $param['paymentCheckUrl'], + 'PRICES_CURRENCY' => isset($this->currencyMapping[$param['currencyCode']]) ? $this->currencyMapping[$param['currencyCode']] : $param['currencyCode'], + 'ORDER_REF_NUMBER' => $param['orderId'], + 'ORDER_AMOUNT' => $param['amount'], + ], + 'CreditCard' => [ + 'CC_NUMBER' => $param['creditCard']['number'], + 'EXP_MONTH' => $param['creditCard']['expiryMonth'], + 'EXP_YEAR' => $param['creditCard']['expiryYear'], + 'CC_CVV' => $param['creditCard']['cvv'], + 'CC_OWNER' => $param['creditCard']['holderName'], + 'INSTALLMENT_NUMBER' => $param['creditCard']['installment'], + ], + 'Customer' => [ + 'FIRST_NAME' => $param['orderId'], + 'LAST_NAME' => $param['orderId'], + 'MAIL' => $this->contactMail, + 'PHONE' => '5555555555', + 'CITY' => $param['orderId'], + 'STATE' => $param['orderId'], + 'ADDRESS' => $param['orderId'], + 'CLIENT_IP' => $this->ipAddress, + ], + 'Product' => $items + ]; + + $checkRequest = $this->makeRequest($method, $payloadData); + + if (!$checkRequest['status']) { + throw new ApiErrorException($checkRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $checkRequest['serviceResponse'] + ]; + + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + return $response; + + } + + public function checkPaymentStatus($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $method = 'api/services/ProcessQuery'; + $payloadData = [ + 'MERCHANT' => $this->merchant, + 'MERCHANT_KEY' => $this->merchantKey, + 'ORDER_REF_NUMBER' => $orderId, + ]; + + $checkStatusRequest = $this->makeRequest($method, $payloadData); + + if (!$checkStatusRequest['status']) { + throw new ApiErrorException($checkStatusRequest['message']); + } + + if ($checkStatusRequest['serviceResponse']['STATUS'] != 'SUCCESS') { + throw new ApiErrorException($checkStatusRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $checkStatusRequest['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($checkStatusRequest['serviceResponse'])) { + $response['serviceResponse'] = $checkStatusRequest['serviceResponse']; + } + + return $response; + + } + +} diff --git a/app/Core/Payment/HalkOde/HalkOde.php b/app/Core/Payment/HalkOde/HalkOde.php new file mode 100644 index 0000000..84cade1 --- /dev/null +++ b/app/Core/Payment/HalkOde/HalkOde.php @@ -0,0 +1,254 @@ +restClient = new Client(); + + $this->requestUrl = 'https://app.halkode.com.tr/ccpayment'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://test.halkode.com.tr/ccpayment'; + } + + + $this->merchantId = $paymentInitializeParam['merchantId']; + $this->merchantKey = $paymentInitializeParam['merchantKey']; + $this->appKey = $paymentInitializeParam['appKey']; + $this->appSecret = $paymentInitializeParam['appSecret']; + + $this->getAccessToken = $this->getAccessToken(); + + } + + private function makeRequest($method, $payloadData) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['Content-Type'] = 'application/json'; + if ($method != 'api/token') { + $requestParams['headers']['Authorization'] = 'Bearer ' . $this->getAccessToken; + } + + + $requestParams['body'] = json_encode($payloadData); + + $result = $this->restClient->post($this->requestUrl . '/' . $method, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if ($getResponseData['status_code'] == 100) { + $response = [ + 'status' => true, + 'serviceResponse' => $getResponseData + ]; + } else { + $response['message'] = $getResponseData['status_description']; + $response['serviceResponse'] = $getResponseData; + } + + + } catch (ClientException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + Log::error($method); + Log::error($payloadData); + Log::error($response); + } + + return $response; + + } + + + private function getAccessToken() + { + + $method = 'api/token'; + $payloadData = [ + 'app_id' => $this->appKey, + 'app_secret' => $this->appSecret, + ]; + + $getTokenDataRequest = $this->makeRequest($method, $payloadData); + + if ($getTokenDataRequest['status']) { + $getTokenData = $getTokenDataRequest['serviceResponse']['data']['token']; + } + + return $getTokenData; + } + + public function generateHashKey($total, $installment, $currency_code, $invoice_id) + { + + $data = $total . '|' . $installment . '|' . $currency_code . '|' . $this->merchantKey . '|' . $invoice_id; + + $iv = substr(sha1(mt_rand()), 0, 16); + $password = sha1($this->appSecret); + + $salt = substr(sha1(mt_rand()), 0, 4); + $saltWithPassword = hash('sha256', $password . $salt); + + $encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv); + + $msg_encrypted_bundle = "$iv:$salt:$encrypted"; + $msg_encrypted_bundle = str_replace('/', '__', $msg_encrypted_bundle); + + return $msg_encrypted_bundle; + } + + public function generateRefundHashKey($invoice_id, $merchant_key, $app_secret) + { + $data = $invoice_id . '|' . $merchant_key; + $iv = substr(sha1(mt_rand()), 0, 16); + $password = sha1($app_secret); + $salt = substr(sha1(mt_rand()), 0, 4); + $saltWithPassword = hash('sha256', $password . $salt); + $encrypted = openssl_encrypt( + $data, 'aes-256-cbc', "$saltWithPassword", null, $iv + ); + $msg_encrypted_bundle = "$iv:$salt:$encrypted"; + $hash_key = str_replace('/', '__', $msg_encrypted_bundle); + return $hash_key; + } + + public function paySmart3D($param) + { + + $response = ['status' => false, 'message' => '']; + try { + + $param['creditCard']['installment'] = $param['creditCard']['installment'] == 0 ? 1 : $param['creditCard']['installment']; + + $generateHashKey = $this->generateHashKey($param['amount'], $param['creditCard']['installment'], $param['currencyCode'], $param['orderId']); + + $items = []; + $items[] = [ + 'name' => 'Booking', + 'price' => $param['amount'], + 'quantity' => 1, + 'description' => $param['orderCode'], + ]; + + $payment3dFormData['gateway'] = $this->requestUrl . '/api/paySmart3D'; + + $payment3dFormData['inputs'] = [ + 'currency_code' => $param['currencyCode'], + 'installments_number' => $param['creditCard']['installment'], + 'invoice_id' => $param['orderId'], + 'invoice_description' => $param['orderCode'], + 'total' => $param['amount'], + 'merchant_key' => $this->merchantKey, + 'items' => json_encode($items), + //'name' => $param['name'], + //'surname' => $param['surname'], + 'hash_key' => $generateHashKey, + 'return_url' => $param['paymentCheckUrl'], + 'cancel_url' => $param['paymentCheckUrl'], + 'cc_holder_name' => $param['creditCard']['holderName'], + 'cc_no' => $param['creditCard']['number'], + 'expiry_month' => $param['creditCard']['expiryMonth'], + 'expiry_year' => $param['creditCard']['expiryYear'], + 'cvv' => $param['creditCard']['cvv'], + 'transaction_type' => 'Auth', + 'is_comission_from_user' => '2', + 'response_method' => 'GET', + ]; + + $response = [ + 'status' => true, + 'data' => $payment3dFormData + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + return $response; + + } + + public function checkPaymentStatus($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $generateHashKey = $this->generateRefundHashKey($orderId, $this->merchantKey, $this->appSecret); + + $method = 'api/checkstatus'; + $payloadData = [ + 'invoice_id' => $orderId, + 'merchant_key' => $this->merchantKey, + 'include_pending_status' => true, + 'hash_key' => $generateHashKey + ]; + + $checkstatusRequest = $this->makeRequest($method, $payloadData); + + if (!$checkstatusRequest['status']) { + throw new ApiErrorException($checkstatusRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $checkstatusRequest['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($checkstatusRequest['serviceResponse'])) { + $response['serviceResponse'] = $checkstatusRequest['serviceResponse']; + } + + return $response; + + } + +} diff --git a/app/Core/Payment/KuveytTurk/KuveytTurk.php b/app/Core/Payment/KuveytTurk/KuveytTurk.php new file mode 100644 index 0000000..918e87c --- /dev/null +++ b/app/Core/Payment/KuveytTurk/KuveytTurk.php @@ -0,0 +1,241 @@ +restClient = new Client(); + + $this->requestUrl = 'https://boa.kuveytturk.com.tr/sanalposservice/Home'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home'; + } + + $this->customerId = $paymentInitializeParam['customerId']; + $this->merchantId = $paymentInitializeParam['merchantId']; + $this->userName = $paymentInitializeParam['userName']; + $this->password = $paymentInitializeParam['password']; + + $this->currencyCodeMapping = [ + 'TRY' => '0949', + 'USD' => '0840', + 'EUR' => '0978', + ]; + + } + + protected function post($param) + { + $getResponse = []; + $param['logDebug'] = fillOnUndefined($param, 'logDebug', false); + try { + + $paymentRequest = $this->restClient->post($this->requestUrl . '/' . $param['method'], [ + 'headers' => [ + 'Content-Type' => 'application/xml; charset=UTF8', + ], + 'body' => $param['payload'] + ]); + + $getResponseBody = $paymentRequest->getBody(); + $getResponseXmlBase = $getResponseBody->getContents(); + + if (!$getResponseXmlBase) { + throw new ApiErrorException('Payment Initialize not processing.'); + } else { + + $response = [ + 'status' => true, + 'data' => $getResponseXmlBase + ]; + } + + if ($param['logDebug']) { + Log::debug(json_encode($param['param'])); + Log::debug($param['param']); + Log::debug($getResponse); + } + + } catch (ApiErrorException | Exception | ClientException | ServerException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function threeDModelPayGate($param = []) + { + + try { + + $hashData = null; + $param['amount'] = (int)(floatval($param['amount']) * 100); + $hashedPassword = base64_encode(sha1($this->password, "ISO-8859-9")); + $hashData = base64_encode(sha1($this->merchantId . $param['orderId'] . $param['amount'] . $param['paymentCheckUrl'] . $param['paymentCheckUrl'] . $this->userName . $hashedPassword, "ISO-8859-9")); + + $requestParam['method'] = 'ThreeDModelPayGate'; + $requestPayload = new \SimpleXMLElement(''); + + $currencyCodeBank = isset($this->currencyCodeMapping[$param['currencyCode']]) ? $this->currencyCodeMapping[$param['currencyCode']] : '0949'; + + $requestPayload->addChild('APIVersion', '1.0.0'); + $requestPayload->addChild('OkUrl', $param['paymentCheckUrl']); + $requestPayload->addChild('FailUrl', $param['paymentCheckUrl']); + $requestPayload->addChild('HashData', $hashData); + $requestPayload->addChild('MerchantId', $this->merchantId); + $requestPayload->addChild('CustomerId', $this->customerId); + $requestPayload->addChild('UserName', $this->userName); + $requestPayload->addChild('CardNumber', $param['creditCard']['number']); + $requestPayload->addChild('CardExpireDateYear', mb_substr($param['creditCard']['expiryYear'], 2, 2)); + $requestPayload->addChild('CardExpireDateMonth', $param['creditCard']['expiryMonth']); + $requestPayload->addChild('CardCVV2', $param['creditCard']['cvv']); + $requestPayload->addChild('CardHolderName', $param['creditCard']['holderName']); + //$requestPayload->addChild('CardType', null); + $requestPayload->addChild('BatchID', 0); + $requestPayload->addChild('TransactionType', 'Sale'); + $requestPayload->addChild('InstallmentCount', $param['creditCard']['installment']); + $requestPayload->addChild('Amount', $param['amount']); + $requestPayload->addChild('DisplayAmount', $param['amount']); + $requestPayload->addChild('CurrencyCode', $currencyCodeBank); + $requestPayload->addChild('MerchantOrderId', $param['orderId']); + $requestPayload->addChild('TransactionSecurity', 3); + + $requestParam['payload'] = $requestPayload->asXML(); + + $paymentRequest = $this->post($requestParam); + + if (!$paymentRequest['status']) { + throw new ApiErrorException($paymentRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $paymentRequest['data'] + ]; + + } catch (ApiErrorException | Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } + + if (isset($getResponseData)) { + $response['serviceResponse'] = $paymentRequest; + } + + + return $response; + } + + public function checkPaymentStatus($paymentCode, $param = []) + { + $response = ['status' => false, 'message' => '']; + + $requestContent = null; + $paymentRequest = []; + + try { + + $requestContent = urldecode($param["AuthenticationResponse"]); + $requestContent = json_decode(json_encode(simplexml_load_string($requestContent)), 1); + + + if ($requestContent['ResponseCode'] != "00") { + $errorMessage = $requestContent['ResponseCode'] . ': ' . $requestContent['ResponseMessage']; + $paymentRequest['serviceResponse'] = $requestContent; + throw new ApiErrorException($errorMessage); + } + + $paymentCheckParam = [ + 'merchantOrderId' => isset($requestContent['VPosMessage']['MerchantOrderId']) ? $requestContent['VPosMessage']['MerchantOrderId'] : null, + 'amount' => isset($requestContent['VPosMessage']['Amount']) ? $requestContent['VPosMessage']['Amount'] : null, + 'md' => isset($requestContent['MD']) ? $requestContent['MD'] : null, + ]; + + $hashData = null; + //$param['amount'] = (int)(floatval($param['amount']) * 100); + $hashedPassword = base64_encode(sha1($this->password, "ISO-8859-9")); + $hashData = base64_encode(sha1($this->merchantId . $paymentCheckParam['merchantOrderId'] . $paymentCheckParam['amount'] . $this->userName . $hashedPassword, "ISO-8859-9")); + + + $requestParam['method'] = 'ThreeDModelProvisionGate'; + $requestPayload = new \SimpleXMLElement(''); + + $requestPayload->addChild('APIVersion', '1.0.0'); + $requestPayload->addChild('HashData', $hashData); + $requestPayload->addChild('MerchantId', $this->merchantId); + $requestPayload->addChild('CustomerId', $this->customerId); + $requestPayload->addChild('UserName', $this->userName); + $requestPayload->addChild('TransactionType', 'Sale'); + $requestPayload->addChild('InstallmentCount', 1); + $requestPayload->addChild('CurrencyCode', '0949'); + $requestPayload->addChild('Amount', $paymentCheckParam['amount']); + $requestPayload->addChild('MerchantOrderId', $paymentCheckParam['merchantOrderId']); + $requestPayload->addChild('TransactionSecurity', 3); + + $kuveytTurkVPosAdditionalData = $requestPayload->addChild('KuveytTurkVPosAdditionalData'); + $additionalData = $kuveytTurkVPosAdditionalData->addChild('AdditionalData'); + $additionalData->addChild('Key', 'MD'); + $additionalData->addChild('Data', $paymentCheckParam['md']); + + $requestParam['payload'] = $requestPayload->asXML(); + + $paymentRequest = $this->post($requestParam); + + if (!$paymentRequest['status']) { + throw new ApiErrorException($paymentRequest['message']); + } + + $requestContent = simplexml_load_string($paymentRequest['data']); + $requestContent = json_decode(json_encode($requestContent), 1); + + $paymentRequest['serviceResponse'] = $requestContent; + + if ($requestContent['ResponseCode'] != "00") { + $errorMessage = $requestContent['ResponseCode'] . ': ' . $requestContent['ResponseMessage']; + throw new ApiErrorException($errorMessage); + } + + $response = [ + 'status' => true, + 'data' => $requestContent + ]; + + } catch (ApiErrorException | Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } + + if (isset($paymentRequest['serviceResponse'])) { + $response['serviceResponse'] = $paymentRequest['serviceResponse']; + } + + + return $response; + } + +} diff --git a/app/Core/Payment/Moka/Moka.php b/app/Core/Payment/Moka/Moka.php new file mode 100644 index 0000000..9924a2c --- /dev/null +++ b/app/Core/Payment/Moka/Moka.php @@ -0,0 +1,176 @@ +restClient = new Client(); + + $this->requestUrl = 'https://service.moka.com'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://service.refmoka.com'; + } + + $this->userName = $paymentInitializeParam['userName']; + $this->password = $paymentInitializeParam['password']; + $this->dealerCode = $paymentInitializeParam['dealerCode']; + + } + + private function errorCodes($code) + { + $codes = [ + md5('PaymentDealer.CheckPaymentDealerAuthentication.InvalidRequest') => 'Hatalı hash bilgisi', + md5('PaymentDealer.CheckPaymentDealerAuthentication.InvalidAccount') => 'Böyle bir bayi bulunamadı', + md5('PaymentDealer.CheckPaymentDealerAuthentication.VirtualPosNotFound') => 'Bu bayi için sanal pos tanımı yapılmamış', + md5('PaymentDealer.RequiredFields.AmountRequired') => 'Tutar girilmemiş', + md5('PaymentDealer.DoCapture.DealerNotAuthorized') => 'Ön provizyonu provizyona çevirmek için yetkiniz yok', + md5('PaymentDealer.DoDirectPayment3dRequest.CardTokenNotFound') => 'Tanımlı CardToken bulunamadı', + md5('PaymentDealer.CheckCardInfo.InvalidCardInfo') => 'Geçersiz Kart Bilgisi', + md5('PaymentDealer.DoDirectPayment3dRequest.VirtualPosNotAvailable') => 'Bu kredi kartı için ödeme desteklenmemektedir, lütfen farklı bir kart deneyiniz', + md5('PaymentDealer.CheckDealerPaymentLimits.DailyDealerLimitExceeded') => 'Bu kredi kartı için günlük kullanım limiti dolmuştur, lütfen farklı bir kart deneyiniz', + md5('PaymentDealer.DoDirectPayment3dRequest.ThisInstallmentNumberNotAvailableForDealer') => 'Bu taksit sayısı kullanılamaz', + md5('EX') => 'Beklenmeyen bir hata oluştu' + ]; + + return fillOnUndefined($codes, md5($code), 'Bilinmeyen bir hata oluştu'); + } + + private function mokaServiceResponse($getResponse, $param) + { + $response = ['status' => false, 'message' => '', 'error' => '', 'data' => []]; + + if ($getResponse['ResultCode'] != 'Success') { + $response['message'] = $this->errorCodes($getResponse['ResultCode']); + //Log::error($getResponse); + } else { + $response = [ + 'status' => true, + 'data' => $getResponse['Data'] + ]; + } + + return $response; + + } + + protected function post($param) + { + $getResponse = []; + $param['logDebug'] = fillOnUndefined($param, 'logDebug', false); + try { + + $res = $this->restClient->post($this->requestUrl. $param['method'], [ + 'verify' => false, + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'body' => json_encode($param['param']) + ]); + + $getResponseBody = $res->getBody(); + $getResponse = json_decode($getResponseBody, true); + + if ($param['logDebug']) { + Log::debug(json_encode($param['param'])); + Log::debug($param['param']); + Log::debug($getResponse); + } + + $getResponse = $this->mokaServiceResponse($getResponse, $param); + + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + return $getResponse; + + } + + public function paymentDealerAuthentication() + { + return [ + 'DealerCode' => $this->dealerCode, + 'Username' => $this->userName, + 'Password' => $this->password, + 'CheckKey' => hash('sha256', $this->dealerCode . 'MK' . $this->userName . 'PD' . $this->password) + ]; + } + + public function DoDirectPaymentThreeD($param = []) + { + + try { + + $doDirectPaymentThreeD['method'] = '/PaymentDealer/DoDirectPaymentThreeD'; + $doDirectPaymentThreeD['param']['PaymentDealerAuthentication'] = $this->paymentDealerAuthentication(); + $doDirectPaymentThreeD['param']['PaymentDealerRequest'] = [ + 'CardHolderFullName' => fillOnUndefined($param, 'CardHolderFullName'), + 'CardNumber' => fillOnUndefined($param, 'CardNumber', ''), + 'ExpMonth' => fillOnUndefined($param, 'ExpMonth'),//12 + 'ExpYear' => fillOnUndefined($param, 'ExpYear'),//2022 + 'CvcNumber' => fillOnUndefined($param, 'CvcNumber'), + 'CardToken' => fillOnUndefined($param, 'CardToken'), + 'Amount' => fillOnUndefined($param, 'Amount'),//10.50 + 'Currency' => fillOnUndefined($param, 'Currency', 'TL'), + 'InstallmentNumber' => fillOnUndefined($param, 'InstallmentNumber', 1), + 'ClientIP' => fillOnUndefined($param, 'ClientIP', Request::ip()), + 'OtherTrxCode' => fillOnUndefined($param, 'OtherTrxCode', ''), + 'IsPreAuth' => fillOnUndefined($param, 'IsPreAuth', 1), + 'Software' => fillOnUndefined($param, 'Software', ''), + 'Description' => fillOnUndefined($param, 'Description', ''), + 'RedirectUrl' => fillOnUndefined($param, 'RedirectUrl'), + ]; + + $response = $this->post($doDirectPaymentThreeD); + + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } + + return $response; + } + + public function getPaymentDetail($param = []) + { + try { + + $getPaymentList['method'] = '/PaymentDealer/GetDealerPaymentTrxDetailList'; + $getPaymentList['param']['PaymentDealerAuthentication'] = $this->paymentDealerAuthentication(); + $getPaymentList['param']['PaymentDealerRequest'] = [ + 'PaymentId' => fillOnUndefined($param, 'PaymentId', ''), + 'OtherTrxCode' => fillOnUndefined($param, 'OtherTrxCode', ''), + ]; + + $response = $this->post($getPaymentList); + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } + + return $response; + } + +} diff --git a/app/Core/Payment/Payriff/Payriff.php b/app/Core/Payment/Payriff/Payriff.php new file mode 100644 index 0000000..2d26368 --- /dev/null +++ b/app/Core/Payment/Payriff/Payriff.php @@ -0,0 +1,170 @@ +restClient = new Client(); + + $this->requestUrl = 'https://api.payriff.com/api/v3'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://api.payriff.com/api/v3'; + } + + + $this->secretKey = $paymentInitializeParam['secretKey']; + + + } + + private function makeRequest($method, $payloadData, $methodType = 'POST') + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['Content-Type'] = 'application/json'; + $requestParams['headers']['Authorization'] = $this->secretKey; + $requestParams['body'] = json_encode($payloadData); + + + $result = $this->restClient->request($methodType, $this->requestUrl . '/' . $method, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + + if ($getResponseData['code'] == '00000') { + $response = [ + 'status' => true, + 'serviceResponse' => $getResponseData + ]; + } else { + $response['message'] = 'Redirect url not found.'; + $response['serviceResponse'] = $getResponseData; + } + + + } catch (ClientException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + Log::error($method); + Log::error($payloadData); + Log::error($response); + } + + return $response; + + } + + public function paymentInitialize($param) + { + + $response = ['status' => false, 'message' => '']; + try { + + $paymentInitializeParam = [ + 'amount' => $param['amount'], + 'language' => 'EN', + 'currency' => $param['currencyCode'], + 'description' => $param['orderId'], + 'callbackUrl' => $param['paymentCheckUrl'], + 'cardSave' => false, + 'operation' => 'PURCHASE', + 'metadata' => [ + 'orderId' => $param['orderId'], + ] + ]; + + $paymentInitialize = $this->makeRequest('orders', $paymentInitializeParam); + + if (!$paymentInitialize['status']) { + throw new ApiErrorException($paymentInitialize['message']); + } + + $response = [ + 'status' => true, + 'data' => $paymentInitialize['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + return $response; + + } + + public function orderInformation($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $method = 'orders/' . $orderId; + $payloadData = []; + + $orderInformationRequest = $this->makeRequest($method, $payloadData, 'GET'); + + if (!$orderInformationRequest['status']) { + throw new ApiErrorException($orderInformationRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $orderInformationRequest['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($orderInformationRequest['serviceResponse'])) { + $response['serviceResponse'] = $orderInformationRequest['serviceResponse']; + } + + return $response; + + } + +} diff --git a/app/Core/Payment/Pos/AkPos.php b/app/Core/Payment/Pos/AkPos.php new file mode 100644 index 0000000..68b961b --- /dev/null +++ b/app/Core/Payment/Pos/AkPos.php @@ -0,0 +1,701 @@ + 'approved', + '01' => 'bank_call', + '02' => 'bank_call', + '05' => 'reject', + '09' => 'try_again', + '12' => 'invalid_transaction', + '28' => 'reject', + '51' => 'insufficient_balance', + '54' => 'expired_card', + '57' => 'does_not_allow_card_holder', + '62' => 'restricted_card', + '77' => 'request_rejected', + '99' => 'general_error', + ]; + + /** + * Transaction Types + * + * @var array + */ + public $types = [ + 'pay' => 'Auth', + 'pre' => 'PreAuth', + 'post' => 'PostAuth', + ]; + + /** + * Currencies + * + * @var array + */ + public $currencies = []; + + /** + * Transaction Type + * + * @var string + */ + public $type; + + /** + * API Account + * + * @var array + */ + protected $account = []; + + /** + * Order Details + * + * @var array + */ + protected $order = []; + + /** + * Credit Card + * + * @var object + */ + protected $card; + + /** + * Request + * + * @var Request + */ + protected $request; + + /** + * Response Raw Data + * + * @var object + */ + protected $data; + + /** + * Processed Response Data + * + * @var mixed + */ + public $response; + + /** + * Configuration + * + * @var array + */ + protected $config = []; + + /** + * Response Raw Data + * + * @var object + */ + protected $mbrId = 5; + + /** + * EstPos constructor. + * + * @param array $config + * @param mixed $account + * @param array $currencies + */ + public function __construct($config, $account, array $currencies) + { + $this->config = $config; + $this->account = $account; + $this->currencies = $currencies; + + $this->url = isset($this->config['urls'][$this->account->env]) ? + $this->config['urls'][$this->account->env] : + $this->config['urls']['production']; + + $this->gateway = isset($this->config['urls']['gateway'][$this->account->env]) ? + $this->config['urls']['gateway'][$this->account->env] : + $this->config['urls']['gateway']['production']; + + + return $this; + } + + + /** + * Get ProcReturnCode + * + * @return string|null + */ + protected function getRandomNumberBase16() + { + $n = 128; + + $characters = '0123456789ABCDEF'; + $randomString = ''; + + for ($i = 0; $i < $n; $i++) { + $index = rand(0, strlen($characters) - 1); + $randomString .= $characters[$index]; + } + + return strtoupper($randomString); + + } + + /** + * Create Regular Payment XML + * + * @return string + */ + protected function createRegularPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'CardType' => isset($this->card->type) ? $this->card->type : null, + 'Number' => $this->card->number, + 'Expires' => $this->card->month . '/' . $this->card->year, + 'Cvv2Val' => $this->card->cvv, + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + 'BillTo' => [ + 'Name' => $this->order->name ? $this->order->name : null, + ] + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create Regular Payment Post XML + * + * @return string + */ + protected function createRegularPostXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->types[$this->order->transaction], + 'OrderId' => $this->order->id, + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create 3D Payment XML + * @return string + */ + protected function create3DPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'Number' => $this->request->get('md'), + 'Expires' => '', + 'Cvv2Val' => '', + 'PayerTxnId' => $this->request->get('xid'), + 'PayerSecurityLevel' => $this->request->get('eci'), + 'PayerAuthenticationCode' => $this->request->get('cavv'), + 'CardholderPresentCode' => '13', + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + ] + ]; + + if ($this->order->name) { + $nodes['BillTo'] = [ + 'Name' => $this->order->name, + ]; + } + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Get ProcReturnCode + * + * @return string|null + */ + protected function getProcReturnCode() + { + return isset($this->data->ProcReturnCode) ? (string)$this->data->ProcReturnCode : null; + } + + /** + * Get Status Detail Text + * + * @return string|null + */ + protected function getStatusDetail() + { + $proc_return_code = $this->getProcReturnCode(); + + return $proc_return_code ? (isset($this->codes[$proc_return_code]) ? (string)$this->codes[$proc_return_code] : null) : null; + } + + /** + * Create 3D Hash + * + * @return string + */ + public function create3DHash() + { + $hashItems = []; + + $hashItems[] = mb_strtoupper($this->account->model); + $hashItems[] = $this->txnCode; + $hashItems[] = $this->account->merchant_safe_id; + $hashItems[] = $this->account->terminal_safe_id; + $hashItems[] = $this->order->id; + $hashItems[] = mb_strtoupper($this->order->lang); + $hashItems[] = $this->order->amount; + //$hashItems[] = $this->order->ccbRewardAmount; + //$hashItems[] = $this->order->pcbRewardAmount; + //$hashItems[] = $this->order->xcbRewardAmount; + $hashItems[] = $this->order->currency; + $hashItems[] = $this->order->installment; + $hashItems[] = $this->order->success_url; + $hashItems[] = $this->order->fail_url; + //$hashItems[] = $this->order->emailAddress; + //$hashItems[] = $this->order->subMerchantId; + $hashItems[] = $this->card->number; + $hashItems[] = $this->getCardExpDate(); + $hashItems[] = $this->card->cvv; + $hashItems[] = $this->order->rand; + $hashItems[] = $this->order->requestDateTime; + //$hashItems[] = $this->order->b2bIdentityNumber; + + $hashItemsString = implode('', $hashItems); + + $hashItemsStringHashed = hash_hmac('sha512', $hashItemsString, $this->account->secret_key, true); + + return base64_encode($hashItemsStringHashed); + } + + /** + * Check 3D Hash + * + * @param array $data + * @return bool + */ + public function check3DHash($data) + { + $return = false; + + $params = explode('+', $data['hashParams']); + + $builder = ''; + foreach ($params as $param) { + $builder .= $data[$param]; + } + + $hashItemsStringHashed = hash_hmac('sha512', $builder, $this->account->secret_key, true); + $hashItemsStringHashed = base64_encode($hashItemsStringHashed); + + if ($data['hash'] == $hashItemsStringHashed) { + $return = true; + } + + return $return; + } + + /** + * Regular Payment + * + * @return $this + * @throws GuzzleException + */ + public function makeRegularPayment() + { + return false; + } + + /** + * Make 3D Payment + * + * @return $this + * @throws GuzzleException + */ + public function make3DPayment() + { + return false; + } + + /** + * Make 3D Pay Payment + * + * @return $this + */ + public function make3DPayPayment() + { + $this->request = Request::createFromGlobals(); + + $status = 'declined'; + + if ($this->check3DHash($this->request->request->all()) && (string)$this->request->get('responseCode') == 'VPS-0000') { + $status = 'approved'; + } + + $transaction_security = 'MPI fallback'; + if ($status == 'approved') { + //if ($this->request->get('3DStatus') == '1') { + $transaction_security = 'Full 3D Secure'; + // } elseif (in_array($this->request->get('3DStatus'), [2, 3, 4])) { + //$transaction_security = 'Half 3D Secure'; + //} + } + + + $this->response = (object)[ + 'id' => (string)$this->request->get('authCode'), + 'order_id' => (string)$this->request->get('orderId'), + 'trans_id' => (string)$this->request->get('rrn'), + 'response' => (string)$this->request->get('responseCode'), + 'merchantSafeId' => (string)$this->request->get('merchantSafeId'), + 'terminalSafeId' => (string)$this->request->get('terminalSafeId'), + 'transaction_type' => $this->type, + 'transaction' => (string)$this->request->get('rrn'), + 'transaction_security' => $transaction_security, + 'auth_code' => (string)$this->request->get('authCode'), + 'host_ref_num' => (string)$this->request->get('rrn'), + 'proc_return_code' => (string)$this->request->get('hostResponseCode'), + 'code' => (string)$this->request->get('responseCode'), + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => (string)$this->request->get('responseCode') != 'VPS-0000' ? (string)$this->request->get('responseCode') : null, + 'error_message' => (string)$this->request->get('responseCode') != 'VPS-0000' ? (string)$this->request->get('responseMessage') : null, + 'md_status' => (string)$this->request->get('md_status'), + 'hash' => (string)$this->request->get('hash'), + 'rand' => null, + 'hash_params' => (string)$this->request->get('hashParams'), + 'hash_params_val' => (string)$this->request->get('hash'), + 'amount' => (string)$this->request->get('amount'), + 'md_error_message' => (string)$this->request->get('responseCode') != 'VPS-0000' ? (string)$this->request->get('responseMessage') : null, + 'all' => $this->request->request->all() + ]; + + + return $this; + } + + /** + * Get 3d Form Data + * + * @return array + */ + public function get3DFormData() + { + $data = []; + + if ($this->order) { + + $this->order->amount = number_format($this->order->amount, 2, '.', ''); + $this->order->rand = $this->getRandomNumberBase16(); + $this->order->requestDateTime = mb_substr(Carbon::now()->toISOString(), 0, 23); + $this->order->installment = $this->order->installment == 0 ? 1 : $this->order->installment; + + $this->order->hash = $this->create3DHash(); + + $inputs = [ + 'paymentModel' => mb_strtoupper($this->account->model), + 'txnCode' => $this->txnCode, + 'merchantSafeId' => $this->account->merchant_safe_id, + 'terminalSafeId' => $this->account->terminal_safe_id, + 'orderId' => $this->order->id, + 'lang' => mb_strtoupper($this->order->lang), + 'amount' => $this->order->amount, + 'currencyCode' => $this->order->currency, + 'installCount' => $this->order->installment, + 'okUrl' => $this->order->success_url, + 'failUrl' => $this->order->fail_url, + 'creditCard' => $this->card->number, + 'expiredDate' => $this->getCardExpDate(), + 'cvv' => $this->card->cvv, + 'randomNumber' => $this->order->rand, + 'requestDateTime' => $this->order->requestDateTime, + 'hash' => $this->order->hash, + ]; + + $data = [ + 'gateway' => $this->gateway, + 'success_url' => $this->order->success_url, + 'fail_url' => $this->order->fail_url, + 'rand' => $this->order->rand, + 'hash' => $this->order->hash, + 'inputs' => $inputs, + ]; + + //$formData = $data; + //echo view('threeDSecureForm', compact('formData'));die(); + + } + + return $data; + } + + /** + * Send contents to WebService + * + * @param $contents + * @return $this + * @throws GuzzleException + */ + public function send($contents) + { + $client = new Client(); + + $response = $client->request('POST', $this->url, [ + 'body' => $contents + ]); + + $this->data = $this->XMLStringToObject($response->getBody()->getContents()); + + return $this; + } + + /** + * Prepare Order + * + * @param object $order + * @param object null $card + * @return mixed + * @throws UnsupportedTransactionTypeException + */ + public function prepare($order, $card = null) + { + $this->type = $this->types['pay']; + if (isset($order->transaction)) { + if (array_key_exists($order->transaction, $this->types)) { + $this->type = $this->types[$order->transaction]; + } else { + throw new UnsupportedTransactionTypeException('Unsupported transaction type!'); + } + } + + $this->order = $order; + $this->card = $card; + $this->txnCode = 3000; + + $this->order->installment = $this->order->installment == 0 ? 0 : $this->order->installment; + } + + /** + * Make Payment + * + * @param object $card + * @return mixed + * @throws UnsupportedPaymentModelException + * @throws GuzzleException + */ + public function payment($card) + { + $this->card = $card; + + $model = 'regular'; + if (isset($this->account->model) && $this->account->model) { + $model = $this->account->model; + } + + if ($model == 'regular') { + $this->makeRegularPayment(); + } elseif ($model == '3d') { + $this->make3DPayment(); + } elseif ($model == '3d_pay') { + $this->make3DPayPayment(); + } else { + throw new UnsupportedPaymentModelException(); + } + + return $this; + } + + /** + * Refund Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function refund(array $meta) + { + return false; + } + + /** + * Cancel Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function cancel(array $meta) + { + return false; + } + + /** + * Order Status + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function status(array $meta) + { + return false; + } + + /** + * Order History + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function history(array $meta) + { + return false; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @return mixed + */ + public function getAccount() + { + return $this->account; + } + + /** + * @return array + */ + public function getCurrencies() + { + return $this->currencies; + } + + /** + * @return mixed + */ + public function getOrder() + { + return $this->order; + } + + /** + * @return mixed + */ + public function getCard() + { + return $this->card; + } + + /** + * @return string|null + */ + public function getCardCode() + { + $card_type = null; + if (isset($this->card->type)) { + if ($this->card->type == 'visa') { + $card_type = '0'; + } elseif ($this->card->type == 'master') { + $card_type = '1'; + } elseif ($this->card->type == '1' || $this->card->type == '2') { + $card_type = $this->card->type; + } + } + return $card_type; + } + + protected function getCardExpDate() + { + $year = (string)substr($this->card->year, 2, 2); + $month = (string)substr($this->card->month, 0, 2); + + return (string)$month . $year; + } + + +} diff --git a/app/Core/Payment/Pos/EstPos.php b/app/Core/Payment/Pos/EstPos.php new file mode 100644 index 0000000..1f4465e --- /dev/null +++ b/app/Core/Payment/Pos/EstPos.php @@ -0,0 +1,881 @@ + 'approved', + '01' => 'bank_call', + '02' => 'bank_call', + '05' => 'reject', + '09' => 'try_again', + '12' => 'invalid_transaction', + '28' => 'reject', + '51' => 'insufficient_balance', + '54' => 'expired_card', + '57' => 'does_not_allow_card_holder', + '62' => 'restricted_card', + '77' => 'request_rejected', + '99' => 'general_error', + ]; + + /** + * Transaction Types + * + * @var array + */ + public $types = [ + 'pay' => 'Auth', + 'pre' => 'PreAuth', + 'post' => 'PostAuth', + ]; + + /** + * Currencies + * + * @var array + */ + public $currencies = []; + + /** + * Transaction Type + * + * @var string + */ + public $type; + + /** + * API Account + * + * @var array + */ + protected $account = []; + + /** + * Order Details + * + * @var array + */ + protected $order = []; + + /** + * Credit Card + * + * @var object + */ + protected $card; + + /** + * Request + * + * @var Request + */ + protected $request; + + /** + * Response Raw Data + * + * @var object + */ + protected $data; + + /** + * Processed Response Data + * + * @var mixed + */ + public $response; + + /** + * Configuration + * + * @var array + */ + protected $config = []; + + /** + * EstPos constructor. + * + * @param array $config + * @param mixed $account + * @param array $currencies + */ + public function __construct($config, $account, array $currencies) + { + $this->config = $config; + $this->account = $account; + $this->currencies = $currencies; + + $this->url = isset($this->config['urls'][$this->account->env]) ? + $this->config['urls'][$this->account->env] : + $this->config['urls']['production']; + + $this->gateway = isset($this->config['urls']['gateway'][$this->account->env]) ? + $this->config['urls']['gateway'][$this->account->env] : + $this->config['urls']['gateway']['production']; + + return $this; + } + + /** + * Create Regular Payment XML + * + * @return string + */ + protected function createRegularPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'CardType' => isset($this->card->type) ? $this->card->type : null, + 'Number' => $this->card->number, + 'Expires' => $this->card->month . '/' . $this->card->year, + 'Cvv2Val' => $this->card->cvv, + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + 'BillTo' => [ + 'Name' => $this->order->name ? $this->order->name : null, + ] + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create Regular Payment Post XML + * + * @return string + */ + protected function createRegularPostXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->types[$this->order->transaction], + 'OrderId' => $this->order->id, + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create 3D Payment XML + * @return string + */ + protected function create3DPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'Number' => $this->request->get('md'), + 'Expires' => '', + 'Cvv2Val' => '', + 'PayerTxnId' => $this->request->get('xid'), + 'PayerSecurityLevel' => $this->request->get('eci'), + 'PayerAuthenticationCode' => $this->request->get('cavv'), + 'CardholderPresentCode' => '13', + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + ] + ]; + + if ($this->order->name) { + $nodes['BillTo'] = [ + 'Name' => $this->order->name, + ]; + } + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Get ProcReturnCode + * + * @return string|null + */ + protected function getProcReturnCode() + { + return isset($this->data->ProcReturnCode) ? (string)$this->data->ProcReturnCode : null; + } + + /** + * Get Status Detail Text + * + * @return string|null + */ + protected function getStatusDetail() + { + $proc_return_code = $this->getProcReturnCode(); + + return $proc_return_code ? (isset($this->codes[$proc_return_code]) ? (string)$this->codes[$proc_return_code] : null) : null; + } + + /** + * Create 3D Hash + * + * @return string + */ + public function create3DHash() + { + $hash_str = ''; + + if ($this->account->model == '3d') { + $hash_str = $this->account->client_id . $this->order->id . $this->order->amount . $this->order->success_url . $this->order->fail_url . $this->order->rand . $this->account->store_key; + } elseif ($this->account->model == '3d_pay') { + $hash_str = $this->account->client_id . $this->order->id . $this->order->amount . $this->order->success_url . $this->order->fail_url . $this->type . $this->order->installment . $this->order->rand . $this->account->store_key; + } + + return base64_encode(sha1($hash_str, true)); + } + + /** + * Check 3D Hash + * + * @param array $data + * @return bool + */ + public function check3DHash($data) + { + $hash_params = $data['HASHPARAMS']; + $hash_params_val = $data['HASHPARAMSVAL']; + $hash_param = $data['HASH']; + $params_val = ''; + + $hashparams_arr = explode(':', $hash_params); + foreach ($hashparams_arr as $value) { + if(!empty($value) && isset($data[$value])){ + $params_val = $params_val . $data[$value]; + } + } + + $hash_val = $params_val . $this->account->store_key; + $hash = base64_encode(sha1($hash_val, true)); + + $return = false; + if ($hash_params && !($params_val != $hash_params_val || $hash_param != $hash)) { + $return = true; + } + + return $return; + } + + /** + * Regular Payment + * + * @return $this + * @throws GuzzleException + */ + public function makeRegularPayment() + { + $contents = ''; + if (in_array($this->order->transaction, ['pay', 'pre'])) { + $contents = $this->createRegularPaymentXML(); + } elseif ($this->order->transaction == 'post') { + $contents = $this->createRegularPostXML(); + } + + $this->send($contents); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'id' => isset($this->data->AuthCode) ? $this->printData($this->data->AuthCode) : null, + 'order_id' => isset($this->data->OrderId) ? $this->printData($this->data->OrderId) : null, + 'group_id' => isset($this->data->GroupId) ? $this->printData($this->data->GroupId) : null, + 'trans_id' => isset($this->data->TransId) ? $this->printData($this->data->TransId) : null, + 'response' => isset($this->data->Response) ? $this->printData($this->data->Response) : null, + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'auth_code' => isset($this->data->AuthCode) ? $this->printData($this->data->AuthCode) : null, + 'host_ref_num' => isset($this->data->HostRefNum) ? $this->printData($this->data->HostRefNum) : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => isset($this->data->Extra->ERRORCODE) ? $this->printData($this->data->Extra->ERRORCODE) : null, + 'error_message' => isset($this->data->Extra->ERRORCODE) ? $this->printData($this->data->ErrMsg) : null, + 'campaign_url' => null, + 'extra' => isset($this->data->Extra) ? $this->data->Extra : null, + 'all' => $this->data, + 'original' => $this->data, + ]; + + return $this; + } + + /** + * Make 3D Payment + * + * @return $this + * @throws GuzzleException + */ + public function make3DPayment() + { + $this->request = Request::createFromGlobals(); + + $status = 'declined'; + if ($this->check3DHash($this->request->request->all())) { + $contents = $this->create3DPaymentXML(); + $this->send($contents); + } + + $transaction_security = 'MPI fallback'; + if ($this->getProcReturnCode() == '00') { + if ($this->request->get('mdStatus') == '1') { + $transaction_security = 'Full 3D Secure'; + } elseif (in_array($this->request->get('mdStatus'), [2, 3, 4])) { + $transaction_security = 'Half 3D Secure'; + } + + $status = 'approved'; + } + + $this->response = (object)[ + 'id' => isset($this->data->AuthCode) ? $this->printData($this->data->AuthCode) : null, + 'order_id' => isset($this->data->OrderId) ? $this->printData($this->data->OrderId) : null, + 'group_id' => isset($this->data->GroupId) ? $this->printData($this->data->GroupId) : null, + 'trans_id' => isset($this->data->TransId) ? $this->printData($this->data->TransId) : null, + 'response' => isset($this->data->Response) ? $this->printData($this->data->Response) : null, + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'transaction_security' => $transaction_security, + 'auth_code' => isset($this->data->AuthCode) ? $this->printData($this->data->AuthCode) : null, + 'host_ref_num' => isset($this->data->HostRefNum) ? $this->printData($this->data->HostRefNum) : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => isset($this->data->Extra->ERRORCODE) ? $this->printData($this->data->Extra->ERRORCODE) : null, + 'error_message' => isset($this->data->Extra->ERRORCODE) ? $this->printData($this->data->ErrMsg) : null, + 'md_status' => $this->request->get('mdStatus'), + 'hash' => (string)$this->request->get('HASH'), + 'rand' => (string)$this->request->get('rnd'), + 'hash_params' => (string)$this->request->get('HASHPARAMS'), + 'hash_params_val' => (string)$this->request->get('HASHPARAMSVAL'), + 'masked_number' => (string)$this->request->get('maskedCreditCard'), + 'month' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Month'), + 'year' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Year'), + 'amount' => (string)$this->request->get('amount'), + 'currency' => (string)$this->request->get('currency'), + 'tx_status' => (string)$this->request->get('txstatus'), + 'eci' => (string)$this->request->get('eci'), + 'cavv' => (string)$this->request->get('cavv'), + 'xid' => (string)$this->request->get('xid'), + 'md_error_message' => (string)$this->request->get('mdErrorMsg'), + 'name' => (string)$this->request->get('firmaadi'), + 'campaign_url' => null, + 'email' => (string)$this->request->get('Email'), + 'extra' => isset($this->data->Extra) ? $this->data->Extra : null, + 'all' => $this->data, + '3d_all' => $this->request->request->all(), + ]; + + return $this; + } + + /** + * Make 3D Pay Payment + * + * @return $this + */ + public function make3DPayPayment() + { + $this->request = Request::createFromGlobals(); + + $status = 'declined'; + + if ($this->check3DHash($this->request->request->all()) && (string)$this->request->get('ProcReturnCode') == '00') { + if (in_array($this->request->get('mdStatus'), [1, 2, 3, 4])) { + $status = 'approved'; + } + } + + $transaction_security = 'MPI fallback'; + if ($status == 'approved') { + if ($this->request->get('mdStatus') == '1') { + $transaction_security = 'Full 3D Secure'; + } elseif (in_array($this->request->get('mdStatus'), [2, 3, 4])) { + $transaction_security = 'Half 3D Secure'; + } + } + + $this->response = (object)[ + 'id' => (string)$this->request->get('AuthCode'), + 'trans_id' => (string)$this->request->get('TransId'), + 'auth_code' => (string)$this->request->get('AuthCode'), + 'host_ref_num' => (string)$this->request->get('HostRefNum'), + 'response' => (string)$this->request->get('Response'), + 'order_id' => (string)$this->request->get('oid'), + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'transaction_security' => $transaction_security, + 'code' => (string)$this->request->get('ProcReturnCode'), + 'md_status' => $this->request->get('mdStatus'), + 'status' => $status, + 'status_detail' => isset($this->codes[$this->request->get('ProcReturnCode')]) ? (string)$this->request->get('ProcReturnCode') : null, + 'hash' => (string)$this->request->get('HASH'), + 'rand' => (string)$this->request->get('rnd'), + 'hash_params' => (string)$this->request->get('HASHPARAMS'), + 'hash_params_val' => (string)$this->request->get('HASHPARAMSVAL'), + 'masked_number' => (string)$this->request->get('maskedCreditCard'), + 'month' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Month'), + 'year' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Year'), + 'amount' => (string)$this->request->get('amount'), + 'currency' => (string)$this->request->get('currency'), + 'tx_status' => (string)$this->request->get('txstatus'), + 'eci' => (string)$this->request->get('eci'), + 'cavv' => (string)$this->request->get('cavv'), + 'xid' => (string)$this->request->get('xid'), + 'error_code' => (string)$this->request->get('ErrCode'), + 'error_message' => (string)$this->request->get('ErrMsg'), + 'md_error_message' => (string)$this->request->get('mdErrorMsg'), + 'name' => (string)$this->request->get('firmaadi'), + 'email' => (string)$this->request->get('Email'), + 'campaign_url' => null, + 'extra' => $this->request->get('Extra'), + 'all' => $this->request->request->all(), + ]; + + return $this; + } + + /** + * Get 3d Form Data + * + * @return array + */ + public function get3DFormData() + { + $data = []; + + if ($this->order) { + $this->order->hash = $this->create3DHash(); + + $inputs = [ + 'clientid' => $this->account->client_id, + 'storetype' => $this->account->model, + 'hash' => $this->order->hash, + 'cardType' => $this->getCardCode(), + 'pan' => $this->card->number, + 'Ecom_Payment_Card_ExpDate_Month' => $this->card->month, + 'Ecom_Payment_Card_ExpDate_Year' => $this->card->year, + 'cv2' => $this->card->cvv, + 'firmaadi' => $this->order->name, + 'Email' => $this->order->email, + 'amount' => $this->order->amount, + 'oid' => $this->order->id, + 'okUrl' => $this->order->success_url, + 'failUrl' => $this->order->fail_url, + 'rnd' => $this->order->rand, + 'lang' => $this->order->lang, + 'currency' => $this->order->currency, + ]; + + if ($this->account->model == '3d_pay') { + $inputs = array_merge($inputs, [ + 'islemtipi' => $this->type, + 'taksit' => $this->order->installment, + ]); + } + + $data = [ + 'gateway' => $this->gateway, + 'success_url' => $this->order->success_url, + 'fail_url' => $this->order->fail_url, + 'rand' => $this->order->rand, + 'hash' => $this->order->hash, + 'inputs' => $inputs, + ]; + } + + return $data; + } + + /** + * Send contents to WebService + * + * @param $contents + * @return $this + * @throws GuzzleException + */ + public function send($contents) + { + $client = new Client(); + + $response = $client->request('POST', $this->url, [ + 'body' => $contents + ]); + + $this->data = $this->XMLStringToObject($response->getBody()->getContents()); + + return $this; + } + + /** + * Prepare Order + * + * @param object $order + * @param object null $card + * @return mixed + * @throws UnsupportedTransactionTypeException + */ + public function prepare($order, $card = null) + { + $this->type = $this->types['pay']; + if (isset($order->transaction)) { + if (array_key_exists($order->transaction, $this->types)) { + $this->type = $this->types[$order->transaction]; + } else { + throw new UnsupportedTransactionTypeException('Unsupported transaction type!'); + } + } + + $this->order = $order; + $this->card = $card; + } + + /** + * Make Payment + * + * @param object $card + * @return mixed + * @throws UnsupportedPaymentModelException + * @throws GuzzleException + */ + public function payment($card) + { + $this->card = $card; + + $model = 'regular'; + if (isset($this->account->model) && $this->account->model) { + $model = $this->account->model; + } + + if ($model == 'regular') { + $this->makeRegularPayment(); + } elseif ($model == '3d') { + $this->make3DPayment(); + } elseif ($model == '3d_pay') { + $this->make3DPayPayment(); + } else { + throw new UnsupportedPaymentModelException(); + } + + return $this; + } + + /** + * Refund Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function refund(array $meta) + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'OrderId' => $meta['order_id'], + 'Type' => 'Credit', + ] + ]; + + if ($meta['amount']) $nodes['Total'] = $meta['amount']; + + $xml = $this->createXML($nodes, 'ISO-8859-9'); + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'order_id' => isset($this->data->OrderId) ? $this->data->OrderId : null, + 'group_id' => isset($this->data->GroupId) ? $this->data->GroupId : null, + 'response' => isset($this->data->Response) ? $this->data->Response : null, + 'auth_code' => isset($this->data->AuthCode) ? $this->data->AuthCode : null, + 'host_ref_num' => isset($this->data->HostRefNum) ? $this->data->HostRefNum : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->data->ProcReturnCode : null, + 'trans_id' => isset($this->data->TransId) ? $this->data->TransId : null, + 'error_code' => isset($this->data->Extra->ERRORCODE) ? $this->data->Extra->ERRORCODE : null, + 'error_message' => isset($this->data->ErrMsg) ? $this->data->ErrMsg : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'all' => $this->data, + ]; + + return $this; + } + + /** + * Cancel Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function cancel(array $meta) + { + $xml = $this->createXML([ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'OrderId' => $meta['order_id'], + 'Type' => 'Void', + ] + ], 'ISO-8859-9'); + + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'order_id' => isset($this->data->OrderId) ? $this->data->OrderId : null, + 'group_id' => isset($this->data->GroupId) ? $this->data->GroupId : null, + 'response' => isset($this->data->Response) ? $this->data->Response : null, + 'auth_code' => isset($this->data->AuthCode) ? $this->data->AuthCode : null, + 'host_ref_num' => isset($this->data->HostRefNum) ? $this->data->HostRefNum : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->data->ProcReturnCode : null, + 'trans_id' => isset($this->data->TransId) ? $this->data->TransId : null, + 'error_code' => isset($this->data->Extra->ERRORCODE) ? $this->data->Extra->ERRORCODE : null, + 'error_message' => isset($this->data->ErrMsg) ? $this->data->ErrMsg : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'all' => $this->data, + ]; + + return $this; + } + + /** + * Order Status + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function status(array $meta) + { + $xml = $this->createXML([ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'OrderId' => $meta['order_id'], + 'Extra' => [ + 'ORDERSTATUS' => 'QUERY', + ], + ] + ], 'ISO-8859-9'); + + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $first_amount = isset($this->data->Extra->ORIG_TRANS_AMT) ? $this->printData($this->data->Extra->ORIG_TRANS_AMT) : null; + $capture_amount = isset($this->data->Extra->CAPTURE_AMT) ? $this->printData($this->data->Extra->CAPTURE_AMT) : null; + $capture = $first_amount == $capture_amount ? true : false; + + $this->response = (object)[ + 'order_id' => isset($this->data->OrderId) ? $this->printData($this->data->OrderId) : null, + 'response' => isset($this->data->Response) ? $this->printData($this->data->Response) : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'trans_id' => isset($this->data->TransId) ? $this->printData($this->data->TransId) : null, + 'error_message' => isset($this->data->ErrMsg) ? $this->printData($this->data->ErrMsg) : null, + 'host_ref_num' => isset($this->data->Extra->HOST_REF_NUM) ? $this->printData($this->data->Extra->HOST_REF_NUM) : null, + 'order_status' => isset($this->data->Extra->ORDERSTATUS) ? $this->printData($this->data->Extra->ORDERSTATUS) : null, + 'process_type' => isset($this->data->Extra->CHARGE_TYPE_CD) ? $this->printData($this->data->Extra->CHARGE_TYPE_CD) : null, + 'pan' => isset($this->data->Extra->PAN) ? $this->printData($this->data->Extra->PAN) : null, + 'num_code' => isset($this->data->Extra->NUMCODE) ? $this->printData($this->data->Extra->NUMCODE) : null, + 'first_amount' => $first_amount, + 'capture_amount' => $capture_amount, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'capture' => $capture, + 'all' => $this->data, + 'xml' => $xml, + ]; + + return $this; + } + + /** + * Order History + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function history(array $meta) + { + $xml = $this->createXML([ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'OrderId' => $meta['order_id'], + 'Extra' => [ + 'ORDERHISTORY' => 'QUERY', + ], + ] + ], 'ISO-8859-9'); + + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'order_id' => isset($this->data->OrderId) ? $this->printData($this->data->OrderId) : null, + 'response' => isset($this->data->Response) ? $this->printData($this->data->Response) : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'error_message' => isset($this->data->ErrMsg) ? $this->printData($this->data->ErrMsg) : null, + 'num_code' => isset($this->data->Extra->NUMCODE) ? $this->printData($this->data->Extra->NUMCODE) : null, + 'trans_count' => isset($this->data->Extra->TRXCOUNT) ? $this->printData($this->data->Extra->TRXCOUNT) : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'all' => $this->data, + 'xml' => $xml, + ]; + + return $this; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @return mixed + */ + public function getAccount() + { + return $this->account; + } + + /** + * @return array + */ + public function getCurrencies() + { + return $this->currencies; + } + + /** + * @return mixed + */ + public function getOrder() + { + return $this->order; + } + + /** + * @return mixed + */ + public function getCard() + { + return $this->card; + } + + /** + * @return string|null + */ + public function getCardCode() + { + $card_type = null; + if (isset($this->card->type)) { + if ($this->card->type == 'visa') { + $card_type = '1'; + } elseif ($this->card->type == 'master') { + $card_type = '2'; + }elseif($this->card->type == '1' || $this->card->type == '2'){ + $card_type = $this->card->type; + } + } + return $card_type; + } +} diff --git a/app/Core/Payment/Pos/EstPosV3.php b/app/Core/Payment/Pos/EstPosV3.php new file mode 100644 index 0000000..65bcf36 --- /dev/null +++ b/app/Core/Payment/Pos/EstPosV3.php @@ -0,0 +1,913 @@ + 'approved', + '01' => 'bank_call', + '02' => 'bank_call', + '05' => 'reject', + '09' => 'try_again', + '12' => 'invalid_transaction', + '28' => 'reject', + '51' => 'insufficient_balance', + '54' => 'expired_card', + '57' => 'does_not_allow_card_holder', + '62' => 'restricted_card', + '77' => 'request_rejected', + '99' => 'general_error', + ]; + + /** + * Transaction Types + * + * @var array + */ + public $types = [ + 'pay' => 'Auth', + 'pre' => 'PreAuth', + 'post' => 'PostAuth', + ]; + + /** + * Currencies + * + * @var array + */ + public $currencies = []; + + /** + * Transaction Type + * + * @var string + */ + public $type; + + /** + * API Account + * + * @var array + */ + protected $account = []; + + /** + * Order Details + * + * @var array + */ + protected $order = []; + + /** + * Credit Card + * + * @var object + */ + protected $card; + + /** + * Request + * + * @var Request + */ + protected $request; + + /** + * Response Raw Data + * + * @var object + */ + protected $data; + + /** + * Processed Response Data + * + * @var mixed + */ + public $response; + + /** + * Configuration + * + * @var array + */ + protected $config = []; + + /** + * EstPos constructor. + * + * @param array $config + * @param mixed $account + * @param array $currencies + */ + public function __construct($config, $account, array $currencies) + { + $this->config = $config; + $this->account = $account; + $this->currencies = $currencies; + + $this->url = isset($this->config['urls'][$this->account->env]) ? + $this->config['urls'][$this->account->env] : + $this->config['urls']['production']; + + $this->gateway = isset($this->config['urls']['gateway'][$this->account->env]) ? + $this->config['urls']['gateway'][$this->account->env] : + $this->config['urls']['gateway']['production']; + + return $this; + } + + /** + * Create Regular Payment XML + * + * @return string + */ + protected function createRegularPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'CardType' => isset($this->card->type) ? $this->card->type : null, + 'Number' => $this->card->number, + 'Expires' => $this->card->month . '/' . $this->card->year, + 'Cvv2Val' => $this->card->cvv, + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + 'BillTo' => [ + 'Name' => $this->order->name ? $this->order->name : null, + ] + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create Regular Payment Post XML + * + * @return string + */ + protected function createRegularPostXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->types[$this->order->transaction], + 'OrderId' => $this->order->id, + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create 3D Payment XML + * @return string + */ + protected function create3DPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'Number' => $this->request->get('md'), + 'Expires' => '', + 'Cvv2Val' => '', + 'PayerTxnId' => $this->request->get('xid'), + 'PayerSecurityLevel' => $this->request->get('eci'), + 'PayerAuthenticationCode' => $this->request->get('cavv'), + 'CardholderPresentCode' => '13', + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + ] + ]; + + if ($this->order->name) { + $nodes['BillTo'] = [ + 'Name' => $this->order->name, + ]; + } + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Get ProcReturnCode + * + * @return string|null + */ + protected function getProcReturnCode() + { + return isset($this->data->ProcReturnCode) ? (string)$this->data->ProcReturnCode : null; + } + + /** + * Get Status Detail Text + * + * @return string|null + */ + protected function getStatusDetail() + { + $proc_return_code = $this->getProcReturnCode(); + + return $proc_return_code ? (isset($this->codes[$proc_return_code]) ? (string)$this->codes[$proc_return_code] : null) : null; + } + + /** + * Create 3D Hash + * + * @return string + */ + public function create3DHash($inputs) + { + + $hash3D = null; + $postParams = []; + foreach ($inputs as $key => $value){ + array_push($postParams, $key); + } + + natcasesort($postParams); + + $hashval = ""; + foreach ($postParams as $param){ + $paramValue = $inputs[$param]; + $escapedParamValue = str_replace("|", "\\|", str_replace("\\", "\\\\", $paramValue)); + + $lowerParam = strtolower($param); + if($lowerParam != "hash" && $lowerParam != "encoding" ) { + $hashval = $hashval . $escapedParamValue . "|"; + } + } + + $storeKey = $this->account->store_key; + $escapedStoreKey = str_replace("|", "\\|", str_replace("\\", "\\\\", $storeKey)); + $hashval = $hashval . $escapedStoreKey; + + $calculatedHashValue = hash('sha512', $hashval); + $hash3D = base64_encode (pack('H*',$calculatedHashValue)); + + return $hash3D; + + } + + /** + * Check 3D Hash + * + * @param array $data + * @return bool + */ + public function check3DHash($data) + { + + $return = false; + + Log::debug('LOG - 1'); + Log::debug($data); + + $postParams = []; + foreach ($data as $key => $value) { + array_push($postParams, $key); + } + + natcasesort($postParams); + + $hashval = ""; + foreach ($postParams as $param) { + $paramValue = $data[$param]; + $escapedParamValue = str_replace("|", "\\|", str_replace("\\", "\\\\", $paramValue)); + + $lowerParam = strtolower($param); + if ($lowerParam != "hash" && $lowerParam != "encoding") { + $hashval = $hashval . $escapedParamValue . "|"; + } + } + + $storeKey = $this->account->store_key; + $escapedStoreKey = str_replace("|", "\\|", str_replace("\\", "\\\\", $storeKey)); + $hashval = $hashval . $escapedStoreKey; + + $calculatedHashValue = hash('sha512', $hashval); + $actualHash = base64_encode(pack('H*', $calculatedHashValue)); + + $retrievedHash = fillOnUndefined($data,'HASH'); + + Log::debug('LOG - 2'); + Log::debug($data); + + if ($retrievedHash == $actualHash) { + $return = true; + } + + return $return; + } + + /** + * Regular Payment + * + * @return $this + * @throws GuzzleException + */ + public function makeRegularPayment() + { + $contents = ''; + if (in_array($this->order->transaction, ['pay', 'pre'])) { + $contents = $this->createRegularPaymentXML(); + } elseif ($this->order->transaction == 'post') { + $contents = $this->createRegularPostXML(); + } + + $this->send($contents); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'id' => isset($this->data->AuthCode) ? $this->printData($this->data->AuthCode) : null, + 'order_id' => isset($this->data->OrderId) ? $this->printData($this->data->OrderId) : null, + 'group_id' => isset($this->data->GroupId) ? $this->printData($this->data->GroupId) : null, + 'trans_id' => isset($this->data->TransId) ? $this->printData($this->data->TransId) : null, + 'response' => isset($this->data->Response) ? $this->printData($this->data->Response) : null, + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'auth_code' => isset($this->data->AuthCode) ? $this->printData($this->data->AuthCode) : null, + 'host_ref_num' => isset($this->data->HostRefNum) ? $this->printData($this->data->HostRefNum) : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => isset($this->data->Extra->ERRORCODE) ? $this->printData($this->data->Extra->ERRORCODE) : null, + 'error_message' => isset($this->data->Extra->ERRORCODE) ? $this->printData($this->data->ErrMsg) : null, + 'campaign_url' => null, + 'extra' => isset($this->data->Extra) ? $this->data->Extra : null, + 'all' => $this->data, + 'original' => $this->data, + ]; + + return $this; + } + + /** + * Make 3D Payment + * + * @return $this + * @throws GuzzleException + */ + public function make3DPayment() + { + $this->request = Request::createFromGlobals(); + + $status = 'declined'; + if ($this->check3DHash($this->request->request->all())) { + $contents = $this->create3DPaymentXML(); + $this->send($contents); + } + + $transaction_security = 'MPI fallback'; + if ($this->getProcReturnCode() == '00') { + if ($this->request->get('mdStatus') == '1') { + $transaction_security = 'Full 3D Secure'; + } elseif (in_array($this->request->get('mdStatus'), [2, 3, 4])) { + $transaction_security = 'Half 3D Secure'; + } + + $status = 'approved'; + } + + $this->response = (object)[ + 'id' => isset($this->data->AuthCode) ? $this->printData($this->data->AuthCode) : null, + 'order_id' => isset($this->data->OrderId) ? $this->printData($this->data->OrderId) : null, + 'group_id' => isset($this->data->GroupId) ? $this->printData($this->data->GroupId) : null, + 'trans_id' => isset($this->data->TransId) ? $this->printData($this->data->TransId) : null, + 'response' => isset($this->data->Response) ? $this->printData($this->data->Response) : null, + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'transaction_security' => $transaction_security, + 'auth_code' => isset($this->data->AuthCode) ? $this->printData($this->data->AuthCode) : null, + 'host_ref_num' => isset($this->data->HostRefNum) ? $this->printData($this->data->HostRefNum) : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => isset($this->data->Extra->ERRORCODE) ? $this->printData($this->data->Extra->ERRORCODE) : null, + 'error_message' => isset($this->data->Extra->ERRORCODE) ? $this->printData($this->data->ErrMsg) : null, + 'md_status' => $this->request->get('mdStatus'), + 'hash' => (string)$this->request->get('HASH'), + 'rand' => (string)$this->request->get('rnd'), + 'hash_params' => (string)$this->request->get('HASHPARAMS'), + 'hash_params_val' => (string)$this->request->get('HASHPARAMSVAL'), + 'masked_number' => (string)$this->request->get('maskedCreditCard'), + 'month' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Month'), + 'year' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Year'), + 'amount' => (string)$this->request->get('amount'), + 'currency' => (string)$this->request->get('currency'), + 'tx_status' => (string)$this->request->get('txstatus'), + 'eci' => (string)$this->request->get('eci'), + 'cavv' => (string)$this->request->get('cavv'), + 'xid' => (string)$this->request->get('xid'), + 'md_error_message' => (string)$this->request->get('mdErrorMsg'), + 'name' => (string)$this->request->get('firmaadi'), + 'campaign_url' => null, + 'email' => (string)$this->request->get('Email'), + 'extra' => isset($this->data->Extra) ? $this->data->Extra : null, + 'all' => $this->data, + '3d_all' => $this->request->request->all(), + ]; + + return $this; + } + + /** + * Make 3D Pay Payment + * + * @return $this + */ + public function make3DPayPayment() + { + $this->request = Request::createFromGlobals(); + + $status = 'declined'; + + if ($this->check3DHash($this->request->request->all()) && (string)$this->request->get('ProcReturnCode') == '00') { + if (in_array($this->request->get('mdStatus'), [1, 2, 3, 4])) { + $status = 'approved'; + } + } + + $transaction_security = 'MPI fallback'; + if ($status == 'approved') { + if ($this->request->get('mdStatus') == '1') { + $transaction_security = 'Full 3D Secure'; + } elseif (in_array($this->request->get('mdStatus'), [2, 3, 4])) { + $transaction_security = 'Half 3D Secure'; + } + } + + $this->response = (object)[ + 'id' => (string)$this->request->get('AuthCode'), + 'trans_id' => (string)$this->request->get('TransId'), + 'auth_code' => (string)$this->request->get('AuthCode'), + 'host_ref_num' => (string)$this->request->get('HostRefNum'), + 'response' => (string)$this->request->get('Response'), + 'order_id' => (string)$this->request->get('oid'), + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'transaction_security' => $transaction_security, + 'code' => (string)$this->request->get('ProcReturnCode'), + 'md_status' => $this->request->get('mdStatus'), + 'status' => $status, + 'status_detail' => isset($this->codes[$this->request->get('ProcReturnCode')]) ? (string)$this->request->get('ProcReturnCode') : null, + 'hash' => (string)$this->request->get('HASH'), + 'rand' => (string)$this->request->get('rnd'), + 'masked_number' => (string)$this->request->get('maskedCreditCard'), + 'month' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Month'), + 'year' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Year'), + 'amount' => (string)$this->request->get('amount'), + 'currency' => (string)$this->request->get('currency'), + 'hashAlgorithm' => (string)$this->request->get('hashAlgorithm'), + 'xid' => (string)$this->request->get('xid'), + 'error_code' => (string)$this->request->get('ErrCode'), + 'error_message' => ($status != 'approved' && empty((string)$this->request->get('ErrMsg'))) ? (string)$this->request->get('mdErrorMsg') : (string)$this->request->get('ErrMsg'), + 'md_error_message' => (string)$this->request->get('mdErrorMsg'), + 'merchantName' => (string)$this->request->get('merchantName'), + 'all' => $this->request->request->all(), + ]; + + return $this; + } + + /** + * Get 3d Form Data + * + * @return array + */ + public function get3DFormData() + { + $data = []; + + + if ($this->order) { + + $inputs = [ + 'clientid' => $this->account->client_id, + 'storetype' => $this->account->model, + 'hashAlgorithm' => 'ver3', + 'cardType' => $this->getCardCode(), + 'pan' => $this->card->number, + 'Ecom_Payment_Card_ExpDate_Month' => $this->card->month, + 'Ecom_Payment_Card_ExpDate_Year' => $this->card->year, + 'cv2' => $this->card->cvv, + //'firmaadi' => $this->order->name, + //'Email' => $this->order->email, + 'amount' => $this->order->amount, + 'oid' => $this->order->id, + 'okUrl' => $this->order->success_url, + 'failUrl' => $this->order->fail_url, + 'callbackUrl' => null, + 'rnd' => $this->order->rand, + 'lang' => $this->order->lang, + 'currency' => $this->order->currency, + 'TranType' => 'Auth', + 'Instalment' => $this->order->installment, + ]; + + $inputs['HASH'] = $this->create3DHash($inputs); + + $data = [ + 'gateway' => $this->gateway, + 'success_url' => $this->order->success_url, + 'fail_url' => $this->order->fail_url, + 'rand' => $this->order->rand, + //'hash' => $this->order->hash, + 'inputs' => $inputs, + ]; + } + + return $data; + } + + /** + * Send contents to WebService + * + * @param $contents + * @return $this + * @throws GuzzleException + */ + public function send($contents) + { + $client = new Client(); + + $response = $client->request('POST', $this->url, [ + 'body' => $contents + ]); + + $this->data = $this->XMLStringToObject($response->getBody()->getContents()); + + return $this; + } + + /** + * Prepare Order + * + * @param object $order + * @param object null $card + * @return mixed + * @throws UnsupportedTransactionTypeException + */ + public function prepare($order, $card = null) + { + $this->type = $this->types['pay']; + if (isset($order->transaction)) { + if (array_key_exists($order->transaction, $this->types)) { + $this->type = $this->types[$order->transaction]; + } else { + throw new UnsupportedTransactionTypeException('Unsupported transaction type!'); + } + } + + $this->order = $order; + $this->card = $card; + } + + /** + * Make Payment + * + * @param object $card + * @return mixed + * @throws UnsupportedPaymentModelException + * @throws GuzzleException + */ + public function payment($card) + { + $this->card = $card; + + $model = 'regular'; + if (isset($this->account->model) && $this->account->model) { + $model = $this->account->model; + } + + if ($model == 'regular') { + $this->makeRegularPayment(); + } elseif ($model == '3d') { + $this->make3DPayment(); + } elseif ($model == '3d_pay') { + $this->make3DPayPayment(); + } else { + throw new UnsupportedPaymentModelException(); + } + + return $this; + } + + /** + * Refund Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function refund(array $meta) + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'OrderId' => $meta['order_id'], + 'Type' => 'Credit', + ] + ]; + + if ($meta['amount']) $nodes['Total'] = $meta['amount']; + + $xml = $this->createXML($nodes, 'ISO-8859-9'); + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'order_id' => isset($this->data->OrderId) ? $this->data->OrderId : null, + 'group_id' => isset($this->data->GroupId) ? $this->data->GroupId : null, + 'response' => isset($this->data->Response) ? $this->data->Response : null, + 'auth_code' => isset($this->data->AuthCode) ? $this->data->AuthCode : null, + 'host_ref_num' => isset($this->data->HostRefNum) ? $this->data->HostRefNum : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->data->ProcReturnCode : null, + 'trans_id' => isset($this->data->TransId) ? $this->data->TransId : null, + 'error_code' => isset($this->data->Extra->ERRORCODE) ? $this->data->Extra->ERRORCODE : null, + 'error_message' => isset($this->data->ErrMsg) ? $this->data->ErrMsg : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'all' => $this->data, + ]; + + return $this; + } + + /** + * Cancel Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function cancel(array $meta) + { + $xml = $this->createXML([ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'OrderId' => $meta['order_id'], + 'Type' => 'Void', + ] + ], 'ISO-8859-9'); + + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'order_id' => isset($this->data->OrderId) ? $this->data->OrderId : null, + 'group_id' => isset($this->data->GroupId) ? $this->data->GroupId : null, + 'response' => isset($this->data->Response) ? $this->data->Response : null, + 'auth_code' => isset($this->data->AuthCode) ? $this->data->AuthCode : null, + 'host_ref_num' => isset($this->data->HostRefNum) ? $this->data->HostRefNum : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->data->ProcReturnCode : null, + 'trans_id' => isset($this->data->TransId) ? $this->data->TransId : null, + 'error_code' => isset($this->data->Extra->ERRORCODE) ? $this->data->Extra->ERRORCODE : null, + 'error_message' => isset($this->data->ErrMsg) ? $this->data->ErrMsg : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'all' => $this->data, + ]; + + return $this; + } + + /** + * Order Status + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function status(array $meta) + { + $xml = $this->createXML([ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'OrderId' => $meta['order_id'], + 'Extra' => [ + 'ORDERSTATUS' => 'QUERY', + ], + ] + ], 'ISO-8859-9'); + + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $first_amount = isset($this->data->Extra->ORIG_TRANS_AMT) ? $this->printData($this->data->Extra->ORIG_TRANS_AMT) : null; + $capture_amount = isset($this->data->Extra->CAPTURE_AMT) ? $this->printData($this->data->Extra->CAPTURE_AMT) : null; + $capture = $first_amount == $capture_amount ? true : false; + + $this->response = (object)[ + 'order_id' => isset($this->data->OrderId) ? $this->printData($this->data->OrderId) : null, + 'response' => isset($this->data->Response) ? $this->printData($this->data->Response) : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'trans_id' => isset($this->data->TransId) ? $this->printData($this->data->TransId) : null, + 'error_message' => isset($this->data->ErrMsg) ? $this->printData($this->data->ErrMsg) : null, + 'host_ref_num' => isset($this->data->Extra->HOST_REF_NUM) ? $this->printData($this->data->Extra->HOST_REF_NUM) : null, + 'order_status' => isset($this->data->Extra->ORDERSTATUS) ? $this->printData($this->data->Extra->ORDERSTATUS) : null, + 'process_type' => isset($this->data->Extra->CHARGE_TYPE_CD) ? $this->printData($this->data->Extra->CHARGE_TYPE_CD) : null, + 'pan' => isset($this->data->Extra->PAN) ? $this->printData($this->data->Extra->PAN) : null, + 'num_code' => isset($this->data->Extra->NUMCODE) ? $this->printData($this->data->Extra->NUMCODE) : null, + 'first_amount' => $first_amount, + 'capture_amount' => $capture_amount, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'capture' => $capture, + 'all' => $this->data, + 'xml' => $xml, + ]; + + return $this; + } + + /** + * Order History + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function history(array $meta) + { + $xml = $this->createXML([ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'OrderId' => $meta['order_id'], + 'Extra' => [ + 'ORDERHISTORY' => 'QUERY', + ], + ] + ], 'ISO-8859-9'); + + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'order_id' => isset($this->data->OrderId) ? $this->printData($this->data->OrderId) : null, + 'response' => isset($this->data->Response) ? $this->printData($this->data->Response) : null, + 'proc_return_code' => isset($this->data->ProcReturnCode) ? $this->printData($this->data->ProcReturnCode) : null, + 'error_message' => isset($this->data->ErrMsg) ? $this->printData($this->data->ErrMsg) : null, + 'num_code' => isset($this->data->Extra->NUMCODE) ? $this->printData($this->data->Extra->NUMCODE) : null, + 'trans_count' => isset($this->data->Extra->TRXCOUNT) ? $this->printData($this->data->Extra->TRXCOUNT) : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'all' => $this->data, + 'xml' => $xml, + ]; + + return $this; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @return mixed + */ + public function getAccount() + { + return $this->account; + } + + /** + * @return array + */ + public function getCurrencies() + { + return $this->currencies; + } + + /** + * @return mixed + */ + public function getOrder() + { + return $this->order; + } + + /** + * @return mixed + */ + public function getCard() + { + return $this->card; + } + + /** + * @return string|null + */ + public function getCardCode() + { + $card_type = null; + if (isset($this->card->type)) { + if ($this->card->type == 'visa') { + $card_type = '1'; + } elseif ($this->card->type == 'master') { + $card_type = '2'; + } elseif ($this->card->type == '1' || $this->card->type == '2') { + $card_type = $this->card->type; + } + } + return $card_type; + } +} diff --git a/app/Core/Payment/Pos/Exceptions/BankClassNullException.php b/app/Core/Payment/Pos/Exceptions/BankClassNullException.php new file mode 100644 index 0000000..c3d0eee --- /dev/null +++ b/app/Core/Payment/Pos/Exceptions/BankClassNullException.php @@ -0,0 +1,25 @@ + 'approved', + '01' => 'bank_call', + '02' => 'bank_call', + '05' => 'reject', + '09' => 'try_again', + '12' => 'invalid_transaction', + '28' => 'reject', + '51' => 'insufficient_balance', + '54' => 'expired_card', + '57' => 'does_not_allow_card_holder', + '62' => 'restricted_card', + '77' => 'request_rejected', + '99' => 'general_error', + ]; + + /** + * Transaction Types + * + * @var array + */ + public $types = [ + 'pay' => 'sales', + 'pre' => 'preauth', + 'post' => 'postauth', + ]; + + /** + * Currencies + * + * @var array + */ + public $currencies = []; + + /** + * Transaction Type + * + * @var string + */ + public $type; + + /** + * API Account + * + * @var array + */ + protected $account = []; + + /** + * Order Details + * + * @var array + */ + protected $order = []; + + /** + * Credit Card + * + * @var object + */ + protected $card; + + /** + * Request + * + * @var Request + */ + protected $request; + + /** + * Response Raw Data + * + * @var object + */ + protected $data; + + /** + * Processed Response Data + * + * @var mixed + */ + public $response; + + /** + * Configuration + * + * @var array + */ + protected $config = []; + + /** + * Mode + * + * @var string + */ + protected $mode = 'PROD'; + + /** + * API version + * @var string + */ + //protected $version = 'v0.01'; + protected $version = '512'; + + /** + * GarantiPost constructor. + * + * @param array $config + * @param array $account + * @param array $currencies + */ + public function __construct($config, $account, array $currencies) + { + $request = Request::createFromGlobals(); + $this->request = $request->request; + + $this->config = $config; + $this->account = $account; + $this->currencies = $currencies; + + $this->url = isset($this->config['urls'][$this->account->env]) ? + $this->config['urls'][$this->account->env] : + $this->config['urls']['production']; + + $this->gateway = isset($this->config['urls']['gateway'][$this->account->env]) ? + $this->config['urls']['gateway'][$this->account->env] : + $this->config['urls']['gateway']['production']; + + if ($this->account->env == 'test') { + $this->mode = 'TEST'; + } + + return $this; + } + + /** + * Make Security Data + * + * @param bool $refund + * @return string + */ + protected function makeSecurityData($refund = false) + { + $map = [ + $this->account->{($refund ? 'refund_' : null) . 'password'}, + str_pad((int)$this->account->terminal_id, 9, 0, STR_PAD_LEFT), + ]; + + return strtoupper(sha1(implode('', $map))); + } + + /** + * Make Hash Data + * + * @param $security_data + * @return string + */ + protected function makeHashData($security_data) + { + $map = [ + $this->order->id, + $this->account->terminal_id, + isset($this->card->number) ? $this->card->number : null, + $this->amountFormat($this->order->amount), + $this->order->currency, + $security_data, + ]; + + + //return strtoupper(sha1(implode('', $map))); + return strtoupper(hash('sha512', implode('', $map))); + } + + /** + * Make 3d Hash Data + * + * @param $security_data + * @return string + */ + protected function make3dHashData($security_data) + { + $map = [ + $this->account->terminal_id, + $this->order->id, + $this->amountFormat($this->order->amount), + $this->order->currency, + $this->order->success_url, + $this->order->fail_url, + $this->type, + $this->order->installment ? $this->order->installment : '', + $this->account->store_key, + $security_data, + ]; + + //return strtoupper(sha1(implode('', $map))); + return strtoupper(hash('sha512', implode('', $map))); + } + + /** + * Make 3d Hash Data + * + * @param $security_data + * @return string + */ + protected function make3dRequestHashData($security_data) + { + $map = [ + $this->order->id, + $this->account->terminal_id, + $this->amountFormat($this->order->amount), + $security_data, + ]; + + return strtoupper(sha1(implode('', $map))); + } + + /** + * Amount Formatter + * + * @param double $amount + * @return int + */ + public function amountFormat($amount) + { + return (int)str_replace('.', '', number_format($amount, 2, '.', '')); + } + + /** + * Create Regular Payment XML + * + * @return string + */ + protected function createRegularPaymentXML() + { + $security_data = $this->makeSecurityData(); + $hash_data = $this->makeHashData($security_data); + + $nodes = [ + 'GVPSRequest' => [ + 'Mode' => $this->mode, + 'Version' => 'v0.01', + 'Terminal' => [ + 'ProvUserID' => 'PROVAUT',//$this->account->username, + 'UserID' => $this->account->username, + 'HashData' => $hash_data, + 'ID' => $this->account->terminal_id, + 'MerchantID' => $this->account->client_id, + ], + 'Customer' => [ + 'IPAddress' => $this->order->ip, + 'EmailAddress' => $this->order->email, + ], + 'Card' => [ + 'Number' => $this->card->number, + 'ExpireDate' => $this->card->month . substr($this->card->year, -2), + 'CVV2' => $this->card->cvv, + ], + 'Order' => [ + 'OrderID' => $this->order->id, + 'GroupID' => '', + 'AddressList' => [ + 'Address' => [ + 'Type' => 'S', + 'Name' => $this->order->name, + 'LastName' => '', + 'Company' => '', + 'Text' => '', + 'District' => '', + 'City' => '', + 'PostalCode' => '', + 'Country' => '', + 'PhoneNumber' => '', + ], + ], + ], + 'Transaction' => [ + 'Type' => $this->type, + 'InstallmentCnt' => $this->order->installment > 1 ? $this->order->installment : '', + 'Amount' => $this->amountFormat($this->order->amount), + 'CurrencyCode' => $this->order->currency, + 'CardholderPresentCode' => '0', + 'MotoInd' => 'N', + 'Description' => '', + 'OriginalRetrefNum' => '', + ], + ] + ]; + + return $this->createXML($nodes); + } + + /** + * Create Regular Payment Post XML + * + * @return string + */ + protected function createRegularPostXML() + { + $security_data = $this->makeSecurityData(); + $hash_data = $this->makeHashData($security_data); + + $nodes = [ + 'GVPSRequest' => [ + 'Mode' => $this->mode, + 'Version' => 'v0.1', + 'Terminal' => [ + 'ProvUserID' => 'PROVAUT',//$this->account->username, + 'UserID' => $this->account->username, + 'HashData' => $hash_data, + 'ID' => $this->account->terminal_id, + 'MerchantID' => $this->account->client_id, + ], + 'Customer' => [ + 'IPAddress' => $this->order->ip, + 'EmailAddress' => isset($this->order->email) ? $this->order->email : null, + ], + 'Order' => [ + 'OrderID' => $this->order->id, + ], + 'Transaction' => [ + 'Type' => $this->types[$this->order->transaction], + 'Amount' => $this->amountFormat($this->order->amount), + 'CurrencyCode' => $this->order->currency, + 'OriginalRetrefNum' => $this->order->ref_ret_num, + ], + ] + ]; + + return $this->createXML($nodes); + } + + /** + * Create 3D Payment XML + * @return string + */ + protected function create3DPaymentXML() + { + $security_data = $this->makeSecurityData(); + $hash_data = $this->makeHashData($security_data); + + $nodes = [ + 'GVPSRequest' => [ + 'Mode' => $this->mode, + 'Version' => $this->version, + 'ChannelCode' => '', + 'Terminal' => [ + 'ProvUserID' => 'PROVAUT',//$this->account->username, + 'UserID' => $this->account->username, + 'HashData' => $hash_data, + 'ID' => $this->account->terminal_id, + 'MerchantID' => $this->account->client_id, + ], + 'Customer' => [ + 'IPAddress' => $this->request->get('customeripaddress'), + 'EmailAddress' => $this->request->get('customeremailaddress'), + ], + 'Card' => [ + 'Number' => '', + 'ExpireDate' => '', + 'CVV2' => '', + ], + 'Order' => [ + 'OrderID' => $this->request->get('orderid'), + 'GroupID' => '', + 'AddressList' => [ + 'Address' => [ + 'Type' => 'B', + 'Name' => $this->order->name, + 'LastName' => '', + 'Company' => '', + 'Text' => '', + 'District' => '', + 'City' => '', + 'PostalCode' => '', + 'Country' => '', + 'PhoneNumber' => '', + ], + ], + ], + 'Transaction' => [ + 'Type' => $this->request->get('txntype'), + 'InstallmentCnt' => $this->order->installment ? $this->order->installment : '', + 'Amount' => $this->request->get('txnamount'), + 'CurrencyCode' => $this->request->get('txncurrencycode'), + 'CardholderPresentCode' => '13', + 'MotoInd' => 'N', + 'Secure3D' => [ + 'AuthenticationCode' => $this->request->get('cavv'), + 'SecurityLevel' => $this->request->get('eci'), + 'TxnID' => $this->request->get('xid'), + 'Md' => $this->request->get('md'), + ], + ], + ] + ]; + + return $this->createXML($nodes); + } + + /** + * Get ProcReturnCode + * + * @return string|null + */ + protected function getProcReturnCode() + { + return isset($this->data->Transaction->Response->Code) ? (string)$this->data->Transaction->Response->Code : null; + } + + /** + * Get Status Detail Text + * + * @return string|null + */ + protected function getStatusDetail() + { + $proc_return_code = $this->getProcReturnCode(); + + return $proc_return_code ? (isset($this->codes[$proc_return_code]) ? (string)$this->codes[$proc_return_code] : null) : null; + } + + /** + * Create 3D Hash + * + * @return string + */ + public function create3DHash() + { + $hash_str = ''; + + if ($this->account->model == '3d') { + $hash_str = $this->account->client_id . $this->order->id . $this->order->amount . $this->order->success_url . $this->order->fail_url . $this->order->rand . $this->account->store_key; + } elseif ($this->account->model == '3d_pay') { + $hash_str = $this->account->client_id . $this->order->id . $this->order->amount . $this->order->success_url . $this->order->fail_url . $this->order->transaction_type . $this->order->installment . $this->order->rand . $this->account->store_key; + } + + return base64_encode(sha1($hash_str, true)); + } + + /** + * Regular Payment + * + * @return $this + * @throws GuzzleException + */ + public function makeRegularPayment() + { + $contents = ''; + if (in_array($this->order->transaction, ['pay', 'pre'])) { + $contents = $this->createRegularPaymentXML(); + } elseif ($this->order->transaction == 'post') { + $contents = $this->createRegularPostXML(); + } + + $this->send($contents); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'id' => isset($this->data->Transaction->AuthCode) ? $this->printData($this->data->Transaction->AuthCode) : null, + 'order_id' => isset($this->data->Order->OrderID) ? $this->printData($this->data->Order->OrderID) : null, + 'group_id' => isset($this->data->Order->GroupID) ? $this->printData($this->data->Order->GroupID) : null, + 'trans_id' => isset($this->data->Transaction->AuthCode) ? $this->printData($this->data->Transaction->AuthCode) : null, + 'response' => isset($this->data->Transaction->Response->Message) ? $this->printData($this->data->Transaction->Response->Message) : null, + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'auth_code' => isset($this->data->Transaction->AuthCode) ? $this->printData($this->data->Transaction->AuthCode) : null, + 'host_ref_num' => isset($this->data->Transaction->RetrefNum) ? $this->printData($this->data->Transaction->RetrefNum) : null, + 'ret_ref_num' => isset($this->data->Transaction->RetrefNum) ? $this->printData($this->data->Transaction->RetrefNum) : null, + 'hash_data' => isset($this->data->Transaction->HashData) ? $this->printData($this->data->Transaction->HashData) : null, + 'proc_return_code' => $this->getProcReturnCode(), + 'code' => $this->getProcReturnCode(), + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => isset($this->data->Transaction->Response->Code) ? $this->printData($this->data->Transaction->Response->Code) : null, + 'error_message' => isset($this->data->Transaction->Response->ErrorMsg) ? $this->printData($this->data->Transaction->Response->ErrorMsg) : null, + 'campaign_url' => isset($this->data->Transaction->CampaignChooseLink) ? $this->printData($this->data->Transaction->CampaignChooseLink) : null, + 'extra' => isset($this->data->Extra) ? $this->data->Extra : null, + 'all' => $this->data, + 'original' => $this->data, + ]; + + return $this; + } + + /** + * Make 3D Payment + * + * @return $this + * @throws GuzzleException + */ + public function make3DPayment() + { + $status = 'declined'; + $response = 'Declined'; + $proc_return_code = '99'; + $transaction_security = 'MPI fallback'; + if (in_array($this->request->get('mdstatus'), [1, 2, 3, 4])) { + if ($this->request->get('mdstatus') == '1') { + $transaction_security = 'Full 3D Secure'; + } elseif (in_array($this->request->get('mdstatus'), [2, 3, 4])) { + $transaction_security = 'Half 3D Secure'; + } + + $contents = $this->create3DPaymentXML(); + $this->send($contents); + + if ($this->data->Transaction->Response->ReasonCode == '00') { + $response = 'Approved'; + $proc_return_code = $this->data->Transaction->Response->ReasonCode; + $status = 'approved'; + } + } + + $this->response = (object)[ + 'id' => isset($this->data->Transaction->AuthCode) ? $this->printData($this->data->Transaction->AuthCode) : null, + 'order_id' => $this->request->get('oid'), + 'group_id' => isset($this->data->Transaction->SequenceNum) ? $this->printData($this->data->Transaction->SequenceNum) : null, + 'trans_id' => $this->request->get('transid'), + 'response' => $response, + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'transaction_security' => $transaction_security, + 'auth_code' => isset($this->data->Transaction->AuthCode) ? $this->printData($this->data->Transaction->AuthCode) : null, + 'host_ref_num' => isset($this->data->Transaction->RetrefNum) ? $this->printData($this->data->Transaction->RetrefNum) : null, + 'proc_return_code' => $proc_return_code, + 'ret_ref_num' => isset($this->data->Transaction->RetrefNum) ? $this->printData($this->data->Transaction->RetrefNum) : null, + 'batch_num' => isset($this->data->Transaction->BatchNum) ? $this->printData($this->data->Transaction->BatchNum) : null, + 'code' => $proc_return_code, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => isset($this->data->Transaction->Response->ErrorCode) ? $this->printData($this->data->Transaction->Response->ErrorCode) : null, + 'error_message' => isset($this->data->Transaction->Response->ErrorMsg) ? $this->printData($this->data->Transaction->Response->ErrorMsg) : null, + 'reason_code' => isset($this->data->Transaction->Response->ReasonCode) ? $this->printData($this->data->Transaction->Response->ReasonCode) : null, + 'campaign_url' => isset($this->data->Transaction->CampaignChooseLink) ? $this->printData($this->data->Transaction->CampaignChooseLink) : null, + 'md_status' => $this->request->get('mdstatus'), + 'rand' => (string)$this->request->get('rnd'), + 'hash' => (string)$this->request->get('secure3dhash'), + 'hash_params' => (string)$this->request->get('hashparams'), + 'hash_params_val' => (string)$this->request->get('hashparamsval'), + 'secure_3d_hash' => (string)$this->request->get('secure3dhash'), + 'secure_3d_level' => (string)$this->request->get('secure3dsecuritylevel'), + 'masked_number' => (string)$this->request->get('MaskedPan'), + 'amount' => (string)$this->request->get('amount'), + 'currency' => (string)$this->request->get('currency'), + 'tx_status' => (string)$this->request->get('txstatus'), + 'eci' => (string)$this->request->get('eci'), + 'cavv' => (string)$this->request->get('cavv'), + 'xid' => (string)$this->request->get('xid'), + 'md_error_message' => (string)$this->request->get('mderrormessage'), + 'name' => (string)$this->request->get('firmaadi'), + 'email' => (string)$this->request->get('Email'), + 'extra' => null, + 'all' => $this->data, + '3d_all' => $this->request->all(), + ]; + + return $this; + } + + /** + * Make 3D Pay Payment + * + * @return $this + */ + public function make3DPayPayment() + { + $status = 'declined'; + $response = 'Declined'; + $proc_return_code = (string)$this->request->get('procreturncode'); + + $transaction_security = 'MPI fallback'; + if (in_array($this->request->get('mdstatus'), [1, 2, 3, 4]) && $proc_return_code == '00') { + if ($this->request->get('mdstatus') == '1') { + $transaction_security = 'Full 3D Secure'; + } elseif (in_array($this->request->get('mdstatus'), [2, 3, 4])) { + $transaction_security = 'Half 3D Secure'; + } + + $status = 'approved'; + $response = 'Approved'; + } + + $this->response = (object)[ + 'id' => (string)$this->request->get('authcode'), + 'order_id' => (string)$this->request->get('oid'), + 'trans_id' => (string)$this->request->get('hostrefnum'), + 'auth_code' => (string)$this->request->get('authcode'), + 'host_ref_num' => (string)$this->request->get('hostrefnum'), + 'response' => $response, + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'transaction_security' => $transaction_security, + 'proc_return_code' => $proc_return_code, + 'code' => $proc_return_code, + 'md_status' => $this->request->get('mdStatus'), + 'status' => $status, + 'status_detail' => isset($this->codes[$this->request->get('ProcReturnCode')]) ? (string)$this->request->get('ProcReturnCode') : null, + 'hash' => (string)$this->request->get('secure3dhash'), + 'rand' => (string)$this->request->get('rnd'), + 'hash_params' => (string)$this->request->get('hashparams'), + 'hash_params_val' => (string)$this->request->get('hashparamsval'), + 'masked_number' => (string)$this->request->get('MaskedPan'), + 'amount' => (string)$this->request->get('amount'), + 'currency' => (string)$this->request->get('currency'), + 'tx_status' => (string)$this->request->get('txstatus'), + 'eci' => (string)$this->request->get('eci'), + 'cavv' => (string)$this->request->get('cavv'), + 'xid' => (string)$this->request->get('xid'), + 'error_code' => (string)$this->request->get('errcode'), + 'error_message' => (string)$this->request->get('errmsg'), + 'md_error_message' => (string)$this->request->get('mderrormessage'), + 'campaign_url' => null, + 'name' => (string)$this->request->get('firmaadi'), + 'email' => (string)$this->request->get('Email'), + 'extra' => $this->request->get('Extra'), + 'all' => $this->request->all(), + ]; + + return $this; + } + + /** + * Get 3d Form Data + * + * @return array + */ + public function get3DFormData() + { + $security_data = $this->makeSecurityData(); + $hash_data = $this->make3dHashData($security_data); + + $inputs = [ + 'secure3dsecuritylevel' => $this->account->model == '3d_pay' ? '3D_PAY' : '3D', + 'mode' => $this->mode, + 'apiversion' => $this->version, + 'terminalprovuserid' => 'PROVAUT',//$this->account->username, + 'terminaluserid' => $this->account->username, + 'terminalmerchantid' => $this->account->client_id, + 'txntype' => $this->type, + 'txnamount' => $this->amountFormat($this->order->amount), + 'txncurrencycode' => $this->order->currency, + 'txninstallmentcount' => $this->order->installment > 1 ? $this->order->installment : '', + 'orderid' => $this->order->id, + 'terminalid' => $this->account->terminal_id, + 'successurl' => $this->order->success_url, + 'errorurl' => $this->order->fail_url, + 'customeremailaddress' => isset($this->order->email) ? $this->order->email : null, + 'customeripaddress' => $this->order->ip, + 'cardnumber' => $this->card->number, + 'cardexpiredatemonth' => $this->card->month, + 'cardexpiredateyear' => substr($this->card->year, -2), + 'cardcvv2' => $this->card->cvv, + 'secure3dhash' => $hash_data, + ]; + + //dd($inputs); + + return [ + 'gateway' => $this->gateway, + 'success_url' => $this->order->success_url, + 'fail_url' => $this->order->fail_url, + 'rand' => $this->order->rand, + 'hash' => $hash_data, + 'inputs' => $inputs, + ]; + } + + /** + * Send contents to WebService + * + * @param $contents + * @return $this + * @throws GuzzleException + */ + public function send($contents) + { + $client = new Client(); + + $response = $client->request('POST', $this->url, [ + 'body' => $contents + ]); + + $this->data = $this->XMLStringToObject($response->getBody()->getContents()); + + return $this; + } + + /** + * Prepare Order + * + * @param object $order + * @param object null $card + * @return mixed + * @throws UnsupportedTransactionTypeException + */ + public function prepare($order, $card = null) + { + $this->type = $this->types['pay']; + if (isset($order->transaction)) { + if (array_key_exists($order->transaction, $this->types)) { + $this->type = $this->types[$order->transaction]; + } else { + throw new UnsupportedTransactionTypeException('Unsupported transaction type!'); + } + } + + $this->order = $order; + $this->card = $card; + + if ($this->card) { + $this->card->month = str_pad($this->card->month, 2, '0', STR_PAD_LEFT); + } + } + + /** + * Make Payment + * + * @param object $card + * @return mixed + * @throws UnsupportedPaymentModelException + * @throws GuzzleException + */ + public function payment($card) + { + $this->card = $card; + + $model = 'regular'; + if (isset($this->account->model) && $this->account->model) { + $model = $this->account->model; + } + + if ($model == 'regular') { + $this->makeRegularPayment(); + } elseif ($model == '3d') { + $this->make3DPayment(); + } elseif ($model == '3d_pay') { + $this->make3DPayPayment(); + } else { + throw new UnsupportedPaymentModelException(); + } + + return $this; + } + + /** + * Refund or Cancel Order + * + * @param array $meta + * @param $type + * @return $this + * @throws GuzzleException + */ + protected function refundOrCancel(array $meta, $type) + { + $this->order = (object)[ + 'id' => $meta['order_id'], + 'amount' => isset($meta['amount']) ? $meta['amount'] : null, + ]; + + $security_data = $this->makeSecurityData(true); + $hash_data = $this->makeHashData($security_data); + + $currency = (int)$this->currencies[$meta['currency']]; + + $nodes = [ + 'GVPSRequest' => [ + 'Mode' => $this->mode, + 'Version' => $this->version, + 'ChannelCode' => '', + 'Terminal' => [ + 'ProvUserID' => 'PROVRFN',//$this->account->username, + 'UserID' => $this->account->refund_username, + 'HashData' => $hash_data, + 'ID' => $this->account->terminal_id, + 'MerchantID' => $this->account->client_id, + ], + 'Customer' => [ + 'IPAddress' => isset($meta['ip']) ? $meta['ip'] : null, + 'EmailAddress' => isset($meta['email']) ? $meta['email'] : null, + ], + 'Order' => [ + 'OrderID' => $this->order->id, + 'GroupID' => '', + ], + 'Transaction' => [ + 'Type' => $type, + 'InstallmentCnt' => '', + 'Amount' => $this->amountFormat($this->order->amount), + 'CurrencyCode' => $currency, + 'CardholderPresentCode' => '0', + 'MotoInd' => 'N', + 'OriginalRetrefNum' => $meta['ref_ret_num'], + ], + ] + ]; + + $xml = $this->createXML($nodes); + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $this->response = (object)[ + 'id' => isset($this->data->Transaction->AuthCode) ? $this->printData($this->data->Transaction->AuthCode) : null, + 'order_id' => isset($this->data->Order->OrderID) ? $this->printData($this->data->Order->OrderID) : null, + 'group_id' => isset($this->data->Order->GroupID) ? $this->printData($this->data->Order->GroupID) : null, + 'trans_id' => isset($this->data->Transaction->AuthCode) ? $this->printData($this->data->Transaction->AuthCode) : null, + 'response' => isset($this->data->Transaction->Response->Message) ? $this->printData($this->data->Transaction->Response->Message) : null, + 'auth_code' => isset($this->data->Transaction->AuthCode) ? $this->data->Transaction->AuthCode : null, + 'host_ref_num' => isset($this->data->Transaction->RetrefNum) ? $this->printData($this->data->Transaction->RetrefNum) : null, + 'ret_ref_num' => isset($this->data->Transaction->RetrefNum) ? $this->printData($this->data->Transaction->RetrefNum) : null, + 'hash_data' => isset($this->data->Transaction->HashData) ? $this->printData($this->data->Transaction->HashData) : null, + 'proc_return_code' => $this->getProcReturnCode(), + 'code' => $this->getProcReturnCode(), + 'error_code' => isset($this->data->Transaction->Response->Code) ? $this->printData($this->data->Transaction->Response->Code) : null, + 'error_message' => isset($this->data->Transaction->Response->ErrorMsg) ? $this->printData($this->data->Transaction->Response->ErrorMsg) : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'all' => $this->data, + ]; + + return $this; + } + + /** + * Refund Order + * + * @param $meta + * @return $this + * @throws GuzzleException + */ + public function refund(array $meta) + { + return $this->refundOrCancel($meta, 'refund'); + } + + /** + * Cancel Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function cancel(array $meta) + { + return $this->refundOrCancel($meta, 'void'); + } + + /** + * Order Status or History + * + * @param array $meta + * @param $type + * @return $this + * @throws GuzzleException + */ + protected function statusOrHistory(array $meta, $type) + { + $obj_item = 'OrderInqResult'; + if ($type == 'orderhistoryinq') { + $obj_item = 'OrderHistInqResult'; + } + + $this->order = (object)[ + 'id' => isset($meta['order_id']) ? $meta['order_id'] : null, + 'currency' => isset($this->currencies[$meta['currency']]) ? $this->currencies[$meta['currency']] : null, + 'amount' => '1', + ]; + + $security_data = $this->makeSecurityData(); + $hash_data = $this->makeHashData($security_data); + + $xml = $this->createXML([ + 'GVPSRequest' => [ + 'Mode' => $this->mode, + 'Version' => 'v0.01', + 'ChannelCode' => '', + 'Terminal' => [ + 'ProvUserID' => 'PROVAUT',//$this->account->username, + 'UserID' => $this->account->username, + 'HashData' => $hash_data, + 'ID' => $this->account->terminal_id, + 'MerchantID' => $this->account->client_id, + ], + 'Customer' => [ + 'IPAddress' => isset($meta['ip']) ? $meta['ip'] : null, + 'EmailAddress' => isset($meta['email']) ? $meta['email'] : null, + ], + 'Order' => [ + 'OrderID' => $this->order->id, + 'GroupID' => '', + ], + 'Card' => [ + 'Number' => '', + 'ExpireDate' => '', + 'CVV2' => '', + ], + 'Transaction' => [ + 'Type' => $type, + 'InstallmentCnt' => '', + 'Amount' => $this->order->amount ? $this->amountFormat($this->order->amount) : null, + 'CurrencyCode' => $this->order->currency, + 'CardholderPresentCode' => '0', + 'MotoInd' => 'N', + ], + ] + ]); + + $this->send($xml); + + $status = 'declined'; + if ($this->getProcReturnCode() == '00') { + $status = 'approved'; + } + + $data = [ + 'id' => isset($this->data->Order->{$obj_item}->AuthCode) ? $this->printData($this->data->Order->{$obj_item}->AuthCode) : null, + 'order_id' => isset($this->data->Order->OrderID) ? $this->printData($this->data->Order->OrderID) : null, + 'group_id' => isset($this->data->Order->GroupID) ? $this->printData($this->data->Order->GroupID) : null, + 'trans_id' => isset($this->data->Transaction->AuthCode) ? $this->printData($this->data->Transaction->AuthCode) : null, + 'response' => isset($this->data->Transaction->Response->Message) ? $this->printData($this->data->Transaction->Response->Message) : null, + 'auth_code' => isset($this->data->Order->{$obj_item}->AuthCode) ? $this->printData($this->data->Order->{$obj_item}->AuthCode) : null, + 'host_ref_num' => isset($this->data->Order->{$obj_item}->RetrefNum) ? $this->printData($this->data->Order->{$obj_item}->RetrefNum) : null, + 'ret_ref_num' => isset($this->data->Order->{$obj_item}->RetrefNum) ? $this->printData($this->data->Order->{$obj_item}->RetrefNum) : null, + 'hash_data' => isset($this->data->Transaction->HashData) ? $this->printData($this->data->Transaction->HashData) : null, + 'proc_return_code' => $this->getProcReturnCode(), + 'code' => $this->getProcReturnCode(), + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => isset($this->data->Transaction->Response->Code) ? $this->printData($this->data->Transaction->Response->Code) : null, + 'error_message' => isset($this->data->Transaction->Response->ErrorMsg) ? $this->printData($this->data->Transaction->Response->ErrorMsg) : null, + 'extra' => isset($this->data->Extra) ? $this->data->Extra : null, + 'all' => $this->data, + 'original' => $this->data, + ]; + + if ($type == 'orderhistoryinq') { + $data = array_merge($data, [ + 'order_txn' => isset($this->data->Order->OrderHistInqResult->OrderTxnList->OrderTxn) ? $this->data->Order->OrderHistInqResult->OrderTxnList->OrderTxn : [] + ]); + } + + $this->response = (object)$data; + + return $this; + } + + /** + * Order Status + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function status(array $meta) + { + return $this->statusOrHistory($meta, 'orderinq'); + } + + /** + * Order History + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function history(array $meta) + { + return $this->statusOrHistory($meta, 'orderhistoryinq'); + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @return mixed + */ + public function getAccount() + { + return $this->account; + } + + /** + * @return array + */ + public function getCurrencies() + { + return $this->currencies; + } + + /** + * @return mixed + */ + public function getOrder() + { + return $this->order; + } + + /** + * @return mixed + */ + public function getCard() + { + return $this->card; + } +} diff --git a/app/Core/Payment/Pos/Pos.php b/app/Core/Payment/Pos/Pos.php new file mode 100644 index 0000000..4591a01 --- /dev/null +++ b/app/Core/Payment/Pos/Pos.php @@ -0,0 +1,230 @@ +config = $config ? $config : require __DIR__ . '/config/pos.php'; + + // API Account + $this->account = (object) $account; + + // Bank API Exist + if ( ! array_key_exists($this->account->bank, $this->config['banks'])) { + throw new BankNotFoundException(); + } + + // Instance Bank Class + $this->instance(); + } + + /** + * Instance Bank Class + * + * @throws BankClassNullException + */ + public function instance() + { + // Bank Class + $class = $this->config['banks'][$this->account->bank]['class']; + + if ( ! $class) throw new BankClassNullException(); + + // Create Bank Class Object + $this->bank = new $class($this->config['banks'][$this->account->bank], $this->account, $this->config['currencies']); + } + + /** + * Prepare Order + * + * @param array $order + * @param array [] $card + * @return Pos + */ + public function prepare(array $order, array $card = []) + { + // Installment + $installment = 0; + if (isset($order['installment'])) { + $installment = $order['installment'] ? (int) $order['installment'] : 0; + } + + // Currency + $currency = null; + if (isset($order['currency'])) { + $currency = (int) $this->config['currencies'][$order['currency']]; + } + + // Order + $this->order = (object) array_merge($order, [ + 'installment' => $installment, + 'currency' => $currency, + ]); + + // Card + $this->card = $card ? (object) $card : null; + + // Prepare Order + $this->bank->prepare($this->order, $this->card); + + return $this; + } + + /** + * Make Payment + * + * @param array [] $card + * @return mixed + */ + public function payment(array $card = []) + { + // Credit Card + if ($card) { + $card = array_merge($card, [ + 'month' => str_pad((int) $card['month'], 2, 0, STR_PAD_LEFT), + 'year' => str_pad((int) $card['year'], 2, 0, STR_PAD_LEFT), + ]); + } + + $this->card = (object) $card; + + // Make Payment + return $this->bank->payment($this->card); + } + + /** + * Get gateway URL + * + * @return string|null + */ + public function getGatewayUrl() + { + return isset($this->bank->gateway) ? $this->bank->gateway : 'null'; + } + + /** + * @return array + */ + public function getConfig(){ + return $this->bank->getConfig(); + } + + /** + * @return mixed + */ + public function getAccount(){ + return $this->bank->getAccount(); + } + + /** + * @return array + */ + public function getCurrencies(){ + return $this->bank->getCurrencies(); + } + + /** + * @return mixed + */ + public function getOrder(){ + return $this->bank->getOrder(); + } + + /** + * @return mixed + */ + public function getCard(){ + return $this->bank->getCard(); + } + + /** + * Get 3d Form Data + * + * @return array + */ + public function get3dFormData() + { + $data = []; + + try { + $data = $this->bank->get3dFormData(); + } catch (Exception $e) {} + + return $data; + } + + /** + * Is success + * + * @return bool + */ + public function isSuccess() + { + return $this->bank->isSuccess(); + } + + /** + * Is error + * + * @return bool + */ + public function isError() + { + return $this->bank->isError(); + } +} diff --git a/app/Core/Payment/Pos/PosHelpersTrait.php b/app/Core/Payment/Pos/PosHelpersTrait.php new file mode 100644 index 0000000..461a29e --- /dev/null +++ b/app/Core/Payment/Pos/PosHelpersTrait.php @@ -0,0 +1,99 @@ +encode($nodes[$rootNodeName], 'xml', [ + XmlEncoder::ROOT_NODE_NAME => $rootNodeName, + XmlEncoder::ENCODING => $encoding + ]); + return $xml; + } + + /** + * Print Data + * + * @param $data + * @return null|string + */ + public function printData($data) + { + if ((is_object($data) || is_array($data)) && !count((array)$data)) { + $data = null; + } + + return (string)$data; + } + + /** + * Is success + * + * @return bool + */ + public function isSuccess() + { + $success = false; + if (isset($this->response) && $this->response->status == 'approved') { + $success = true; + } + + return $success; + } + + /** + * Is error + * + * @return bool + */ + public function isError() + { + return !$this->isSuccess(); + } + + /** + * Converts XML string to object + * + * @param string data + * @return object + */ + public function XMLStringToObject($data) + { + $encoder = new XmlEncoder(); + $xml = $encoder->decode($data, 'xml'); + return (object)json_decode(json_encode($xml)); + } +} diff --git a/app/Core/Payment/Pos/PosInterface.php b/app/Core/Payment/Pos/PosInterface.php new file mode 100644 index 0000000..2e5c239 --- /dev/null +++ b/app/Core/Payment/Pos/PosInterface.php @@ -0,0 +1,141 @@ + 'declined', + '1' => 'approved', + '2' => 'declined', + '00' => 'approved', + '0001' => 'bank_call', + '0005' => 'reject', + '0007' => 'bank_call', + '0012' => 'reject', + '0014' => 'reject', + '0030' => 'bank_call', + '0041' => 'reject', + '0043' => 'reject', + '0051' => 'reject', + '0053' => 'bank_call', + '0054' => 'reject', + '0057' => 'reject', + '0058' => 'reject', + '0062' => 'reject', + '0065' => 'reject', + '0091' => 'bank_call', + '0123' => 'transaction_not_found', + '0444' => 'bank_call', + ]; + + /** + * Transaction Types + * + * @var array + */ + public $types = [ + 'pay' => 'Sale', + 'pre' => 'Auth', + 'post' => 'Capt', + ]; + + /** + * Currencies + * + * @var array + */ + public $currencies = []; + + /** + * Fixed Currencies + * @var array + */ + protected $_currencies = [ + 'TRY' => 'TL', + 'USD' => 'US', + 'EUR' => 'EU', + 'GBP' => 'GB', + 'JPY' => 'JP', + 'RUB' => 'RU', + ]; + + /** + * Transaction Type + * + * @var string + */ + public $type; + + /** + * API Account + * + * @var array + */ + protected $account = []; + + /** + * Order Details + * + * @var array + */ + protected $order = []; + + /** + * Credit Card + * + * @var object + */ + protected $card; + + /** + * Request + * + * @var Request + */ + protected $request; + + /** + * Response Raw Data + * + * @var object + */ + protected $data; + + /** + * Processed Response Data + * + * @var mixed + */ + public $response; + + /** + * Configuration + * + * @var array + */ + protected $config = []; + + /** + * @var PosNetCrypt|null + */ + public $crypt; + + /** + * PosNet constructor. + * + * @param array $config + * @param array $account + * @param array $currencies + */ + public function __construct($config, $account, array $currencies) + { + $request = Request::createFromGlobals(); + $this->request = $request->request; + + $this->crypt = new PosNetCrypt(); + + $this->config = $config; + $this->account = $account; + $this->currencies = $currencies; + + $this->url = isset($this->config['urls'][$this->account->env]) ? + $this->config['urls'][$this->account->env] : + $this->config['urls']['production']; + + $this->gateway = isset($this->config['urls']['gateway'][$this->account->env]) ? + $this->config['urls']['gateway'][$this->account->env] : + $this->config['urls']['gateway']['production']; + + return $this; + } + + /** + * Get currency + * + * @return int|string + */ + protected function getCurrency() { + $search = array_search($this->order->currency, $this->currencies); + $currency = $this->order->currency; + if ($search) { + $currency = $this->_currencies[$search]; + } + + return $currency; + } + + /** + * Get amount + * + * @return int + */ + protected function getAmount() + { + return (int) str_replace('.', '', number_format($this->order->amount, 2, '.', '')); + } + + /** + * Get PrefixedOrderId + * To check the status of an order or cancel/refund order Yapikredi + * - requires the order length to be 24 + * - and order id prefix which is "TDSC" for 3D payments + * @return string + */ + protected function getPrefixedOrderId() + { + if($this->account->model == '3d'){ + return $this->config['order']['id_3d_prefix'] . $this->getOrderId($this->config['order']['id_total_length'] - strlen($this->config['order']['id_3d_prefix'])); + }elseif($this->account->model == '3d_pay') { + return $this->config['order']['id_3d_pay_prefix'] . $this->getOrderId($this->config['order']['id_total_length'] - strlen($this->config['order']['id_3d_pay_prefix'])); + } + return $this->config['order']['id_regular_prefix'] . $this->getOrderId($this->config['order']['id_total_length'] - strlen($this->config['order']['id_regular_prefix'])); + } + + /** + * Get orderId + * + * @param int $pad_length + * @return string + */ + protected function getOrderId(int $pad_length = null) + { + $this->order->id = str_replace('_','',$this->order->id); + if($pad_length === null) $pad_length = $this->config['order']['id_length']; + return (string) str_pad($this->order->id, $pad_length, '0', STR_PAD_LEFT); + } + + /** + * Get Installment + * + * @return int|string + */ + protected function getInstallment() + { + $installment = (int) $this->order->installment; + if (!$this->order->installment) { + $installment = '00'; + } + + return $installment; + } + + /** + * Create Regular Payment XML + * + * @return string + */ + protected function createRegularPaymentXML() + { + $transaction = strtolower($this->type); + + $nodes = [ + 'posnetRequest' => [ + 'mid' => $this->account->client_id, + 'tid' => $this->account->terminal_id, + 'tranDateRequired' => '1', + $transaction => [ + 'orderID' => $this->getOrderId(), + 'installment' => $this->getInstallment(), + 'amount' => $this->getAmount(), + 'currencyCode' => $this->getCurrency(), + 'ccno' => $this->card->number, + 'expDate' => $this->card->year . $this->card->month, + 'cvc' => $this->card->cvv, + ], + ] + ]; + + return $this->createXML($nodes, $encoding = 'ISO-8859-9'); + } + + /** + * Create Regular Payment Post XML + * + * @return string + */ + protected function createRegularPostXML() + { + $nodes = [ + 'posnetRequest' => [ + 'mid' => $this->account->client_id, + 'tid' => $this->account->terminal_id, + 'tranDateRequired' => '1', + 'capt' => [ + 'hostLogKey' => $this->order->host_ref_num, + 'amount' => $this->getAmount(), + 'currencyCode' => $this->getCurrency(), + 'installment' => $this->order->installment ? $this->getInstallment() : null + ], + ] + ]; + + return $this->createXML($nodes); + } + + /** + * Create 3D Payment XML + * @return string + */ + protected function create3DPaymentXML() + { + $nodes = [ + 'posnetRequest' => [ + 'mid' => $this->account->client_id, + 'tid' => $this->account->terminal_id, + 'oosResolveMerchantData' => [ + 'bankData' => $this->request->get('BankPacket'), + 'merchantData' => $this->request->get('MerchantPacket'), + 'sign' => $this->request->get('Sign'), + 'mac' => $this->create3DHash() + ], + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Get ProcReturnCode + * + * @return string|null + */ + protected function getProcReturnCode() + { + return (string) $this->data->approved == '1' ? '00' : $this->data->approved; + } + + /** + * Get Status Detail Text + * + * @return string|null + */ + protected function getStatusDetail() + { + $proc_return_code = $this->getProcReturnCode(); + + return isset($this->codes[$proc_return_code]) ? (string) $this->codes[$proc_return_code] : null; + } + + /** + * Get card exp date + * + * @return string + */ + protected function getCardExpDate() + { + $expDate = (string) substr($this->card->year, -2).$this->card->month; + return (string) $expDate; + } + + /** + * Get OOS transaction data + * + * @return object + * @throws GuzzleException + */ + public function getOosTransactionData() + { + $name = isset($this->card->name) ? $this->card->name : null; + if (!$name) { + $name = isset($this->order->name) ? $this->order->name : null; + } + + $contents = $this->createXML([ + 'posnetRequest' => [ + 'mid' => $this->account->client_id, + 'tid' => $this->account->terminal_id, + 'oosRequestData' => [ + 'posnetid' => $this->account->posnet_id, + 'ccno' => $this->card->number, + 'expDate' => $this->getCardExpDate(), + 'cvc' => $this->card->cvv, + 'amount' => $this->getAmount(), + 'currencyCode' => $this->getCurrency(), + 'installment' => $this->getInstallment(), + 'XID' => $this->getOrderId(), + 'cardHolderName' => $name, + 'tranType' => $this->type, + ] + ], + ]); + + $this->send($contents); + + return $this->data; + } + + /** + * Regular Payment + * + * @return $this + * @throws GuzzleException + */ + public function makeRegularPayment() + { + $contents = ''; + if (in_array($this->order->transaction, ['pay', 'pre'])) { + $contents = $this->createRegularPaymentXML(); + } elseif ($this->order->transaction == 'post') { + $contents = $this->createRegularPostXML(); + } + + $this->send($contents); + + $status = 'declined'; + $code = '1'; + $proc_return_code = '01'; + $obj = isset($this->data) ? $this->data : null; + $error_code = !empty($obj->respCode) ? $obj->respCode : null; + + if ($this->getProcReturnCode() == '00' && $this->getStatusDetail() == 'approved' && $obj && !$error_code) { + $status = 'approved'; + $code = isset($obj->approved) ? $obj->approved : null; + $proc_return_code = $this->getProcReturnCode(); + } + + $this->response = (object) [ + 'id' => isset($obj->authCode) ? $this->printData($obj->authCode) : null, + 'order_id' => $this->order->id, + 'fixed_order_id' => $this->getOrderId(), + 'group_id' => isset($obj->groupID) ? $this->printData($obj->groupID) : null, + 'trans_id' => isset($obj->authCode) ? $this->printData($obj->authCode) : null, + 'response' => $this->getStatusDetail(), + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'auth_code' => isset($obj->authCode) ? $this->printData($obj->authCode) : null, + 'host_ref_num' => isset($obj->hostlogkey) ? $this->printData($obj->hostlogkey) : null, + 'ret_ref_num' => isset($obj->hostlogkey) ? $this->printData($obj->hostlogkey) : null, + 'proc_return_code' => $proc_return_code, + 'code' => $code, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => $error_code, + 'error_message' => !empty($obj->respText) ? $this->printData($obj->respText) : null, + 'campaign_url' => null, + 'extra' => null, + 'all' => $this->data, + 'original' => $this->data, + ]; + + return $this; + } + + /** + * Check 3D Hash + * + * @return bool + */ + protected function check3DHash() + { + $check = false; + + if ($this->crypt instanceof PosNetCrypt) { + $decrypted_data = $this->crypt->decrypt($this->request->get('MerchantPacket'), $this->account->store_key); + + $decrypted_data_array = explode(';', $decrypted_data); + + $original_data = array_map('strval', [ + $this->account->client_id, + $this->account->terminal_id, + $this->getAmount(), + $this->getInstallment(), + $this->getOrderId() + ]); + + $decrypted_data_list = array_map('strval', [ + $decrypted_data_array[0], + $decrypted_data_array[1], + $decrypted_data_array[2], + $decrypted_data_array[3], + $decrypted_data_array[4] + ]); + + if ($original_data == $decrypted_data_list) { + $check = true; + } + } else { + $check = false; + } + + return $check; + } + + /** + * Make 3D Payment + * + * @return $this + * @throws GuzzleException + */ + public function make3DPayment() + { + $status = 'declined'; + $transaction_security = 'MPI fallback'; + + if ($this->check3DHash()) { + $contents = $this->create3DPaymentXML(); + $this->send($contents); + }else{ + goto end; + } + + if($this->getProcReturnCode() != '00'){ + goto end; + } + + if(!$this->verifyResponseMAC($this->data->oosResolveMerchantDataResponse)) { + goto end; + } + + if ($this->getProcReturnCode() == '00' && $this->getStatusDetail() == 'approved') { + if ($this->data->oosResolveMerchantDataResponse->mdStatus == '1') { + $transaction_security = 'Full 3D Secure'; + $status = 'approved'; + } elseif (in_array($this->data->oosResolveMerchantDataResponse->mdStatus, [2, 3, 4])) { + $transaction_security = 'Half 3D Secure'; + $status = 'approved'; + } + + //if 3D Authentication is failed + if($status != 'approved') goto end; + + $nodes = [ + 'posnetRequest' => [ + 'mid' => $this->account->client_id, + 'tid' => $this->account->terminal_id, + 'oosTranData' => [ + 'bankData' => $this->request->get('BankPacket'), + 'merchantData' => $this->request->get('MerchantPacket'), + 'sign' => $this->request->get('Sign'), + 'wpAmount' => 0, + 'mac' => $this->create3DHash() + ], + ] + ]; + + $contents = $this->createXML($nodes, $encoding = 'ISO-8859-9'); + $this->send($contents); + } + + if ($this->data->approved != 1) { + $status = 'declined'; + } + + end: + $this->response = (object) [ + 'id' => isset($this->data->authCode) ? $this->printData($this->data->authCode) : null, + 'order_id' => isset($this->order->id) ? $this->printData($this->order->id) : null, + 'fixed_order_id' => $this->getOrderId(), + 'group_id' => isset($this->data->groupID) ? $this->printData($this->data->groupID) : null, + 'trans_id' => isset($this->data->authCode) ? $this->printData($this->data->authCode) : null, + 'response' => $this->getStatusDetail(), + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'transaction_security' => $transaction_security, + 'auth_code' => isset($this->data->authCode) ? $this->printData($this->data->authCode) : null, + 'host_ref_num' => isset($this->data->hostlogkey) ? $this->printData($this->data->hostlogkey) : null, + 'ret_ref_num' => isset($this->data->transaction->hostlogkey) ? $this->printData($this->data->transaction->hostlogkey) : null, + 'proc_return_code' => $this->getProcReturnCode(), + 'code' => $this->getProcReturnCode(), + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => !empty($this->data->respCode) ? $this->printData($this->data->respCode) : null, + 'error_message' => !empty($this->data->respText) ? $this->printData($this->data->respText) : null, + 'md_status' => isset($this->data->oosResolveMerchantDataResponse->mdStatus) ? $this->printData($this->data->oosResolveMerchantDataResponse->mdStatus) : null, + 'hash' => [ + 'merchant_packet' => $this->request->get('MerchantPacket'), + 'bank_packet' => $this->request->get('BankPacket'), + 'sign' => $this->request->get('Sign'), + ], + 'xid' => isset($this->data->oosResolveMerchantDataResponse->xid) ? $this->data->oosResolveMerchantDataResponse->xid : null, + 'md_error_message' => isset($this->data->oosResolveMerchantDataResponse->mdErrorMessage) ? $this->data->oosResolveMerchantDataResponse->mdErrorMessage : null, + 'campaign_url' => null, + 'all' => $this->data, + ]; + + if(empty($this->response->error_message)) { + $this->response->error_message = $this->response->md_error_message; + } + + return $this; + } + + /** + * Make 3D Pay Payment + * + * @return $this + */ + public function make3DPayPayment() + { + //TODO + return $this; + } + + /** + * Get 3d Form Data + * + * @return array + * @throws GuzzleException + */ + public function get3DFormData() + { + $inputs = []; + $data = null; + + if ($this->card && $this->order) { + $data = $this->getOosTransactionData(); + + if($data->approved == 0) { + Log::debug('PosNET getOosTransactionData'); + Log::debug($data); + } + + $inputs = [ + 'posnetData' => $data->oosRequestDataResponse->data1, + 'posnetData2' => $data->oosRequestDataResponse->data2, + 'mid' => $this->account->client_id, + 'posnetID' => $this->account->posnet_id, + 'digest' => $data->oosRequestDataResponse->sign, + 'vftCode' => isset($this->account->promotion_code) ? $this->account->promotion_code : null, + 'merchantReturnURL' => $this->order->success_url, + 'url' => '', + 'lang' => $this->order->lang, + ]; + } + + return [ + 'gateway' => $this->gateway, + 'success_url' => $this->order->success_url, + 'fail_url' => $this->order->fail_url, + 'rand' => $data->oosRequestDataResponse->sign, + 'hash' => $data->oosRequestDataResponse->data1, + 'inputs' => $inputs, + ]; + } + + /** + * Send contents to WebService + * + * @param $contents + * @return $this + * @throws GuzzleException + */ + public function send($contents) + { + $client = new Client(); + + $headers = [ + 'Content-Type' => 'application/x-www-form-urlencoded' + ]; + + $response = $client->request('POST', $this->url, [ + 'headers' => $headers, + 'body' => "xmldata=" . $contents, + ]); + + $this->data = $this->XMLStringToObject($response->getBody()->getContents()); + + + + Log::debug($this->url); + Log::debug($contents); + Log::debug(json_decode(json_encode($this->data),1)); + + return $this; + } + + /** + * Prepare Order + * + * @param object $order + * @param object null $card + * @return mixed + * @throws UnsupportedTransactionTypeException + */ + public function prepare($order, $card = null) + { + $this->type = $this->types['pay']; + if (isset($order->transaction)) { + if (array_key_exists($order->transaction, $this->types)) { + $this->type = $this->types[$order->transaction]; + } else { + throw new UnsupportedTransactionTypeException('Unsupported transaction type!'); + } + } + + $this->order = $order; + $this->card = $card; + } + + /** + * Make Payment + * + * @param object $card + * @return mixed + * @throws UnsupportedPaymentModelException + * @throws GuzzleException + */ + public function payment($card) + { + $this->card = $card; + + $model = 'regular'; + if (isset($this->account->model) && $this->account->model) { + $model = $this->account->model; + } + + if ($model == 'regular') { + $this->makeRegularPayment(); + } elseif ($model == '3d') { + $this->make3DPayment(); + } elseif ($model == '3d_pay') { + //$this->make3DPayPayment(); + $this->make3DPayment(); + } else { + throw new UnsupportedPaymentModelException(); + } + + return $this; + } + + /** + * Refund or Cancel Order + * + * @param array $meta + * @param string $type + * @return $this + * @throws GuzzleException + */ + protected function refundOrCancel(array $meta, $type = 'cancel') + { + $this->order = (object) [ + 'id' => $meta['order_id'], + 'host_ref_num' => isset($meta['host_ref_num']) ? $meta['host_ref_num'] : null, + 'auth_code' => isset($meta['auth_code']) ? $meta['auth_code'] : null, + 'amount' => isset($meta['amount']) ? $meta['amount'] : null, + 'currency' => isset($meta['currency']) ? $this->_currencies[$meta['currency']] : null, + ]; + + $nodes = [ + 'mid' => $this->account->client_id, + 'tid' => $this->account->terminal_id, + 'tranDateRequired' => '1', + ]; + + if ($type == 'refund') { + $return = [ + 'amount' => $this->getAmount(), + 'currencyCode' => $this->getCurrency(), + 'orderID' => $this->getPrefixedOrderId(), + ]; + + if ($this->order->host_ref_num) { + $return['hostLogKey'] = $this->order->host_ref_num; + unset($return['orderID']); + } + + $append = [ + 'return' => $return, + ]; + } else { + $reverse = [ + 'transaction' => 'pointUsage', + 'orderID' => $this->getPrefixedOrderId(), + 'authCode' => $this->order->auth_code, + ]; + + if ($this->order->host_ref_num) { + $reverse = [ + 'transaction' => 'pointUsage', + 'hostLogKey' => $this->order->host_ref_num, + 'authCode' => $this->order->auth_code, + ]; + } + + $append = [ + 'reverse' => $reverse, + ]; + } + + $nodes = array_merge($nodes, $append); + + $xml = $this->createXML([ + 'posnetRequest' => $nodes + ]); + + $this->send($xml); + + $status = 'declined'; + $code = '1'; + $proc_return_code = '01'; + $obj = isset($this->data) ? $this->data : null; + $error_code = !empty($obj->respCode) ? $obj->respCode : null; + + if ($this->getProcReturnCode() == '00' && $obj && !$error_code) { + $status = 'approved'; + $code = isset($obj->approved) ? $obj->approved : null; + $proc_return_code = $this->getProcReturnCode(); + } + + $transaction = null; + $transaction_type = null; + $state = isset($obj->state) ? $obj->state : null; + if ($state == 'Sale') { + $transaction = 'pay'; + $transaction_type = $this->types[$transaction]; + } elseif ($state == 'Authorization') { + $transaction = 'pre'; + $transaction_type = $this->types[$transaction]; + } elseif ($state == 'Capture') { + $transaction = 'post'; + $transaction_type = $this->types[$transaction]; + } + + $data = [ + 'id' => isset($obj->transaction->authCode) ? $this->printData($obj->transaction->authCode) : null, + 'order_id' => isset($this->order->id) ? $this->printData($this->order->id) : null, + 'fixed_order_id' => isset($obj->transaction->orderID) ? $this->printData($obj->transaction->orderID) : null, + 'group_id' => isset($obj->transaction->groupID) ? $this->printData($obj->transaction->groupID) : null, + 'trans_id' => isset($obj->transaction->authCode) ? $this->printData($obj->transaction->authCode) : null, + 'response' => $this->getStatusDetail(), + 'auth_code' => isset($obj->transaction->authCode) ? $this->printData($obj->transaction->authCode) : null, + 'host_ref_num' => isset($obj->transaction->hostlogkey) ? $this->printData($obj->transaction->hostlogkey) : null, + 'ret_ref_num' => isset($obj->transaction->hostlogkey) ? $this->printData($obj->transaction->hostlogkey) : null, + 'transaction' => $transaction, + 'transaction_type' => $transaction_type, + 'state' => $state, + 'date' => isset($obj->transaction->tranDate) ? $this->printData($obj->transaction->tranDate) : null, + 'proc_return_code' => $proc_return_code, + 'code' => $code, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => $error_code, + 'error_message' => !empty($obj->respText) ? $this->printData($obj->respText) : null, + 'extra' => null, + 'all' => $this->data, + 'original' => $this->data, + ]; + + $this->response = (object) $data; + + return $this; + } + + /** + * Refund Order + * + * @param $meta + * @return $this + * @throws GuzzleException + */ + public function refund(array $meta) + { + return $this->refundOrCancel($meta, 'refund'); + } + + /** + * Cancel Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function cancel(array $meta) + { + return $this->refundOrCancel($meta, 'cancel'); + } + + /** + * Order Status + * + * @param array $meta + * @param bool $history + * @return $this + * @throws GuzzleException + */ + public function status(array $meta, $history = false) + { + $this->order = (object) [ + 'id' => isset($meta['order_id']) ? $meta['order_id'] : null, + ]; + + $xml = $this->createXML([ + 'posnetRequest' => [ + 'mid' => $this->account->client_id, + 'tid' => $this->account->terminal_id, + 'agreement' => [ + 'orderID' => $this->getPrefixedOrderId(), + ], + ] + ]); + + $this->send($xml); + + $status = 'declined'; + $code = '1'; + $proc_return_code = '01'; + $obj = isset($this->data->transactions) ? $this->data->transactions : null; + $error_code = !empty($this->data->respCode) ? $this->data->respCode : null; + + if ($this->getProcReturnCode() == '00' && $obj && !$error_code) { + $status = 'approved'; + $code = isset($obj->approved) ? $obj->approved : null; + $proc_return_code = $this->getProcReturnCode(); + } + + $transaction = null; + $transaction_type = null; + + $state = null; + $auth_code = null; + $refunds = []; + if (isset($this->data->transactions->transaction)) { + $state = isset($this->data->transactions->transaction->state) ? + $this->data->transactions->transaction->state : + null; + + $auth_code = isset($obj->transaction->authCode) ? $this->printData($obj->transaction->authCode) : null; + + if (is_array($this->data->transactions->transaction) && count($this->data->transactions->transaction)) { + $state = $this->data->transactions->transaction[0]->state; + $auth_code = $this->data->transactions->transaction[0]->authCode; + + if (count($this->data->transactions->transaction) > 1 && $history) { + $_currencies = array_flip($this->_currencies); + + foreach ($this->data->transactions->transaction as $key => $_transaction) { + if ($key > 0) { + $currency = isset($_currencies[$_transaction->currencyCode]) ? + (string) $_currencies[$_transaction->currencyCode] : + $_transaction->currencyCode; + $refunds[] = [ + 'amount' => (double) $_transaction->amount, + 'currency' => $currency, + 'auth_code' => $_transaction->authCode, + 'date' => $_transaction->tranDate, + ]; + } + } + } + } + } + + if ($state == 'Sale') { + $transaction = 'pay'; + $state = $transaction; + $transaction_type = $this->types[$transaction]; + } elseif ($state == 'Authorization') { + $transaction = 'pre'; + $state = $transaction; + $transaction_type = $this->types[$transaction]; + } elseif ($state == 'Capture') { + $transaction = 'post'; + $state = $transaction; + $transaction_type = $this->types[$transaction]; + } elseif ($state == 'Bonus_Reverse') { + $state = 'cancel'; + } else { + $state = 'mixed'; + } + + $data = [ + 'id' => $auth_code, + 'order_id' => isset($this->order->id) ? $this->printData($this->order->id) : null, + 'fixed_order_id' => $this->getOrderId(), + 'group_id' => isset($obj->transaction->groupID) ? $this->printData($obj->transaction->groupID) : null, + 'trans_id' => $auth_code, + 'response' => $this->getStatusDetail(), + 'auth_code' => $auth_code, + 'host_ref_num' => isset($obj->transaction->hostLogKey) ? $this->printData($obj->transaction->hostLogKey) : null, + 'ret_ref_num' => null, + 'transaction' => $transaction, + 'transaction_type' => $transaction_type, + 'state' => $state, + 'date' => isset($obj->transaction->tranDate) ? $this->printData($obj->transaction->tranDate) : null, + 'refunds' => $refunds, + 'proc_return_code' => $proc_return_code, + 'code' => $code, + 'status' => $status, + 'status_detail' => $this->getStatusDetail(), + 'error_code' => $error_code, + 'error_message' => !empty($this->data->respText) ? $this->printData($this->data->respText) : null, + 'extra' => null, + 'all' => $this->data, + 'original' => $this->data, + ]; + + if (!$history) { + unset($data['refunds']); + } + + $this->response = (object) $data; + + return $this; + } + + /** + * Order History + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function history(array $meta) + { + return $this->status($meta, true); + } + + /** + * @return array + */ + public function getConfig(){ + return $this->config; + } + + /** + * @return mixed + */ + public function getAccount(){ + return $this->account; + } + + /** + * @return array + */ + public function getCurrencies(){ + return $this->currencies; + } + + /** + * @return mixed + */ + public function getOrder(){ + return $this->order; + } + + /** + * @return mixed + */ + public function getCard(){ + return $this->card; + } + + /** + * Hash string + * + * @return string + */ + public function hashString(string $str) + { + return base64_encode(hash('sha256',$str,true)); + } + + /** + * Create 3D Hash (MAC) + * + * @return string + */ + public function create3DHash() + { + $hash_str = ''; + + $firstHash = $this->hashString($this->account->store_key . ";" . $this->account->terminal_id); + + if ($this->account->model == '3d' || $this->account->model == '3d_pay') { + $hash_str = $this->hashString($this->getOrderId() . ";" . $this->getAmount() . ";" . $this->getCurrency() . ";" . $this->account->client_id . ";" . $firstHash); + } + + return $hash_str; + } + + /** + * verifies the if request came from bank + * + * @param mixed $data oosResolveMerchantDataResponse + * @return boolean + */ + public function verifyResponseMAC($data) + { + $hash_str = ''; + + $firstHash = $this->hashString($this->account->store_key . ";" . $this->account->terminal_id); + + if ($this->account->model == '3d' || $this->account->model == '3d_pay') { + $hash_str = $this->hashString($data->mdStatus . ";" . $this->getOrderId() . ";" . $this->getAmount() . ";" . $this->getCurrency() . ";" . $this->account->client_id . ";" . $firstHash); + } + + return $hash_str == $data->mac; + } +} diff --git a/app/Core/Payment/Pos/PosNetCrypt.php b/app/Core/Payment/Pos/PosNetCrypt.php new file mode 100644 index 0000000..80c1955 --- /dev/null +++ b/app/Core/Payment/Pos/PosNetCrypt.php @@ -0,0 +1,170 @@ +algo = 'des-ede3-cbc'; + $this->block = 8; + $this->ks = 24; + $this->error = ''; + } + + /** + * This function is used to get encryption errors. + * + * @return string + */ + public function getLastError () + { + return $this->error; + } + + /** + * @return string + */ + public function createIV () + { + $temp = sprintf("%05d", rand()); + $temp .= sprintf("%05d", rand()); + $temp .= sprintf("%05d", rand()); + $temp .= sprintf("%05d", rand()); + + return pack("H*", substr($temp, 0, 16)); + } + + /** + * @param $data + * @param $key + * @return string + */ + public function encrypt($data, $key) + { + // Create IV + $iv = $this->createIV(); + + // Encrypt Data + $encrypted_data = openssl_encrypt($data, $this->algo, $this->detKey($key), OPENSSL_RAW_DATA, $iv); + + // Add IV and Convert to HEX + $hex_encrypted_data = strtoupper(bin2hex($iv)).strtoupper(bin2hex($encrypted_data)); + + // Add CRC + $hex_encrypted_data = $this->addCrc($hex_encrypted_data); + + return $hex_encrypted_data; + } + + /** + * @param $data + * @param $key + * @return bool|string + */ + public function decrypt($data, $key) { + + $parsed_data = $this->parseEncryptedData($data); + + if (!$parsed_data) return false; + + // Check CRC + if (!$this->checkCrc($parsed_data['crc_data'], $parsed_data['crc'])) { + $this->error = "CRC is not valid! (" . $parsed_data['crc'] . ")"; + return FALSE; + } + + // Get IV + $iv = pack("H*", $parsed_data['iv']); + + // Get Encrypted Data + $encrypted_data = pack("H*", $parsed_data['payload']); + + // Decrypt Data + $decrypted_data = openssl_decrypt($encrypted_data, $this->algo, $this->detKey($key), OPENSSL_RAW_DATA, $iv); + + return $decrypted_data; + } + + /** + * @param $key + * @return bool|string + */ + public function detKey($key) + { + $deskey = substr(strtoupper(md5($key)), 0, $this->ks); + return $deskey; + } + + /** + * @param $data + * @return string + */ + public function addCrc($data) + { + $crc = crc32($data); + $hex_crc = sprintf("%08x", $crc); + $data .= strtoupper($hex_crc); + + return $data; + } + + /** + * @param $data + * @param $crc + * @return bool + */ + public function checkCrc($data, $crc) + { + $crc_calc = crc32($data); + $hex_crc = sprintf("%08x", $crc_calc); + $crc_calc = strtoupper($hex_crc); + + return strcmp($crc_calc, $crc) == 0 ? true : false; + } + + /** + * @param string $data + * @return array|bool + */ + private function parseEncryptedData(string $data){ + + if (strlen($data) < 16 + 8) return false; + + return [ + 'crc' => substr($data, -8), + 'crc_data' => substr($data, 0, strlen($data)-8), + 'iv' => substr($data, 0, 16), + 'payload' => substr($data, 16, strlen($data)-16-8) + ]; + } +} diff --git a/app/Core/Payment/Pos/QPos.php b/app/Core/Payment/Pos/QPos.php new file mode 100644 index 0000000..37b7323 --- /dev/null +++ b/app/Core/Payment/Pos/QPos.php @@ -0,0 +1,644 @@ + 'approved', + '01' => 'bank_call', + '02' => 'bank_call', + '05' => 'reject', + '09' => 'try_again', + '12' => 'invalid_transaction', + '28' => 'reject', + '51' => 'insufficient_balance', + '54' => 'expired_card', + '57' => 'does_not_allow_card_holder', + '62' => 'restricted_card', + '77' => 'request_rejected', + '99' => 'general_error', + ]; + + /** + * Transaction Types + * + * @var array + */ + public $types = [ + 'pay' => 'Auth', + 'pre' => 'PreAuth', + 'post' => 'PostAuth', + ]; + + /** + * Currencies + * + * @var array + */ + public $currencies = []; + + /** + * Transaction Type + * + * @var string + */ + public $type; + + /** + * API Account + * + * @var array + */ + protected $account = []; + + /** + * Order Details + * + * @var array + */ + protected $order = []; + + /** + * Credit Card + * + * @var object + */ + protected $card; + + /** + * Request + * + * @var Request + */ + protected $request; + + /** + * Response Raw Data + * + * @var object + */ + protected $data; + + /** + * Processed Response Data + * + * @var mixed + */ + public $response; + + /** + * Configuration + * + * @var array + */ + protected $config = []; + + /** + * Response Raw Data + * + * @var object + */ + protected $mbrId = 5; + + /** + * EstPos constructor. + * + * @param array $config + * @param mixed $account + * @param array $currencies + */ + public function __construct($config, $account, array $currencies) + { + $this->config = $config; + $this->account = $account; + $this->currencies = $currencies; + + $this->url = isset($this->config['urls'][$this->account->env]) ? + $this->config['urls'][$this->account->env] : + $this->config['urls']['production']; + + $this->gateway = isset($this->config['urls']['gateway'][$this->account->env]) ? + $this->config['urls']['gateway'][$this->account->env] : + $this->config['urls']['gateway']['production']; + + + return $this; + } + + /** + * Create Regular Payment XML + * + * @return string + */ + protected function createRegularPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'CardType' => isset($this->card->type) ? $this->card->type : null, + 'Number' => $this->card->number, + 'Expires' => $this->card->month . '/' . $this->card->year, + 'Cvv2Val' => $this->card->cvv, + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + 'BillTo' => [ + 'Name' => $this->order->name ? $this->order->name : null, + ] + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create Regular Payment Post XML + * + * @return string + */ + protected function createRegularPostXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->types[$this->order->transaction], + 'OrderId' => $this->order->id, + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create 3D Payment XML + * @return string + */ + protected function create3DPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'Number' => $this->request->get('md'), + 'Expires' => '', + 'Cvv2Val' => '', + 'PayerTxnId' => $this->request->get('xid'), + 'PayerSecurityLevel' => $this->request->get('eci'), + 'PayerAuthenticationCode' => $this->request->get('cavv'), + 'CardholderPresentCode' => '13', + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + ] + ]; + + if ($this->order->name) { + $nodes['BillTo'] = [ + 'Name' => $this->order->name, + ]; + } + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Get ProcReturnCode + * + * @return string|null + */ + protected function getProcReturnCode() + { + return isset($this->data->ProcReturnCode) ? (string)$this->data->ProcReturnCode : null; + } + + /** + * Get Status Detail Text + * + * @return string|null + */ + protected function getStatusDetail() + { + $proc_return_code = $this->getProcReturnCode(); + + return $proc_return_code ? (isset($this->codes[$proc_return_code]) ? (string)$this->codes[$proc_return_code] : null) : null; + } + + /** + * Create 3D Hash + * + * @return string + */ + public function create3DHash() + { + $hash_str = ''; + $hash_str = $this->mbrId . $this->order->id . $this->order->amount . $this->order->success_url . $this->order->fail_url . $this->type . $this->order->installment . $this->order->rand . $this->account->merchant_pass; + + return base64_encode(pack('H*', sha1($hash_str))); + } + + /** + * Check 3D Hash + * + * @param array $data + * @return bool + */ + public function check3DHash($data) + { + $return = false; + $responseHash = $data['ResponseHash']; + + //MerchantID + MerchantPass + OrderId + AuthCode + ProcReturnCode + 3DStatus + ResponseRnd + UserCode + $generatedHash = $this->account->merchant_id . $this->account->merchant_pass . $data['OrderId'] . $data['AuthCode'] . $data['ProcReturnCode'] . $data['3DStatus'] . $data['ResponseRnd'] . $this->account->user_code; + $generatedHash = base64_encode(pack('H*', sha1($generatedHash))); + + if ($generatedHash == $responseHash) { + $return = true; + } + + return $return; + } + + /** + * Regular Payment + * + * @return $this + * @throws GuzzleException + */ + public function makeRegularPayment() + { + return false; + } + + /** + * Make 3D Payment + * + * @return $this + * @throws GuzzleException + */ + public function make3DPayment() + { + return false; + } + + /** + * Make 3D Pay Payment + * + * @return $this + */ + public function make3DPayPayment() + { + $this->request = Request::createFromGlobals(); + + $status = 'declined'; + + if ($this->check3DHash($this->request->request->all()) && (string)$this->request->get('ProcReturnCode') == '00') { + if (in_array($this->request->get('3DStatus'), [1])) { + $status = 'approved'; + } + } + + $transaction_security = 'MPI fallback'; + if ($status == 'approved') { + if ($this->request->get('3DStatus') == '1') { + $transaction_security = 'Full 3D Secure'; + } elseif (in_array($this->request->get('3DStatus'), [2, 3, 4])) { + $transaction_security = 'Half 3D Secure'; + } + } + + $this->response = (object)[ + 'id' => (string)$this->request->get('RequestGuid'), + 'trans_id' => (string)$this->request->get('HostRefNum'), + 'auth_code' => (string)$this->request->get('AuthCode'), + 'host_ref_num' => (string)$this->request->get('HostRefNum'), + //'response' => (string)$this->request->get('Response'), + 'order_id' => (string)$this->request->get('OrderId'), + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'transaction_security' => $transaction_security, + 'code' => (string)$this->request->get('ProcReturnCode'), + 'md_status' => $this->request->get('3DStatus'), + 'status' => $status, + 'status_detail' => isset($this->codes[$this->request->get('ProcReturnCode')]) ? (string)$this->request->get('ProcReturnCode') : null, + 'hash' => (string)$this->request->get('ResponseHash'), + 'rand' => (string)$this->request->get('Rnd'), + //'hash_params' => (string)$this->request->get('HASHPARAMS'), + //'hash_params_val' => (string)$this->request->get('HASHPARAMSVAL'), + 'masked_number' => (string)$this->request->get('CardMask'), + //'month' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Month'), + //'year' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Year'), + 'amount' => (string)$this->request->get('PurchAmount'), + 'currency' => (string)$this->request->get('Currency'), + 'tx_status' => (string)$this->request->get('TxnStatus'), + 'eci' => (string)$this->request->get('Eci'), + 'cavv' => (string)$this->request->get('CavvResult'), + //'xid' => (string)$this->request->get('xid'), + //'error_code' => (string)$this->request->get('ErrCode'), + 'error_message' => (string)$this->request->get('ErrMsg'), + //'md_error_message' => (string)$this->request->get('mdErrorMsg'), + 'name' => (string)$this->request->get('MrcName'), + //'email' => (string)$this->request->get('Email'), + 'extra' => $this->request->get('Extra'), + 'response_rnd' => $this->request->get('ResponseRnd'), + 'all' => $this->request->request->all(), + ]; + + return $this; + } + + /** + * Get 3d Form Data + * + * @return array + */ + public function get3DFormData() + { + $data = []; + + if ($this->order) { + $this->order->hash = $this->create3DHash(); + + $inputs = [ + 'MbrId' => $this->mbrId, + 'MerchantID' => $this->account->merchant_id, + 'UserCode' => $this->account->user_code, + 'UserPass' => $this->account->user_pass, + 'SecureType' => $this->account->model == '3d_pay' ? '3DPay' : '3D', + 'TxnType' => $this->type, + 'InstallmentCount' => $this->order->installment, + 'Currency' => $this->order->currency, + 'OkUrl' => $this->order->success_url, + 'FailUrl' => $this->order->fail_url, + 'OrderId' => $this->order->id, + 'PurchAmount' => $this->order->amount, + 'Lang' => $this->order->lang, + 'Rnd' => $this->order->rand, + 'Hash' => $this->order->hash, + 'Pan' => $this->card->number, + 'Cvv2' => $this->card->cvv, + 'Expiry' => $this->getCardExpDate(), + ]; + + $data = [ + 'gateway' => $this->gateway, + 'success_url' => $this->order->success_url, + 'fail_url' => $this->order->fail_url, + 'rand' => $this->order->rand, + 'hash' => $this->order->hash, + 'inputs' => $inputs, + ]; + } + + return $data; + } + + /** + * Send contents to WebService + * + * @param $contents + * @return $this + * @throws GuzzleException + */ + public function send($contents) + { + $client = new Client(); + + $response = $client->request('POST', $this->url, [ + 'body' => $contents + ]); + + $this->data = $this->XMLStringToObject($response->getBody()->getContents()); + + return $this; + } + + /** + * Prepare Order + * + * @param object $order + * @param object null $card + * @return mixed + * @throws UnsupportedTransactionTypeException + */ + public function prepare($order, $card = null) + { + $this->type = $this->types['pay']; + if (isset($order->transaction)) { + if (array_key_exists($order->transaction, $this->types)) { + $this->type = $this->types[$order->transaction]; + } else { + throw new UnsupportedTransactionTypeException('Unsupported transaction type!'); + } + } + + $this->order = $order; + $this->card = $card; + + $this->order->installment = $this->order->installment == 0 ? 0 : $this->order->installment; + } + + /** + * Make Payment + * + * @param object $card + * @return mixed + * @throws UnsupportedPaymentModelException + * @throws GuzzleException + */ + public function payment($card) + { + $this->card = $card; + + $model = 'regular'; + if (isset($this->account->model) && $this->account->model) { + $model = $this->account->model; + } + + if ($model == 'regular') { + $this->makeRegularPayment(); + } elseif ($model == '3d') { + $this->make3DPayment(); + } elseif ($model == '3d_pay') { + $this->make3DPayPayment(); + } else { + throw new UnsupportedPaymentModelException(); + } + + return $this; + } + + /** + * Refund Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function refund(array $meta) + { + return false; + } + + /** + * Cancel Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function cancel(array $meta) + { + return false; + } + + /** + * Order Status + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function status(array $meta) + { + return false; + } + + /** + * Order History + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function history(array $meta) + { + return false; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @return mixed + */ + public function getAccount() + { + return $this->account; + } + + /** + * @return array + */ + public function getCurrencies() + { + return $this->currencies; + } + + /** + * @return mixed + */ + public function getOrder() + { + return $this->order; + } + + /** + * @return mixed + */ + public function getCard() + { + return $this->card; + } + + /** + * @return string|null + */ + public function getCardCode() + { + $card_type = null; + if (isset($this->card->type)) { + if ($this->card->type == 'visa') { + $card_type = '0'; + } elseif ($this->card->type == 'master') { + $card_type = '1'; + } elseif ($this->card->type == '1' || $this->card->type == '2') { + $card_type = $this->card->type; + } + } + return $card_type; + } + + protected function getCardExpDate() + { + $year = (string)substr($this->card->year, 2, 2); + $month = (string)substr($this->card->month, 0, 2); + + return (string)$month . $year; + } + + +} diff --git a/app/Core/Payment/Pos/VPos.php b/app/Core/Payment/Pos/VPos.php new file mode 100644 index 0000000..3db8465 --- /dev/null +++ b/app/Core/Payment/Pos/VPos.php @@ -0,0 +1,656 @@ + 'approved', + '01' => 'bank_call', + '02' => 'bank_call', + '05' => 'reject', + '09' => 'try_again', + '12' => 'invalid_transaction', + '28' => 'reject', + '51' => 'insufficient_balance', + '54' => 'expired_card', + '57' => 'does_not_allow_card_holder', + '62' => 'restricted_card', + '77' => 'request_rejected', + '99' => 'general_error', + ]; + + /** + * Transaction Types + * + * @var array + */ + public $types = [ + 'pay' => 'Auth', + 'pre' => 'PreAuth', + 'post' => 'PostAuth', + ]; + + /** + * Currencies + * + * @var array + */ + public $currencies = []; + + /** + * Transaction Type + * + * @var string + */ + public $type; + + /** + * API Account + * + * @var array + */ + protected $account = []; + + /** + * Order Details + * + * @var array + */ + protected $order = []; + + /** + * Credit Card + * + * @var object + */ + protected $card; + + /** + * Request + * + * @var Request + */ + protected $request; + + /** + * Response Raw Data + * + * @var object + */ + protected $data; + + /** + * Processed Response Data + * + * @var mixed + */ + public $response; + + /** + * Configuration + * + * @var array + */ + protected $config = []; + + /** + * EstPos constructor. + * + * @param array $config + * @param mixed $account + * @param array $currencies + */ + public function __construct($config, $account, array $currencies) + { + $this->config = $config; + $this->account = $account; + $this->currencies = $currencies; + + $this->url = isset($this->config['urls'][$this->account->env]) ? + $this->config['urls'][$this->account->env] : + $this->config['urls']['production']; + + $this->gateway = isset($this->config['urls']['gateway'][$this->account->env]) ? + $this->config['urls']['gateway'][$this->account->env] : + $this->config['urls']['gateway']['production']; + + + return $this; + } + + /** + * Create Regular Payment XML + * + * @return string + */ + protected function createRegularPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'CardType' => isset($this->card->type) ? $this->card->type : null, + 'Number' => $this->card->number, + 'Expires' => $this->card->month . '/' . $this->card->year, + 'Cvv2Val' => $this->card->cvv, + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + 'BillTo' => [ + 'Name' => $this->order->name ? $this->order->name : null, + ] + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create Regular Payment Post XML + * + * @return string + */ + protected function createRegularPostXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->types[$this->order->transaction], + 'OrderId' => $this->order->id, + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create 3D Payment XML + * @return string + */ + protected function create3DPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'Number' => $this->request->get('md'), + 'Expires' => '', + 'Cvv2Val' => '', + 'PayerTxnId' => $this->request->get('xid'), + 'PayerSecurityLevel' => $this->request->get('eci'), + 'PayerAuthenticationCode' => $this->request->get('cavv'), + 'CardholderPresentCode' => '13', + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + ] + ]; + + if ($this->order->name) { + $nodes['BillTo'] = [ + 'Name' => $this->order->name, + ]; + } + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Get ProcReturnCode + * + * @return string|null + */ + protected function getProcReturnCode() + { + return isset($this->data->ProcReturnCode) ? (string)$this->data->ProcReturnCode : null; + } + + /** + * Get Status Detail Text + * + * @return string|null + */ + protected function getStatusDetail() + { + $proc_return_code = $this->getProcReturnCode(); + + return $proc_return_code ? (isset($this->codes[$proc_return_code]) ? (string)$this->codes[$proc_return_code] : null) : null; + } + + /** + * Create 3D Hash + * + * @return string + */ + public function create3DHash() + { + $hash_str = ''; + + if ($this->account->model == '3d') { + $hash_str = $this->account->client_id . $this->order->id . $this->order->amount . $this->order->success_url . $this->order->fail_url . $this->order->rand . $this->account->store_key; + } elseif ($this->account->model == '3d_pay') { + $hash_str = $this->account->client_id . $this->order->id . $this->order->amount . $this->order->success_url . $this->order->fail_url . $this->type . $this->order->installment . $this->order->rand . $this->account->store_key; + } + + return base64_encode(pack('H*',sha1($hash_str))); + } + + /** + * Check 3D Hash + * + * @param array $data + * @return bool + */ + public function check3DHash($data) + { + $hash_params = $data['HASHPARAMS']; + $hash_params_val = $data['HASHPARAMSVAL']; + $hash_param = $data['HASH']; + $params_val = ''; + + $hashparams_arr = explode(':', $hash_params); + foreach ($hashparams_arr as $value) { + if(!empty($value) && isset($data[$value])){ + $params_val = $params_val . $data[$value]; + } + } + + $hash_val = $params_val . $this->account->store_key; + $hash = base64_encode(sha1($hash_val, true)); + + $return = false; + if ($hash_params && !($params_val != $hash_params_val || $hash_param != $hash)) { + $return = true; + } + + return $return; + } + + /** + * Regular Payment + * + * @return $this + * @throws GuzzleException + */ + public function makeRegularPayment() + { + return false; + } + + /** + * Make 3D Payment + * + * @return $this + * @throws GuzzleException + */ + public function make3DPayment() + { + return false; + } + + /** + * Make 3D Pay Payment + * + * @return $this + */ + public function make3DPayPayment() + { + $this->request = Request::createFromGlobals(); + + $status = 'declined'; + + if ($this->check3DHash($this->request->request->all()) && (string)$this->request->get('ProcReturnCode') == '00') { + if (in_array($this->request->get('mdStatus'), [1, 2, 3, 4])) { + $status = 'approved'; + } + } + + $transaction_security = 'MPI fallback'; + if ($status == 'approved') { + if ($this->request->get('mdStatus') == '1') { + $transaction_security = 'Full 3D Secure'; + } elseif (in_array($this->request->get('mdStatus'), [2, 3, 4])) { + $transaction_security = 'Half 3D Secure'; + } + } + + $this->response = (object)[ + 'id' => (string)$this->request->get('AuthCode'), + 'trans_id' => (string)$this->request->get('TransId'), + 'auth_code' => (string)$this->request->get('AuthCode'), + 'host_ref_num' => (string)$this->request->get('HostRefNum'), + 'response' => (string)$this->request->get('Response'), + 'order_id' => (string)$this->request->get('oid'), + 'transaction_type' => $this->type, + 'transaction' => $this->order->transaction, + 'transaction_security' => $transaction_security, + 'code' => (string)$this->request->get('ProcReturnCode'), + 'md_status' => $this->request->get('mdStatus'), + 'status' => $status, + 'status_detail' => isset($this->codes[$this->request->get('ProcReturnCode')]) ? (string)$this->request->get('ProcReturnCode') : null, + 'hash' => (string)$this->request->get('HASH'), + 'rand' => (string)$this->request->get('rnd'), + 'hash_params' => (string)$this->request->get('HASHPARAMS'), + 'hash_params_val' => (string)$this->request->get('HASHPARAMSVAL'), + 'masked_number' => (string)$this->request->get('maskedCreditCard'), + 'month' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Month'), + 'year' => (string)$this->request->get('Ecom_Payment_Card_ExpDate_Year'), + 'amount' => (string)$this->request->get('amount'), + 'currency' => (string)$this->request->get('currency'), + 'tx_status' => (string)$this->request->get('txstatus'), + 'eci' => (string)$this->request->get('eci'), + 'cavv' => (string)$this->request->get('cavv'), + 'xid' => (string)$this->request->get('xid'), + 'error_code' => (string)$this->request->get('ErrCode'), + 'error_message' => (string)$this->request->get('ErrMsg'), + 'md_error_message' => (string)$this->request->get('mdErrorMsg'), + 'name' => (string)$this->request->get('firmaadi'), + 'email' => (string)$this->request->get('Email'), + 'campaign_url' => null, + 'extra' => $this->request->get('Extra'), + 'all' => $this->request->request->all(), + ]; + + return $this; + } + + /** + * Get 3d Form Data + * + * @return array + */ + public function get3DFormData() + { + $data = []; + + if ($this->order) { + $this->order->hash = $this->create3DHash(); + + $inputs = [ + 'Version3D' => '2.0', + 'ShopCode' => $this->account->client_id, + 'PurchAmount' => $this->order->amount, + 'Currency' => $this->order->currency, + 'OrderId' => $this->order->id, + 'OkUrl' => $this->order->success_url, + 'FailUrl' => $this->order->fail_url, + 'Rnd' => $this->order->rand, + 'Hash' => $this->order->hash, + 'TxnType' => $this->type, + 'Pan' => $this->card->number, + 'Cvv2' => $this->card->cvv, + 'Expiry' => $this->getCardExpDate(), + 'CardType' => $this->getCardCode(), + 'BonusAmount' => '', + 'Lang' => $this->order->lang + ]; + + if ($this->account->model == '3d_pay') { + $inputs = array_merge($inputs, [ + 'SecureType' => '3DPay', + 'InstallmentCount' => $this->order->installment, + ]); + } + + $data = [ + 'gateway' => $this->gateway, + 'success_url' => $this->order->success_url, + 'fail_url' => $this->order->fail_url, + 'rand' => $this->order->rand, + 'hash' => $this->order->hash, + 'inputs' => $inputs, + ]; + } + + return $data; + } + + /** + * Send contents to WebService + * + * @param $contents + * @return $this + * @throws GuzzleException + */ + public function send($contents) + { + $client = new Client(); + + $response = $client->request('POST', $this->url, [ + 'body' => $contents + ]); + + $this->data = $this->XMLStringToObject($response->getBody()->getContents()); + + return $this; + } + + /** + * Prepare Order + * + * @param object $order + * @param object null $card + * @return mixed + * @throws UnsupportedTransactionTypeException + */ + public function prepare($order, $card = null) + { + $this->type = $this->types['pay']; + if (isset($order->transaction)) { + if (array_key_exists($order->transaction, $this->types)) { + $this->type = $this->types[$order->transaction]; + } else { + throw new UnsupportedTransactionTypeException('Unsupported transaction type!'); + } + } + + $this->order = $order; + $this->card = $card; + + $this->order->installment = $this->order->installment == 0 ? '' : $this->order->installment; + } + + /** + * Make Payment + * + * @param object $card + * @return mixed + * @throws UnsupportedPaymentModelException + * @throws GuzzleException + */ + public function payment($card) + { + $this->card = $card; + + $model = 'regular'; + if (isset($this->account->model) && $this->account->model) { + $model = $this->account->model; + } + + if ($model == 'regular') { + $this->makeRegularPayment(); + } elseif ($model == '3d') { + $this->make3DPayment(); + } elseif ($model == '3d_pay') { + $this->make3DPayPayment(); + } else { + throw new UnsupportedPaymentModelException(); + } + + return $this; + } + + /** + * Refund Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function refund(array $meta) + { + return false; + } + + /** + * Cancel Order + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function cancel(array $meta) + { + return false; + } + + /** + * Order Status + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function status(array $meta) + { + return false; + } + + /** + * Order History + * + * @param array $meta + * @return $this + * @throws GuzzleException + */ + public function history(array $meta) + { + return false; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @return mixed + */ + public function getAccount() + { + return $this->account; + } + + /** + * @return array + */ + public function getCurrencies() + { + return $this->currencies; + } + + /** + * @return mixed + */ + public function getOrder() + { + return $this->order; + } + + /** + * @return mixed + */ + public function getCard() + { + return $this->card; + } + + /** + * @return string|null + */ + public function getCardCode() + { + $card_type = null; + if (isset($this->card->type)) { + if ($this->card->type == 'visa') { + $card_type = '0'; + } elseif ($this->card->type == 'master') { + $card_type = '1'; + }elseif($this->card->type == '1' || $this->card->type == '2'){ + $card_type = $this->card->type; + } + } + return $card_type; + } + + protected function getCardExpDate() + { + $year = (string) substr($this->card->year, 2,2); + $month = (string) substr($this->card->month, 0,2); + + return (string) $month . $year; + } + + +} diff --git a/app/Core/Payment/Pos/VkfPos.php b/app/Core/Payment/Pos/VkfPos.php new file mode 100644 index 0000000..ef5e51d --- /dev/null +++ b/app/Core/Payment/Pos/VkfPos.php @@ -0,0 +1,689 @@ + 'approved', + '01' => 'bank_call', + '02' => 'bank_call', + '05' => 'reject', + '09' => 'try_again', + '12' => 'invalid_transaction', + '28' => 'reject', + '51' => 'insufficient_balance', + '54' => 'expired_card', + '57' => 'does_not_allow_card_holder', + '62' => 'restricted_card', + '77' => 'request_rejected', + '99' => 'general_error', + ]; + + /** + * Transaction Types + * + * @var array + */ + public $types = [ + 'pay' => 'Auth', + 'pre' => 'PreAuth', + 'post' => 'PostAuth', + ]; + + /** + * Currencies + * + * @var array + */ + public $currencies = []; + + /** + * Transaction Type + * + * @var string + */ + public $type; + + /** + * API Account + * + * @var array + */ + protected $account = []; + + /** + * Order Details + * + * @var array + */ + protected $order = []; + + /** + * Credit Card + * + * @var object + */ + protected $card; + + /** + * Request + * + * @var Request + */ + protected $request; + + /** + * Response Raw Data + * + * @var object + */ + protected $data; + + /** + * Processed Response Data + * + * @var mixed + */ + public $response; + + /** + * Configuration + * + * @var array + */ + protected $config = []; + + /** + * Processed Response Data + * + * @var mixed + */ + public $client; + + /** + * EstPos constructor. + * + * @param array $config + * @param mixed $account + * @param array $currencies + */ + public function __construct($config, $account, array $currencies) + { + $client = new Client(); + $this->client = $client; + $this->config = $config; + $this->account = $account; + $this->currencies = $currencies; + + $this->url = isset($this->config['urls'][$this->account->env]) ? + $this->config['urls'][$this->account->env] : + $this->config['urls']['production']; + + $this->gateway = isset($this->config['urls']['gateway'][$this->account->env]) ? + $this->config['urls']['gateway'][$this->account->env] : + $this->config['urls']['gateway']['production']; + + + return $this; + } + + /** + * Create Regular Payment XML + * + * @return string + */ + protected function createRegularPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'CardType' => isset($this->card->type) ? $this->card->type : null, + 'Number' => $this->card->number, + 'Expires' => $this->card->month . '/' . $this->card->year, + 'Cvv2Val' => $this->card->cvv, + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + 'BillTo' => [ + 'Name' => $this->order->name ? $this->order->name : null, + ] + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create Regular Payment Post XML + * + * @return string + */ + protected function createRegularPostXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->types[$this->order->transaction], + 'OrderId' => $this->order->id, + ] + ]; + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Create 3D Payment XML + * @return string + */ + protected function create3DPaymentXML() + { + $nodes = [ + 'CC5Request' => [ + 'Name' => $this->account->username, + 'Password' => $this->account->password, + 'ClientId' => $this->account->client_id, + 'Type' => $this->type, + 'IPAddress' => $this->order->ip, + 'Email' => $this->order->email, + 'OrderId' => $this->order->id, + 'UserId' => isset($this->order->user_id) ? $this->order->user_id : null, + 'Total' => $this->order->amount, + 'Currency' => $this->order->currency, + 'Taksit' => $this->order->installment, + 'Number' => $this->request->get('md'), + 'Expires' => '', + 'Cvv2Val' => '', + 'PayerTxnId' => $this->request->get('xid'), + 'PayerSecurityLevel' => $this->request->get('eci'), + 'PayerAuthenticationCode' => $this->request->get('cavv'), + 'CardholderPresentCode' => '13', + 'Mode' => 'P', + 'GroupId' => '', + 'TransId' => '', + ] + ]; + + if ($this->order->name) { + $nodes['BillTo'] = [ + 'Name' => $this->order->name, + ]; + } + + return $this->createXML($nodes, 'ISO-8859-9'); + } + + /** + * Get ProcReturnCode + * + * @return string|null + */ + protected function getProcReturnCode() + { + return isset($this->data->ProcReturnCode) ? (string)$this->data->ProcReturnCode : null; + } + + /** + * Get Status Detail Text + * + * @return string|null + */ + protected function getStatusDetail() + { + $proc_return_code = $this->getProcReturnCode(); + + return $proc_return_code ? (isset($this->codes[$proc_return_code]) ? (string)$this->codes[$proc_return_code] : null) : null; + } + + /** + * Create 3D Hash + * + * @return string + */ + public function create3DHash() + { + $hash_str = ''; + $hash_str = $this->order->id . $this->order->amount . $this->order->success_url . $this->order->fail_url . $this->type . $this->order->installment . $this->order->rand . $this->account->merchant_pass; + + return base64_encode(pack('H*', sha1($hash_str))); + } + + /** + * Check 3D Hash + * + * @param array $data + * @return bool + */ + public function check3DHash($data) + { + $return = false; + $responseHash = $data['ResponseHash']; + + //MerchantID + MerchantPass + OrderId + AuthCode + ProcReturnCode + 3DStatus + ResponseRnd + UserCode + $generatedHash = $this->account->merchant_id . $this->account->merchant_pass . $data['OrderId'] . $data['AuthCode'] . $data['ProcReturnCode'] . $data['3DStatus'] . $data['ResponseRnd'] . $this->account->user_code; + $generatedHash = base64_encode(pack('H*', sha1($generatedHash))); + + if ($generatedHash == $responseHash) { + $return = true; + } + + return $return; + } + + /** + * Regular Payment + * + * @return $this + * @throws GuzzleException + */ + public function makeRegularPayment() + { + return false; + } + + /** + * Make 3D Payment + * + * @return $this + * @throws GuzzleException + */ + public function make3DPayment() + { + return false; + } + + /** + * Make 3D Pay Payment + * + * @return $this + */ + public function make3DPayPayment() + { + $this->request = Request::createFromGlobals(); + $requestAll = $this->request->request->all(); + $ipAddress = $this->request->getClientIp(); + + /*echo "
";
+        print_r($this->order);
+        die();*/
+
+        $paymentCheck = null;
+        $status = 'declined';
+        $transaction_security = 'MPI fallback';
+        $errorMessage = null;
+
+        try {
+
+            $requestParam = new \SimpleXMLElement('');
+
+            $requestParam->addChild('MerchantId', $this->account->merchant_id);
+            $requestParam->addChild('Password', $this->account->merchant_password);
+            $requestParam->addChild('TerminalNo', $this->account->terminal_no);
+            $requestParam->addChild('TransactionType', 'Sale');
+            $requestParam->addChild('OrderId', $this->order->id);
+            $requestParam->addChild('CurrencyAmount', number_format(($requestAll['PurchAmount'] / 100), 2, '.', ''));
+            $requestParam->addChild('CurrencyCode', $requestAll['PurchCurrency']);
+            $requestParam->addChild('Pan', $requestAll['Pan']);
+            $requestParam->addChild('Expiry', $this->order->creditCardYear . $this->order->creditCardMonth);
+            $requestParam->addChild('Cvv', $this->order->creditCardYearCvv);
+            $requestParam->addChild('ECI', $requestAll['Eci']);
+            $requestParam->addChild('CAVV', $requestAll['Cavv']);
+            $requestParam->addChild('MpiTransactionId', $requestAll['VerifyEnrollmentRequestId']);
+            $requestParam->addChild('ClientIp', $ipAddress);
+            $requestParam->addChild('TransactionDeviceSource', 0);
+
+            if (!empty($requestAll['InstallmentCount'])) {
+                $requestParam->addChild('NumberOfInstallments', $requestAll['InstallmentCount']);
+            }
+
+            $payment = $this->client->request('POST', $this->url, [
+                'form_params' => ['prmstr' => $requestParam->asXML()]
+            ]);
+
+            $paymentCheck = $this->XMLStringToObject($payment->getBody()->getContents());
+
+            if ($paymentCheck->ResultCode == '0000') {
+                $status = 'approved';
+            } else {
+                $errorMessage = $paymentCheck->ResultDetail;
+            }
+
+            if ($paymentCheck->ThreeDSecureType == '2') {
+                $transaction_security = 'Full 3D Secure';
+            } elseif ($paymentCheck->ThreeDSecureType == '3') {
+                $transaction_security = 'Half 3D Secure';
+            }
+
+        } catch (Exception $e) {
+            Log::error($this->account->bank . ' Error!');
+            Log::error($e->getMessage());
+        }
+
+
+
+        $this->response = (object)[
+            'id' => isset($paymentCheck->TransactionId) ? (string)$paymentCheck->TransactionId : null,
+            'trans_id' => isset($paymentCheck->TransactionId) ? (string)$paymentCheck->TransactionId : null,
+            'auth_code' => isset($paymentCheck->AuthCode) ? (string)$paymentCheck->AuthCode : null,
+            'host_ref_num' => isset($paymentCheck->TransactionId) ? (string)$paymentCheck->TransactionId : null,
+            'order_id' => isset($paymentCheck->TransactionId) ? (string)$paymentCheck->TransactionId : null,
+            'status' => $status,
+            'response' => $paymentCheck,
+            'transaction_security' => $transaction_security,
+            'error_code' => isset($paymentCheck->ResultCode) ? (string)$paymentCheck->ResultCode : null,
+            'error_message' => $errorMessage,
+            'all' => $paymentCheck
+        ];
+
+        /*echo "
";
+        print_r($this->response);
+        die();*/
+
+        return $this;
+    }
+
+    /**
+     * Get 3d Form Data
+     *
+     * @return array
+     */
+    public function get3DFormData()
+    {
+        $threeDFormData = [];
+
+        try {
+
+            if ($this->order) {
+
+                $threeDCheckParam = [
+                    'Pan' => $this->card->number,
+                    'ExpiryDate' => $this->getCardExpDate(),
+                    'PurchaseAmount' => number_format(($this->order->amount * 100 / 100), 2, '.', ''),
+                    'Currency' => $this->order->currency,
+                    'BrandName' => null,
+                    'VerifyEnrollmentRequestId' => $this->order->id,
+                    'MerchantId' => $this->account->merchant_id,
+                    'MerchantPassword' => $this->account->merchant_password,
+                    'SuccessUrl' => $this->order->success_url,
+                    'FailUrl' => $this->order->success_url,
+                ];
+
+                $response = $this->client->request('POST', $this->gateway, [
+                    'form_params' => $threeDCheckParam
+                ]);
+
+                $threeDCheck = $this->XMLStringToObject($response->getBody()->getContents());
+
+                if ($threeDCheck->MessageErrorCode != 200) {
+                    throw new Exception($threeDCheck->MessageErrorCode . ' - ' . $threeDCheck->ErrorMessage);
+                }
+
+                $threeDFormData = [
+                    'status' => $threeDCheck->Message->VERes->Status,
+                    'gateway' => $threeDCheck->Message->VERes->ACSUrl,
+                    'success_url' => $this->order->success_url,
+                    'fail_url' => $this->order->success_url,
+                    'inputs' => [
+                        'PaReq' => $threeDCheck->Message->VERes->PaReq,
+                        'TermUrl' => $threeDCheck->Message->VERes->TermUrl,
+                        'MD' => $threeDCheck->Message->VERes->MD
+                    ]
+                ];
+
+                if ($threeDFormData['status'] != 'Y') {
+                    throw new Exception('3D Secure is not supported for this card.');
+                }
+
+            }
+
+        } catch (Exception $e) {
+            Log::error($this->account->bank . ' Error!');
+            Log::error($e->getMessage());
+            $threeDFormData = [];
+        }
+
+        return $threeDFormData;
+    }
+
+    /**
+     * Send contents to WebService
+     *
+     * @param $contents
+     * @return $this
+     * @throws GuzzleException
+     */
+    public function send($contents)
+    {
+        $client = new Client();
+
+        $response = $client->request('POST', $this->url, [
+            'body' => $contents
+        ]);
+
+        $this->data = $this->XMLStringToObject($response->getBody()->getContents());
+
+        return $this;
+    }
+
+    /**
+     * Prepare Order
+     *
+     * @param object $order
+     * @param object null $card
+     * @return mixed
+     * @throws UnsupportedTransactionTypeException
+     */
+    public function prepare($order, $card = null)
+    {
+        $this->type = $this->types['pay'];
+        if (isset($order->transaction)) {
+            if (array_key_exists($order->transaction, $this->types)) {
+                $this->type = $this->types[$order->transaction];
+            } else {
+                throw new UnsupportedTransactionTypeException('Unsupported transaction type!');
+            }
+        }
+
+        $this->order = $order;
+        $this->card = $card;
+
+        $this->order->installment = $this->order->installment == 0 ? 0 : $this->order->installment;
+    }
+
+    /**
+     * Make Payment
+     *
+     * @param object $card
+     * @return mixed
+     * @throws UnsupportedPaymentModelException
+     * @throws GuzzleException
+     */
+    public function payment($card)
+    {
+        $this->card = $card;
+
+        $model = 'regular';
+        if (isset($this->account->model) && $this->account->model) {
+            $model = $this->account->model;
+        }
+
+        if ($model == 'regular') {
+            $this->makeRegularPayment();
+        } elseif ($model == '3d') {
+            $this->make3DPayment();
+        } elseif ($model == '3d_pay') {
+            $this->make3DPayPayment();
+        } else {
+            throw new UnsupportedPaymentModelException();
+        }
+
+        return $this;
+    }
+
+    /**
+     * Refund Order
+     *
+     * @param array $meta
+     * @return $this
+     * @throws GuzzleException
+     */
+    public function refund(array $meta)
+    {
+        return false;
+    }
+
+    /**
+     * Cancel Order
+     *
+     * @param array $meta
+     * @return $this
+     * @throws GuzzleException
+     */
+    public function cancel(array $meta)
+    {
+        return false;
+    }
+
+    /**
+     * Order Status
+     *
+     * @param array $meta
+     * @return $this
+     * @throws GuzzleException
+     */
+    public function status(array $meta)
+    {
+        return false;
+    }
+
+    /**
+     * Order History
+     *
+     * @param array $meta
+     * @return $this
+     * @throws GuzzleException
+     */
+    public function history(array $meta)
+    {
+        return false;
+    }
+
+    /**
+     * @return array
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getAccount()
+    {
+        return $this->account;
+    }
+
+    /**
+     * @return array
+     */
+    public function getCurrencies()
+    {
+        return $this->currencies;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getOrder()
+    {
+        return $this->order;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getCard()
+    {
+        return $this->card;
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getCardCode()
+    {
+        $card_type = null;
+        if (isset($this->card->type)) {
+            if ($this->card->type == 'visa') {
+                $card_type = '0';
+            } elseif ($this->card->type == 'master') {
+                $card_type = '1';
+            } elseif ($this->card->type == '1' || $this->card->type == '2') {
+                $card_type = $this->card->type;
+            }
+        }
+        return $card_type;
+    }
+
+    protected function getCardExpDate()
+    {
+        $year = (string)substr($this->card->year, 2, 2);
+        $month = (string)substr($this->card->month, 0, 2);
+
+        return (string)$year . $month;
+    }
+
+
+}
diff --git a/app/Core/Payment/Pos/config/pos.php b/app/Core/Payment/Pos/config/pos.php
new file mode 100644
index 0000000..0c7916f
--- /dev/null
+++ b/app/Core/Payment/Pos/config/pos.php
@@ -0,0 +1,202 @@
+ [
+        'TRY' => 949,
+        'USD' => 840,
+        'EUR' => 978,
+        'GBP' => 826,
+        'JPY' => 392,
+        'RUB' => 643,
+    ],
+
+    // Banks
+    'banks' => [
+        'akbank' => [
+            'name' => 'AKBANK T.A.S.', ////Test - 5571135571135575, 12, 2026, 000, a
+            'class' => AkPos::class,//EstPos::class,
+            'urls' => [
+                'production' => 'https://api.akbank.com/api/v1/payment/virtualpos/transaction/process',
+                'test' => 'https://apipre.akbank.com/api/v1/payment/virtualpos/transaction/process',
+                'gateway' => [
+                    'production' => 'https://virtualpospaymentgateway.akbank.com/securepay',
+                    'test' => 'https://virtualpospaymentgatewaypre.akbank.com/securepay',
+                ],
+            ]
+        ],
+        'denizbank' => [ //https://test.inter-vpos.com.tr, 3123, InterTest11, InterTest11
+            'name' => 'DENIZ BANK', //Test - 4090700090840057, 11, 2022, 592, 123
+            'class' => VPos::class,
+            'urls' => [
+                'production' => 'https://spos.denizbank.com/mpi/Default.aspx',
+                'test' => 'https://sanaltest.denizbank.com/mpi/Default.aspx',
+                'gateway' => [
+                    'production' => 'https://spos.denizbank.com/mpi/Default.aspx',
+                    'test' => 'https://sanaltest.denizbank.com/mpi/Default.aspx',
+                ],
+            ]
+        ],
+        'ziraat' => [
+            'name' => 'Ziraat Bankası',
+            'class' => EstPosV3::class,//EstPos::class,
+            'urls' => [
+                'production' => 'https://sanalpos2.ziraatbank.com.tr/fim/api',
+                'test' => 'https://entegrasyon.asseco-see.com.tr/fim/api',
+                'gateway' => [
+                    'production' => 'https://sanalpos2.ziraatbank.com.tr/fim/est3dgate',
+                    'test' => 'https://entegrasyon.asseco-see.com.tr/fim/est3Dgate',
+                ],
+            ]
+        ],
+        'finansbank' => [
+            'name' => 'QNB Finansbank',
+            'class' => QPos::class,
+            'urls' => [
+                'production' => 'https://vpos.qnbfinansbank.com/Gateway/Default.aspx',
+                'test' => 'https://vpostest.qnbfinansbank.com/Gateway/Default.aspx',
+                'gateway' => [
+                    'production' => 'https://vpos.qnbfinansbank.com/Gateway/Default.aspx',
+                    'test' => 'https://vpostest.qnbfinansbank.com/Gateway/Default.aspx',
+                ],
+            ]
+        ],
+        'vakifbank' => [
+            'name' => 'Vakıfbank',
+            'class' => VkfPos::class,
+            'urls' => [
+                'production' => 'https://onlineodeme.vakifbank.com.tr:4443/VposService/v3/Vposreq.aspx',
+                'test' => 'https://onlineodemetest.vakifbank.com.tr:4443/VposService/v3/Vposreq.aspx',
+                'gateway' => [
+                    'production' => 'https://3dsecure.vakifbank.com.tr/MPIAPI/MPI_Enrollment.aspx',
+                    'test' => 'https://3dsecuretest.vakifbank.com.tr/MPIAPI/MPI_Enrollment.aspx',
+                ],
+            ]
+        ],
+        'turkiyefinans' => [ //TODO: Check this infos
+            'name' => 'Türkiye Finans',
+            'class' => EstPosV3::class,//EstPos::class,
+            'urls' => [
+                'production' => 'https://www.fbwebpos.com/fim/api',
+                'test' => 'https://entegrasyon.asseco-see.com.tr/fim/api',
+                'gateway' => [
+                    'production' => 'https://www.fbwebpos.com/fim/est3dgate',
+                    'test' => 'https://entegrasyon.asseco-see.com.tr/fim/est3Dgate',
+                ],
+            ]
+        ],
+        'halkbank' => [
+            'name' => 'Halkbank',
+            'class' => EstPosV3::class,
+            'urls' => [
+                'production' => 'https://sanalpos.halkbank.com.tr/fim/api',
+                'test' => 'https://entegrasyon.asseco-see.com.tr/fim/api',
+                'gateway' => [
+                    'production' => 'https://sanalpos.halkbank.com.tr/fim/est3dgate',
+                    'test' => 'https://entegrasyon.asseco-see.com.tr/fim/est3Dgate',
+                ],
+            ]
+        ],
+        'teb' => [
+            'name' => 'TEB',
+            'class' => EstPosV3::class,//EstPos::class,
+            'urls' => [
+                'production' => 'https://sanalpos.teb.com.tr/fim/api',
+                'test' => 'https://entegrasyon.asseco-see.com.tr/fim/api',
+                'gateway' => [
+                    'production' => 'https://sanalpos.teb.com.tr/fim/est3Dgate',
+                    'test' => 'https://entegrasyon.asseco-see.com.tr/fim/est3Dgate',
+                ],
+            ]
+        ],
+        'ingbank' => [
+            'name' => 'ING Bank',
+            'class' => EstPosV3::class,//EstPos::class,
+            'urls' => [
+                'production' => 'https://sanalpos.ingbank.com.tr/fim/api',
+                'test' => 'https://entegrasyon.asseco-see.com.tr/fim/api',
+                'gateway' => [
+                    'production' => 'https://sanalpos.ingbank.com.tr/fim/est3Dgate',
+                    'test' => 'https://entegrasyon.asseco-see.com.tr/fim/est3Dgate',
+                ],
+            ]
+        ],
+        'isbank' => [
+            'name' => 'İşbank',
+            'class' => EstPosV3::class,//EstPos::class,
+            'urls' => [
+                'production' => 'https://sanalpos.isbank.com.tr/fim/api',
+                'test' => 'https://entegrasyon.asseco-see.com.tr/fim/api',
+                'gateway' => [
+                    'production' => 'https://sanalpos.isbank.com.tr/fim/est3Dgate',
+                    'test' => 'https://entegrasyon.asseco-see.com.tr/fim/est3Dgate',
+                ],
+            ]
+        ],
+        'isbank-payflex' => [
+            'name' => 'İşbank - PayFlex',
+            'class' => Mews\Pos\PayFlex::class,
+            'urls' => [
+                'production' => 'https://trx.payflex.com.tr/VposWeb/v3/Vposreq.aspx',
+                'test' => 'https://sanalpos.innova.com.tr/ISBANK_v4/VposWeb/v3/Vposreq.aspx',
+                'gateway' => [
+                    'production' => 'https://mpi.vpos.isbank.com.tr/MPIEnrollment.aspx',
+                    'test' => 'https://sanalpos.innova.com.tr/ISBANK/MpiWeb/Enrollment.aspx',
+                ],
+            ]
+        ],
+        'yapikredi' => [
+            'name' => 'Yapıkredi',
+            'class' => PosNet::class,
+            'urls' => [
+                'production' => 'https://posnet.yapikredi.com.tr/PosnetWebService/XML',
+                'test' => 'https://setmpos.ykb.com/PosnetWebService/XML',
+                'gateway' => [
+                    'production' => 'https://posnet.yapikredi.com.tr/3DSWebService/YKBPaymentService',
+                    'test' => 'https://setmpos.ykb.com/3DSWebService/YKBPaymentService',
+                ],
+            ],
+            'order' => [
+                'id_total_length' => 24,
+                'id_length' => 20,
+                'id_3d_prefix' => 'TDSC',
+                'id_3d_pay_prefix' => '', //?
+                'id_regular_prefix' => '' //?
+            ]
+        ],
+        'garanti' => [
+            'name' => 'Garanti',
+            'class' => GarantiPos::class,
+            'urls' => [
+                'production' => 'https://sanalposprov.garanti.com.tr/VPServlet',
+                'test' => 'https://sanalposprovtest.garanti.com.tr/VPServlet',
+                'gateway' => [
+                    'production' => 'https://sanalposprov.garanti.com.tr/servlet/gt3dengine',
+                    'test' => 'https://sanalposprovtest.garanti.com.tr/servlet/gt3dengine',
+                ],
+            ]
+        ]
+    ],
+
+];
+//STRIPE
+//4000000000003063 3D required
+//4000000000003089 3D recommended
+//4000000000003055 3d optional
+//378282246310005  3d not_supported direct pay
+
+//Vakıfbank
+//4938460158754205 2024/11 715 123456
+//4119790155203496 2024/04 579 123456
+//4119790166544284 2024/04 961 123456
+//6501700161161969 2024/11 390 123456
diff --git a/app/Core/Payment/Pos/ex/.gitignore b/app/Core/Payment/Pos/ex/.gitignore
new file mode 100644
index 0000000..8a09a34
--- /dev/null
+++ b/app/Core/Payment/Pos/ex/.gitignore
@@ -0,0 +1,4 @@
+.DS_Store
+composer.lock
+/vendor
+/.idea
diff --git a/app/Core/Payment/Pos/ex/LICENCE.md b/app/Core/Payment/Pos/ex/LICENCE.md
new file mode 100644
index 0000000..9f5c8fa
--- /dev/null
+++ b/app/Core/Payment/Pos/ex/LICENCE.md
@@ -0,0 +1,3 @@
+© 2018 Muharrem ERİN (me@mewebstudio.com)
+
+http://www.opensource.org/licenses/mit-license.php The MIT License
diff --git a/app/Core/Payment/Pos/ex/LICENSE.txt b/app/Core/Payment/Pos/ex/LICENSE.txt
new file mode 100644
index 0000000..05a7c57
--- /dev/null
+++ b/app/Core/Payment/Pos/ex/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 MeWebStudio - Muharrem ERİN
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/app/Core/Payment/Pos/ex/README.md b/app/Core/Payment/Pos/ex/README.md
new file mode 100644
index 0000000..65c6268
--- /dev/null
+++ b/app/Core/Payment/Pos/ex/README.md
@@ -0,0 +1,190 @@
+# Türk bankaları için sanal pos paketi (PHP)
+
+Bu paket ile amaçlanan; ortak bir arayüz sınıfı ile, tüm Türk banka sanal pos sistemlerinin kullanılabilmesidir.
+EST altyapısı tam olarak test edilmiş ve kullanıma hazırdır.
+Garanti Ödeme sistemi çalışmaktadır, fakat 3D ödeme kısmının üretim ortamında test edilmesi gerekiyor.
+YapıKredi Posnet sistemi çalışmaktadır, fakat 3D ödeme kısmının üretim ortamında test edilmesi gerekiyor.
+
+> EST altyapısında olan Akbank ve Ziraat bankası test edilmiştir.
+
+### Özellikler
+  - Standart E-Commerce modeliyle ödeme (model => regular)
+  - 3D modeliyle ödeme (model => 3d)
+  - 3D Pay modeliyle ödeme (model => 3d_pay)
+  - Sipariş/Ödeme sorgulama (query)
+  - Sipariş/Ödeme geçmişi sorgulama (history)
+  - Sipariş/Para iadesi yapma (refund)
+  - Sipariş iptal etme (cancel)
+
+### Minimum Gereksinimler
+  - PHP >= 7.1.3
+  - ext-dom
+  - ext-json
+  - ext-openssl
+  - ext-SimpleXML
+
+### Kurulum
+Test sunucunuz üzerinde;
+```sh
+$ mkdir pos-test && cd pos-test
+$ composer require mews/pos
+```
+
+**config.php (Ayar dosyası)**
+```php
+getClientIp();
+
+// API kullanıcı bilgileri
+$account = [
+    'bank'          => 'akbank',
+    'model'         => 'regular',
+    'client_id'     => 'XXXXXXXX',
+    'username'      => 'XXXXXXXX',
+    'password'      => 'XXXXXXXX',
+    'env'           => 'test', // test veya production. test ise; API Test Url, production ise; API Production URL kullanılır.
+];
+
+// API kullanıcı hesabı ile paket bir değişkene aktarılıyor
+try {
+    $pos = new \Mews\Pos\Pos($account);
+} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) {
+    var_dump($e->getCode(), $e->getMessage());
+    exit();
+} catch (\Mews\Pos\Exceptions\BankClassNullException $e) {
+    var_dump($e->getCode(), $e->getMessage());
+    exit();
+}
+```
+
+**test.php (Test Dosyası)**
+```php
+ 'BENZERSIZ-SIPERIS-ID',
+    'name'          => 'John Doe', // zorunlu değil
+    'email'         => 'mail@customer.com', // zorunlu değil
+    'user_id'       => '12', // zorunlu değil
+    'amount'        => (double) 20, // Sipariş tutarı
+    'installment'   => '0',
+    'currency'      => 'TRY',
+    'ip'            => $ip,
+    'transaction'   => 'pay', // pay => Auth, pre PreAuth (Direkt satış için pay, ön provizyon için pre)
+];
+
+// Kredi kartı bilgieri
+$card = [
+    'number'        => 'XXXXXXXXXXXXXXXX', // Kredi kartı numarası
+    'month'         => 'XX', // SKT ay
+    'year'          => 'XX', // SKT yıl, son iki hane
+    'cvv'           => 'XXX', // Güvenlik kodu, son üç hane
+];
+
+// API kullanıcısı ile oluşturulan $pos değişkenine prepare metoduyla sipariş bilgileri gönderiliyor
+$pos->prepare($order);
+
+// Ödeme tamamlanıyor
+$payment = $pos->payment($card);
+
+// Ödeme başarılı mı?
+$payment->isSuccess();
+//veya
+$pos->isSuccess();
+
+// Ödeme başarısız mı?
+$payment->isError();
+//veya
+$pos->isError();
+
+// Sonuç çıktısı
+var_dump($payment->response);
+
+````
+
+### Farklı Banka Sanal Poslarını Eklemek
+Kendi projenizin dizinindeyken
+```sh
+$ cp ./vendor/mews/pos/config/pos.php ./pos_ayarlar.php
+```
+ya da;
+
+Projenizde bir ayar dosyası oluşturup (pos_ayarlar.php gibi), paket içerisinde `./config/pos.php` dosyasının içeriğini buraya kopyalayın.
+
+```php
+ [
+        'TRY'       => 949,
+        'USD'       => 840,
+        'EUR'       => 978,
+        'GBP'       => 826,
+        'JPY'       => 392,
+        'RUB'       => 643,
+    ],
+
+    // Banka sanal pos tanımlamaları
+    'banks'         => [
+        'akbank'    => [
+            'name'  => 'AKBANK T.A.S.',
+            'class' => \Mews\Pos\EstPos::class,
+            'urls'  => [
+                'production'    => 'https://www.sanalakpos.com/fim/api',
+                'test'          => 'https://entegrasyon.asseco-see.com.tr/fim/api',
+                'gateway'       => [
+                    'production'    => 'https://www.sanalakpos.com/fim/est3Dgate',
+                    'test'          => 'https://entegrasyon.asseco-see.com.tr/fim/est3Dgate',
+                ],
+            ]
+        ],
+
+        // Yeni eklenen banka
+        'isbank'    => [
+            'name'  => 'İŞ BANKASI .A.S.',
+            'class' => \Mews\Pos\EstPos::class, // Altyapı sınıfı
+            'urls'  => [
+                'production'    => 'xxxx', // API Url
+                'test'          => 'xxxx', // API Test Url
+                'gateway'       => [
+                    'production'    => 'xxxx', // 3d Kapı Url
+                    'test'          => 'xxxx', // 3d Test Kapı Url
+                ],
+            ]
+        ],
+    ]
+];
+
+```
+
+Bundan sonra nesnemizi, yeni ayarlarımıza göre oluşturup kullanmamız gerekir. Örnek:
+```php
+$yeni_ayarlar = require './pos_ayarlar.php';
+$pos = new \Mews\Pos\Pos($account, $yeni_ayarlar);
+```
+
+### Örnek Kodlar
+`./pos/examples` dizini içerisinde.
+
+### Yol Haritası
+  - Dökümantasyon hazırlanacak
+  - UnitTest yazılacak -> Bu hiçbir zaman olmayabilir, birisi el atarsa sevinirim :)
+
+> Değerli yorum, öneri ve katkılarınızı bekliyorum.
+
+License
+----
+
+MIT
diff --git a/app/Core/Payment/Pos/ex/composer.json b/app/Core/Payment/Pos/ex/composer.json
new file mode 100644
index 0000000..70584f0
--- /dev/null
+++ b/app/Core/Payment/Pos/ex/composer.json
@@ -0,0 +1,31 @@
+{
+    "name": "mews/pos",
+    "description": "Türk bankaları için sanal pos kütüphanesi",
+	"keywords": ["pos", "sanal pos", "est", "est pos", "akbank"],
+	"homepage": "https://github.com/mewebstudio/pos",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Muharrem ERİN",
+            "email": "me@mewebstudio.com"
+        }
+    ],
+    "require": {
+        "php": "^7.1.3",
+        "ext-dom": "*",
+        "ext-json": "*",
+        "ext-openssl": "*",
+        "ext-SimpleXML": "*",
+        "guzzlehttp/guzzle": "^6.3.3",
+        "symfony/http-foundation": "^5.0",
+        "symfony/serializer": "^5.0"
+    },
+	"autoload": {
+		"psr-4": {
+			"Mews\\Pos\\": "src/"
+		}
+	},
+    "require-dev": {
+        "phpunit/phpunit": "^9.0"
+    }
+}
diff --git a/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/_config.php b/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/_config.php
new file mode 100644
index 0000000..efdf9c5
--- /dev/null
+++ b/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/_config.php
@@ -0,0 +1,30 @@
+getClientIp();
+
+$account = [
+    'bank'          => 'akbank',
+    'model'         => '3d_pay',
+    'client_id'     => 'XXXXXXX',
+    'store_key'     => 'XXXXXXX',
+    'env'           => 'test',
+];
+
+try {
+    $pos = new \Mews\Pos\Pos($account);
+} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) {
+    var_dump($e->getCode(), $e->getMessage());
+} catch (\Mews\Pos\Exceptions\BankClassNullException $e) {
+    var_dump($e->getCode(), $e->getMessage());
+}
+
+$template_title = '3D Pay Model Payment';
diff --git a/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/form.php b/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/form.php
new file mode 100644
index 0000000..7765b93
--- /dev/null
+++ b/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/form.php
@@ -0,0 +1,64 @@
+getMethod() !== 'POST') {
+    echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url);
+    exit();
+}
+
+$order_id = date('Ymd') . strtoupper(substr(uniqid(sha1(time())),0,4));
+
+$amount = (double) 320;
+$instalment = '0';
+
+$success_url = $base_url . 'response.php';
+$fail_url = $base_url . 'response.php';
+
+$rand = microtime();
+
+$order = [
+    'id'                => $order_id,
+    'email'             => 'mail@customer.com', // optional
+    'name'              => 'John Doe', // optional
+    'amount'            => $amount,
+    'installment'       => $instalment,
+    'currency'          => 'TRY',
+    'ip'                => $ip,
+    'success_url'       => $success_url,
+    'fail_url'          => $fail_url,
+    'transaction'       => 'pay', // pay => Auth, pre PreAuth
+    'lang'              => 'tr',
+    'rand'              => $rand,
+];
+
+$_SESSION['order'] = $order;
+
+$card = [
+    'name'      => $request->get('name'),
+    'type'      => $request->get('type'),
+    'number'    => $request->get('number'),
+    'month'     => $request->get('month'),
+    'year'      => $request->get('year'),
+    'cvv'       => $request->get('cvv'),
+];
+
+$pos->prepare($order, $card);
+
+$form_data = $pos->get3dFormData();
+?>
+
+
+ $value): ?> + + +
Redirecting...
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/index.php b/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/index.php new file mode 100644 index 0000000..f2b550f --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/index.php @@ -0,0 +1,57 @@ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/response.php b/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/response.php new file mode 100644 index 0000000..6da7c2e --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/3d-pay/response.php @@ -0,0 +1,108 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order = $_SESSION['order']; + +$pos->prepare($order); +$payment = $pos->payment(); +$response = $payment->response; + +$dump = get_object_vars($response); +?> + +
+

+ status == 'approved' ? 'Payment is successful!' : 'Payment is not successful'; ?> +

+
+
+
Response:
+
response; ?>
+
+
+
+
Status:
+
status; ?>
+
+
+
+
Transaction:
+
transaction; ?>
+
+
+
+
Transaction Type:
+
transaction_type; ?>
+
+
+
+
Transaction Security:
+
transaction_security; ?>
+
+
+
+
Hash:
+
hash; ?>
+
+
+
+
Order ID:
+
order_id ? $response->order_id : '-'; ?>
+
+
+
+
AuthCode:
+
auth_code ? $response->auth_code : '-'; ?>
+
+
+
+
HostRefNum:
+
host_ref_num ? $response->host_ref_num : '-'; ?>
+
+
+
+
ProcReturnCode:
+
code ? $response->code : '-'; ?>
+
+
+
+
mdStatus:
+
md_status ? $response->md_status : '-'; ?>
+
+
+
+
Error Code:
+
error_code ? $response->error_code : '-'; ?>
+
+
+
+
Error Message:
+
error_message ? $response->error_message : '-'; ?>
+
+
+
+
Md Error Message:
+
md_error_message ? $response->md_error_message : '-'; ?>
+
+
+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/3d/_config.php b/app/Core/Payment/Pos/ex/examples/akbank/3d/_config.php new file mode 100644 index 0000000..87bf932 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/3d/_config.php @@ -0,0 +1,32 @@ +getClientIp(); + +$account = [ + 'bank' => 'akbank', + 'model' => '3d', + 'client_id' => 'XXXXXXX', + 'username' => 'XXXXXXX', + 'password' => 'XXXXXXX', + 'store_key' => 'XXXXXXX', + 'env' => 'test', +]; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$template_title = '3D Model Payment'; diff --git a/app/Core/Payment/Pos/ex/examples/akbank/3d/form.php b/app/Core/Payment/Pos/ex/examples/akbank/3d/form.php new file mode 100644 index 0000000..38f4e00 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/3d/form.php @@ -0,0 +1,64 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order_id = date('Ymd') . strtoupper(substr(uniqid(sha1(time())),0,4)); + +$amount = (double) 320; +$instalment = '0'; + +$success_url = $base_url . 'response.php'; +$fail_url = $base_url . 'response.php'; + +$rand = microtime(); + +$order = [ + 'id' => $order_id, + 'email' => 'mail@customer.com', // optional + 'name' => 'John Doe', // optional + 'amount' => $amount, + 'installment' => $instalment, + 'currency' => 'TRY', + 'ip' => $ip, + 'success_url' => $success_url, + 'fail_url' => $fail_url, + 'transaction' => 'pay', // pay => Auth, pre PreAuth, + 'lang' => 'tr', + 'rand' => $rand, +]; + +$_SESSION['order'] = $order; + +$card = [ + 'name' => $request->get('name'), + 'type' => $request->get('type'), + 'number' => $request->get('number'), + 'month' => $request->get('month'), + 'year' => $request->get('year'), + 'cvv' => $request->get('cvv'), +]; + +$pos->prepare($order, $card); + +$form_data = $pos->get3dFormData(); +?> + +
+ $value): ?> + + +
Redirecting...
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/3d/index.php b/app/Core/Payment/Pos/ex/examples/akbank/3d/index.php new file mode 100644 index 0000000..f2b550f --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/3d/index.php @@ -0,0 +1,57 @@ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/3d/response.php b/app/Core/Payment/Pos/ex/examples/akbank/3d/response.php new file mode 100644 index 0000000..04b70d9 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/3d/response.php @@ -0,0 +1,108 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order = $_SESSION['order']; + +$pos->prepare($order); +$payment = $pos->payment(); +$response = $payment->response; + +$dump = get_object_vars($response); +?> + +
+

+ status == 'approved' ? 'Payment is successful!' : 'Payment is not successful'; ?> +

+
+
+
Response:
+
response ? $response->response : '-'; ?>
+
+
+
+
Status:
+
status; ?>
+
+
+
+
Transaction:
+
transaction; ?>
+
+
+
+
Transaction Type:
+
transaction_type; ?>
+
+
+
+
Transaction Security:
+
transaction_security; ?>
+
+
+
+
Hash:
+
hash; ?>
+
+
+
+
Order ID:
+
order_id ? $response->order_id : '-'; ?>
+
+
+
+
AuthCode:
+
auth_code ? $response->auth_code : '-'; ?>
+
+
+
+
HostRefNum:
+
host_ref_num ? $response->host_ref_num : '-'; ?>
+
+
+
+
ProcReturnCode:
+
code ? $response->code : '-'; ?>
+
+
+
+
mdStatus:
+
md_status ? $response->md_status : '-'; ?>
+
+
+
+
Error Code:
+
error_code ? $response->error_code : '-'; ?>
+
+
+
+
Error Message:
+
error_message ? $response->error_message : '-'; ?>
+
+
+
+
Md Error Message:
+
md_error_message ? $response->md_error_message : '-'; ?>
+
+
+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/regular/_config.php b/app/Core/Payment/Pos/ex/examples/akbank/regular/_config.php new file mode 100644 index 0000000..e0844fe --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/regular/_config.php @@ -0,0 +1,31 @@ +getClientIp(); + +$account = [ + 'bank' => 'akbank', + 'model' => 'regular', + 'client_id' => 'XXXXXXX', + 'username' => 'XXXXXXX', + 'password' => 'XXXXXXX', + 'env' => 'test', +]; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$gateway = $base_url . 'response.php'; + +$template_title = 'Regular Payment'; diff --git a/app/Core/Payment/Pos/ex/examples/akbank/regular/cancel.php b/app/Core/Payment/Pos/ex/examples/akbank/regular/cancel.php new file mode 100644 index 0000000..37bd6ed --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/regular/cancel.php @@ -0,0 +1,34 @@ +bank->cancel([ + 'order_id' => '20181029A3C1', +]); + +$response = $cancel->response; +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Cancel Order is successful!' : 'Cancel Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/regular/direct.php b/app/Core/Payment/Pos/ex/examples/akbank/regular/direct.php new file mode 100644 index 0000000..21f8531 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/regular/direct.php @@ -0,0 +1,35 @@ + $order_id, + 'name' => 'John Doe', // optional + 'email' => 'mail@customer.com', // optional + 'user_id' => '12', // optional + 'amount' => $amount, + 'installment' => '0', + 'currency' => 'TRY', + 'ip' => $ip, + 'transaction' => 'pay', // pay => Auth, pre PreAuth +]; + +$card = [ + 'number' => '4355084355084358', + 'month' => '12', + 'year' => '18', + 'cvv' => '000', +]; + +try { + $pos->prepare($order); +} catch (\Mews\Pos\Exceptions\UnsupportedTransactionTypeException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$payment = $pos->payment($card); + +var_dump($payment->response); diff --git a/app/Core/Payment/Pos/ex/examples/akbank/regular/history.php b/app/Core/Payment/Pos/ex/examples/akbank/regular/history.php new file mode 100644 index 0000000..9d1de9c --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/regular/history.php @@ -0,0 +1,34 @@ +bank->history([ + 'order_id' => '201810297189', +]); + +$response = $query->response; +$dump = get_object_vars($response); +?> + +
+

+ proc_return_code == '00' ? 'History Order is successful!' : 'History Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/regular/index.php b/app/Core/Payment/Pos/ex/examples/akbank/regular/index.php new file mode 100644 index 0000000..92e7bb9 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/regular/index.php @@ -0,0 +1,48 @@ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/regular/post.php b/app/Core/Payment/Pos/ex/examples/akbank/regular/post.php new file mode 100644 index 0000000..db7e7db --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/regular/post.php @@ -0,0 +1,66 @@ +getClientIp(); + +$account = [ + 'bank' => 'akbank', + 'model' => 'regular', + 'client_id' => '100100000', + 'username' => 'mewsapi', + 'password' => 'ME12345.', + 'env' => 'test', +]; + +$template_title = 'Post Auth Order'; + +require '../../template/_header.php'; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$order = [ + 'id' => '201810297189', + 'transaction' => 'post', +]; + +try { + $pos->prepare($order); +} catch (\Mews\Pos\Exceptions\UnsupportedTransactionTypeException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$payment = $pos->payment(); + +$response = $payment->response; +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() == '00' ? 'Post Auth Order is successful!' : 'Post Auth Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/regular/refund.php b/app/Core/Payment/Pos/ex/examples/akbank/regular/refund.php new file mode 100644 index 0000000..a86a475 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/regular/refund.php @@ -0,0 +1,35 @@ +bank->refund([ + 'order_id' => '201810297E8B', + 'amount' => '100', +]); + +$response = $refund->response; +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Refund Order is successful!' : 'Refund Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/regular/response.php b/app/Core/Payment/Pos/ex/examples/akbank/regular/response.php new file mode 100644 index 0000000..1f0b3c2 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/regular/response.php @@ -0,0 +1,114 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order_id = date('Ymd') . strtoupper(substr(uniqid(sha1(time())),0,4)); +$amount = (double) 100; + +$order = [ + 'id' => $order_id, + 'name' => 'John Doe', // optional + 'email' => 'mail@customer.com', // optional + 'user_id' => '12', // optional + 'amount' => $amount, + 'installment' => '0', + 'currency' => 'TRY', + 'ip' => $ip, + 'transaction' => 'pre', // pay => Auth, pre PreAuth +]; + +$pos->prepare($order); + +$card = [ + 'number' => $request->get('number'), + 'month' => $request->get('month'), + 'year' => $request->get('year'), + 'cvv' => $request->get('cvv'), +]; + +$payment = $pos->payment($card); +$response = $payment->response; + +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Payment is successful!' : 'Payment is not successful!'; ?> +

+
+
+
Response:
+
response; ?>
+
+
+
+
Status:
+
status; ?>
+
+
+
+
Transaction:
+
transaction; ?>
+
+
+
+
Transaction Type:
+
transaction_type; ?>
+
+
+
+
Order ID:
+
order_id ? $response->order_id : '-'; ?>
+
+
+
+
Group ID:
+
group_id ? $response->group_id : '-'; ?>
+
+
+
+
AuthCode:
+
auth_code ? $response->auth_code : '-'; ?>
+
+
+
+
HostRefNum:
+
host_ref_num ? $response->host_ref_num : '-'; ?>
+
+
+
+
ProcReturnCode:
+
code; ?>
+
+
+
+
Error Code:
+
error_code ? $response->error_code : '-'; ?>
+
+
+
+
Error Message:
+
error_message ? $response->error_message : '-'; ?>
+
+
+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/akbank/regular/status.php b/app/Core/Payment/Pos/ex/examples/akbank/regular/status.php new file mode 100644 index 0000000..229612b --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/akbank/regular/status.php @@ -0,0 +1,34 @@ +bank->status([ + 'order_id' => '201810297189' +]); + +$response = $query->response; +$dump = get_object_vars($response); +?> + +
+

+ proc_return_code == '00' ? 'Query Order is successful!' : 'Query Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/_config.php b/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/_config.php new file mode 100644 index 0000000..d2ac7de --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/_config.php @@ -0,0 +1,33 @@ +getClientIp(); + +$account = [ + 'bank' => 'garanti', + 'model' => '3d_pay', + 'client_id' => '7000679', + 'terminal_id' => '30691298', + 'username' => 'PROVAUT', + 'password' => '123qweASD/', + 'store_key' => '12345678', + 'env' => 'test', +]; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$template_title = '3D Pay Model Payment'; diff --git a/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/form.php b/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/form.php new file mode 100644 index 0000000..9b41fa8 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/form.php @@ -0,0 +1,68 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order_id = date('Ymd') . strtoupper(substr(uniqid(sha1(time())),0,4)); + +$amount = (double) 1; +$instalment = '0'; + +$success_url = $base_url . 'response.php'; +$fail_url = $base_url . 'response.php'; + +$transaction = 'pay'; // pay => Auth, pre PreAuth +$transaction_type = $pos->bank->types[$transaction]; + +$rand = microtime(); + +$order = [ + 'id' => $order_id, + 'email' => 'mail@customer.com', // optional + 'name' => 'John Doe', // optional + 'amount' => $amount, + 'installment' => $instalment, + 'currency' => 'TRY', + 'ip' => $ip, + 'success_url' => $success_url, + 'fail_url' => $fail_url, + 'transaction' => $transaction, + 'lang' => 'tr', + 'rand' => $rand, +]; + +$_SESSION['order'] = $order; + +$card = [ + 'name' => $request->get('name'), + 'type' => $request->get('type'), + 'number' => $request->get('number'), + 'month' => $request->get('month'), + 'year' => $request->get('year'), + 'cvv' => $request->get('cvv'), +]; + +$pos->prepare($order, $card); + +$form_data = $pos->get3dFormData(); +?> + +
+ $value): ?> + + +
Redirecting...
+
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/index.php b/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/index.php new file mode 100644 index 0000000..f2b550f --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/index.php @@ -0,0 +1,57 @@ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/response.php b/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/response.php new file mode 100644 index 0000000..d63ff94 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/3d-pay/response.php @@ -0,0 +1,109 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order = $_SESSION['order']; + +$pos->prepare($order); +$payment = $pos->payment(); +$response = $payment->response; + +$dump = get_object_vars($response); +?> + +
+
+

+ isSuccess() ? 'Payment is successful!' : 'Payment is not successful'; ?> +

+
+
+
Response:
+
response ? $response->response : '-'; ?>
+
+
+
+
Status:
+
status; ?>
+
+
+
+
Transaction:
+
transaction; ?>
+
+
+
+
Transaction Type:
+
transaction_type; ?>
+
+
+
+
Transaction Security:
+
transaction_security; ?>
+
+
+
+
Hash:
+
hash; ?>
+
+
+
+
Order ID:
+
order_id ? $response->order_id : '-'; ?>
+
+
+
+
AuthCode:
+
auth_code ? $response->auth_code : '-'; ?>
+
+
+
+
HostRefNum:
+
host_ref_num ? $response->host_ref_num : '-'; ?>
+
+
+
+
ProcReturnCode:
+
code ? $response->code : '-'; ?>
+
+
+
+
mdStatus:
+
md_status ? $response->md_status : '-'; ?>
+
+
+
+
Error Code:
+
error_code ? $response->error_code : '-'; ?>
+
+
+
+
Error Message:
+
error_message ? $response->error_message : '-'; ?>
+
+
+
+
Md Error Message:
+
md_error_message ? $response->md_error_message : '-'; ?>
+
+
+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/3d/_config.php b/app/Core/Payment/Pos/ex/examples/garanti/3d/_config.php new file mode 100644 index 0000000..7d73fc1 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/3d/_config.php @@ -0,0 +1,33 @@ +getClientIp(); + +$account = [ + 'bank' => 'garanti', + 'model' => '3d', + 'client_id' => '7000679', + 'terminal_id' => '30691298', + 'username' => 'PROVAUT', + 'password' => '123qweASD/', + 'store_key' => '12345678', + 'env' => 'test', +]; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$template_title = '3D Model Payment'; diff --git a/app/Core/Payment/Pos/ex/examples/garanti/3d/form.php b/app/Core/Payment/Pos/ex/examples/garanti/3d/form.php new file mode 100644 index 0000000..dd3c640 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/3d/form.php @@ -0,0 +1,64 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order_id = date('Ymd') . strtoupper(substr(uniqid(sha1(time())),0,4)); + +$amount = (double) 1; +$instalment = '0'; + +$success_url = $base_url . 'response.php'; +$fail_url = $base_url . 'response.php'; + +$rand = microtime(); + +$order = [ + 'id' => $order_id, + 'email' => 'mail@customer.com', // optional + 'name' => 'John Doe', // optional + 'amount' => $amount, + 'installment' => $instalment, + 'currency' => 'TRY', + 'ip' => $ip, + 'success_url' => $success_url, + 'fail_url' => $fail_url, + 'transaction' => 'pay', // pay => Auth, pre PreAuth, + 'lang' => 'tr', + 'rand' => $rand, +]; + +$_SESSION['order'] = $order; + +$card = [ + 'name' => $request->get('name'), + 'type' => $request->get('type'), + 'number' => $request->get('number'), + 'month' => $request->get('month'), + 'year' => $request->get('year'), + 'cvv' => $request->get('cvv'), +]; + +$pos->prepare($order, $card); + +$form_data = $pos->get3dFormData(); +?> + +
+ $value): ?> + + +
Redirecting...
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/3d/index.php b/app/Core/Payment/Pos/ex/examples/garanti/3d/index.php new file mode 100644 index 0000000..f2b550f --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/3d/index.php @@ -0,0 +1,57 @@ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/3d/response.php b/app/Core/Payment/Pos/ex/examples/garanti/3d/response.php new file mode 100644 index 0000000..e54c065 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/3d/response.php @@ -0,0 +1,108 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order = $_SESSION['order']; + +$pos->prepare($order); +$payment = $pos->payment(); +$response = $payment->response; + +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Payment is successful!' : 'Payment is not successful'; ?> +

+
+
+
Response:
+
response ? $response->response : '-'; ?>
+
+
+
+
Status:
+
status; ?>
+
+
+
+
Transaction:
+
transaction; ?>
+
+
+
+
Transaction Type:
+
transaction_type; ?>
+
+
+
+
Transaction Security:
+
transaction_security; ?>
+
+
+
+
Hash:
+
hash; ?>
+
+
+
+
Order ID:
+
order_id ? $response->order_id : '-'; ?>
+
+
+
+
AuthCode:
+
auth_code ? $response->auth_code : '-'; ?>
+
+
+
+
HostRefNum:
+
host_ref_num ? $response->host_ref_num : '-'; ?>
+
+
+
+
ProcReturnCode:
+
code ? $response->code : '-'; ?>
+
+
+
+
mdStatus:
+
md_status ? $response->md_status : '-'; ?>
+
+
+
+
Error Code:
+
error_code ? $response->error_code : '-'; ?>
+
+
+
+
Error Message:
+
error_message ? $response->error_message : '-'; ?>
+
+
+
+
Md Error Message:
+
md_error_message ? $response->md_error_message : '-'; ?>
+
+
+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/regular/_config.php b/app/Core/Payment/Pos/ex/examples/garanti/regular/_config.php new file mode 100644 index 0000000..c4664f7 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/regular/_config.php @@ -0,0 +1,34 @@ +getClientIp(); + +$account = [ + 'bank' => 'garanti', + 'model' => 'regular', + 'client_id' => '7000679', + 'terminal_id' => '30691298', + 'username' => 'PROVAUT', + 'password' => '123qweASD/', + 'refund_username' => 'PROVRFN', + 'refund_password' => '123qweASD/', + 'env' => 'test', +]; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$gateway = $base_url . 'response.php'; + +$template_title = 'Regular Payment'; diff --git a/app/Core/Payment/Pos/ex/examples/garanti/regular/cancel.php b/app/Core/Payment/Pos/ex/examples/garanti/regular/cancel.php new file mode 100644 index 0000000..c77d011 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/regular/cancel.php @@ -0,0 +1,39 @@ +bank->cancel([ + 'order_id' => '20181114DF2C', + 'ip' => $ip, + 'email' => 'mail@customer.com', + 'ref_ret_num' => '831803579226', + 'amount' => 1, + 'currency' => 'TRY', +]); + +$response = $cancel->response; +$dump = get_object_vars($response); +?> + +
+

+ proc_return_code == '00' ? 'Cancel Order is successful!' : 'Cancel Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/regular/direct.php b/app/Core/Payment/Pos/ex/examples/garanti/regular/direct.php new file mode 100644 index 0000000..6d88021 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/regular/direct.php @@ -0,0 +1,35 @@ + $order_id, + 'name' => 'John Doe', // optional + 'email' => 'mail@customer.com', // optional + 'user_id' => '12', // optional + 'amount' => $amount, + 'installment' => '0', + 'currency' => 'TRY', + 'ip' => $ip, + 'transaction' => 'pay', // pay => Auth, pre PreAuth +]; + +$card = [ + 'number' => '4282209027132016', + 'month' => '05', + 'year' => '20', + 'cvv' => '165', +]; + +try { + $pos->prepare($order); +} catch (\Mews\Pos\Exceptions\UnsupportedTransactionTypeException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$payment = $pos->payment($card); + +var_dump($payment->response); diff --git a/app/Core/Payment/Pos/ex/examples/garanti/regular/history.php b/app/Core/Payment/Pos/ex/examples/garanti/regular/history.php new file mode 100644 index 0000000..4ae31a3 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/regular/history.php @@ -0,0 +1,36 @@ +bank->history([ + 'order_id' => '2018111377EF', + 'currency' => 'TRY', + 'ip' => $ip, +]); + +$response = $query->response; +$dump = get_object_vars($response); +?> + +
+

+ proc_return_code == '00' ? 'History Order is successful!' : 'History Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/regular/index.php b/app/Core/Payment/Pos/ex/examples/garanti/regular/index.php new file mode 100644 index 0000000..2999bdd --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/regular/index.php @@ -0,0 +1,44 @@ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/regular/post.php b/app/Core/Payment/Pos/ex/examples/garanti/regular/post.php new file mode 100644 index 0000000..7d78dab --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/regular/post.php @@ -0,0 +1,70 @@ +getClientIp(); + +$account = [ + 'bank' => 'garanti', + 'model' => 'regular', + 'client_id' => '7000679', + 'terminal_id' => '30691297', + 'username' => 'PROVAUT', + 'password' => '123qweASD/', + 'env' => 'test', +]; + +$template_title = 'Post Auth Order'; + +require '../../template/_header.php'; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$order = [ + 'id' => '201810231553', + 'transaction' => 'post', + 'amount' => '1', + 'ref_ret_num' => '829603332856', + 'ip' => $ip, +]; + +try { + $pos->prepare($order); +} catch (\Mews\Pos\Exceptions\UnsupportedTransactionTypeException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$payment = $pos->payment(); + +$response = $payment->response; +$dump = get_object_vars($response); +?> + +
+

+ proc_return_code == '00' ? 'Post Auth Order is successful!' : 'Post Auth Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/regular/refund.php b/app/Core/Payment/Pos/ex/examples/garanti/regular/refund.php new file mode 100644 index 0000000..89d6b5b --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/regular/refund.php @@ -0,0 +1,39 @@ +bank->refund([ + 'order_id' => '201811142A0A', + 'ip' => $ip, + 'email' => 'mail@customer.com', + 'ref_ret_num' => '831803586333', + 'amount' => 1, + 'currency' => 'TRY', +]); + +$response = $refund->response; +$dump = get_object_vars($response); +?> + +
+

+ proc_return_code == '00' ? 'Refund Order is successful!' : 'Refund Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/regular/response.php b/app/Core/Payment/Pos/ex/examples/garanti/regular/response.php new file mode 100644 index 0000000..b3a99bd --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/regular/response.php @@ -0,0 +1,125 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order_id = date('Ymd') . strtoupper(substr(uniqid(sha1(time())),0,4)); +$amount = (double) 1; + +$order = [ + 'id' => $order_id, + 'name' => 'John Doe', // optional + 'email' => 'mail@customer.com', // optional + 'user_id' => '12', // optional + 'amount' => $amount, + 'installment' => '1', + 'currency' => 'TRY', + 'ip' => $ip, + 'transaction' => 'pay', // pay => S, pre => preauth +]; + +$pos->prepare($order); + +$card = [ + 'number' => $request->get('number'), + 'month' => $request->get('month'), + 'year' => $request->get('year'), + 'cvv' => $request->get('cvv'), +]; + +$payment = $pos->payment($card); + +$response = $payment->response; + +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Payment is successful!' : 'Payment is not successful!'; ?> +

+
+
+
Response:
+
response; ?>
+
+
+
+
Status:
+
status; ?>
+
+
+
+
Transaction:
+
transaction; ?>
+
+
+
+
Transaction Type:
+
transaction_type; ?>
+
+
+
+
Order ID:
+
order_id ? $response->order_id : '-'; ?>
+
+
+
+
Group ID:
+
group_id ? $response->group_id : '-'; ?>
+
+
+
+
AuthCode:
+
auth_code ? $response->auth_code : '-'; ?>
+
+
+
+
HostRefNum:
+
host_ref_num ? $response->host_ref_num : '-'; ?>
+
+
+
+
RetrefNum:
+
ret_ref_num ? $response->ret_ref_num : '-'; ?>
+
+
+
+
HashData:
+
hash_data ? $response->hash_data : '-'; ?>
+
+
+
+
ProcReturnCode:
+
code; ?>
+
+
+
+
Error Code:
+
error_code ? $response->error_code : '-'; ?>
+
+
+
+
Error Message:
+
error_message ? $response->error_message : '-'; ?>
+
+
+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/garanti/regular/status.php b/app/Core/Payment/Pos/ex/examples/garanti/regular/status.php new file mode 100644 index 0000000..eba5176 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/garanti/regular/status.php @@ -0,0 +1,36 @@ +bank->status([ + 'order_id' => '201812195CF2', + 'currency' => 'TRY', + 'ip' => $ip +]); + +$response = $query->response; +$dump = get_object_vars($response); +?> + +
+

+ proc_return_code == '00' ? 'Query Order is successful!' : 'Query Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/template/_footer.php b/app/Core/Payment/Pos/ex/examples/template/_footer.php new file mode 100644 index 0000000..da75efc --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/template/_footer.php @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/app/Core/Payment/Pos/ex/examples/template/_header.php b/app/Core/Payment/Pos/ex/examples/template/_header.php new file mode 100644 index 0000000..8aad334 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/template/_header.php @@ -0,0 +1,16 @@ + + + + <?php echo $template_title; ?> + + + + + + + + +
+
+

+
diff --git a/app/Core/Payment/Pos/ex/examples/ykb/3d/_config.php b/app/Core/Payment/Pos/ex/examples/ykb/3d/_config.php new file mode 100644 index 0000000..b27c6dc --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/3d/_config.php @@ -0,0 +1,38 @@ +getClientIp(); + +$account = [ + 'bank' => 'yapikredi', + 'model' => '3d', + 'client_id' => 'XXXXXX', + 'terminal_id' => 'XXXXXX', + 'posnet_id' => 'XXXXXX', + 'username' => 'XXXXXX', + 'password' => 'XXXXXX', + 'store_key' => '10,10,10,10,10,10,10,10', + 'promotion_code' => '', + 'env' => 'test', +]; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$template_title = '3D Model Payment'; diff --git a/app/Core/Payment/Pos/ex/examples/ykb/3d/form.php b/app/Core/Payment/Pos/ex/examples/ykb/3d/form.php new file mode 100644 index 0000000..2460624 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/3d/form.php @@ -0,0 +1,48 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order = [ + 'id' => $_POST['order_id'], + 'name' => $_POST['name'], + 'amount' => $_POST['amount'], + 'currency' => $_POST['currency'], + 'transaction' => $_POST['transaction'], + 'success_url' => $_POST['success_url'], + 'fail_url' => $_POST['fail_url'], + 'lang' => $_POST['lang'], +]; + +$_SESSION['order'] = $order; + +$card = [ + 'type' => $_POST['type'], + 'name' => $_POST['name'], + 'number' => $_POST['number'], + 'month' => $_POST['month'], + 'year' => $_POST['year'], + 'cvv' => $_POST['cvv'], +]; + +$pos->prepare($order, $card); + +$form_data = $pos->get3dFormData(); +?> + +Redirecting... +
+ $value): ?> + + +
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/ykb/3d/index.php b/app/Core/Payment/Pos/ex/examples/ykb/3d/index.php new file mode 100644 index 0000000..c38e7b1 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/3d/index.php @@ -0,0 +1,77 @@ + $order_id, + 'amount' => $amount, + 'installment' => $instalment, + 'currency' => 'TRY', + 'success_url' => $success_url, + 'fail_url' => $fail_url, + 'transaction' => 'pay', // pay => Sale, pre Auth + 'lang' => 'tr', +]; +?> + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + + + + +
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/ykb/3d/response.php b/app/Core/Payment/Pos/ex/examples/ykb/3d/response.php new file mode 100644 index 0000000..8b4ee8e --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/3d/response.php @@ -0,0 +1,112 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order = $_SESSION['order']; + +$pos->prepare($order); +$payment = $pos->payment(); +$response = $payment->response; + +$dump = get_object_vars($response); +?> + +
+

+ status == 'approved' ? 'Payment is successful!' : 'Payment is not successful'; ?> +

+
+
+
Response:
+
response ? $response->response : '-'; ?>
+
+
+
+
Status:
+
status; ?>
+
+
+
+
Transaction:
+
transaction; ?>
+
+
+
+
Transaction Type:
+
transaction_type; ?>
+
+
+
+
Transaction Security:
+
transaction_security; ?>
+
+
+
+
Hash:
+
+ hash as $key => $hash): ?> + + +
+
+
+
+
Order ID:
+
id; ?>
+
+
+
+
AuthCode:
+
auth_code ? $response->auth_code : '-'; ?>
+
+
+
+
HostRefNum:
+
host_ref_num ? $response->host_ref_num : '-'; ?>
+
+
+
+
ProcReturnCode:
+
code ? $response->code : '-'; ?>
+
+
+
+
mdStatus:
+
md_status ? $response->md_status : '-'; ?>
+
+
+
+
Error Code:
+
error_code ? $response->error_code : '-'; ?>
+
+
+
+
Error Message:
+
error_message ? $response->error_message : '-'; ?>
+
+
+
+
Md Error Message:
+
md_error_message ? $response->md_error_message : '-'; ?>
+
+
+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/ykb/regular/_config.php b/app/Core/Payment/Pos/ex/examples/ykb/regular/_config.php new file mode 100644 index 0000000..956952e --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/regular/_config.php @@ -0,0 +1,31 @@ +getClientIp(); + +$account = [ + 'bank' => 'yapikredi', + 'model' => 'regular', + 'client_id' => '6706598320', + 'terminal_id' => '67322946', + 'posnet_id' => '27426', + 'env' => 'test', +]; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$gateway = $base_url . 'response.php'; + +$template_title = 'Regular Payment'; diff --git a/app/Core/Payment/Pos/ex/examples/ykb/regular/cancel.php b/app/Core/Payment/Pos/ex/examples/ykb/regular/cancel.php new file mode 100644 index 0000000..5aa24cb --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/regular/cancel.php @@ -0,0 +1,43 @@ +bank->cancel([ + 'order_id' => '201811133F3F', +]); + +/* +// faster params... +$cancel = $pos->bank->cancel([ + 'order_id' => '201810295863', + 'host_ref_num' => '018711539490000181', + 'auth_code' => '115394', +]); +*/ + +$response = $cancel->response; +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Cancel Order is successful!' : 'Cancel Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/ykb/regular/direct.php b/app/Core/Payment/Pos/ex/examples/ykb/regular/direct.php new file mode 100644 index 0000000..21f8531 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/regular/direct.php @@ -0,0 +1,35 @@ + $order_id, + 'name' => 'John Doe', // optional + 'email' => 'mail@customer.com', // optional + 'user_id' => '12', // optional + 'amount' => $amount, + 'installment' => '0', + 'currency' => 'TRY', + 'ip' => $ip, + 'transaction' => 'pay', // pay => Auth, pre PreAuth +]; + +$card = [ + 'number' => '4355084355084358', + 'month' => '12', + 'year' => '18', + 'cvv' => '000', +]; + +try { + $pos->prepare($order); +} catch (\Mews\Pos\Exceptions\UnsupportedTransactionTypeException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$payment = $pos->payment($card); + +var_dump($payment->response); diff --git a/app/Core/Payment/Pos/ex/examples/ykb/regular/history.php b/app/Core/Payment/Pos/ex/examples/ykb/regular/history.php new file mode 100644 index 0000000..114f028 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/regular/history.php @@ -0,0 +1,34 @@ +bank->history([ + 'order_id' => '201811133F3F', +]); + +$response = $query->response; +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() == '00' ? 'History Order is successful!' : 'History Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/ykb/regular/index.php b/app/Core/Payment/Pos/ex/examples/ykb/regular/index.php new file mode 100644 index 0000000..2999bdd --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/regular/index.php @@ -0,0 +1,44 @@ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + diff --git a/app/Core/Payment/Pos/ex/examples/ykb/regular/post.php b/app/Core/Payment/Pos/ex/examples/ykb/regular/post.php new file mode 100644 index 0000000..5fcbd30 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/regular/post.php @@ -0,0 +1,70 @@ +getClientIp(); + +$account = [ + 'bank' => 'yapikredi', + 'model' => 'regular', + 'client_id' => '6706598320', + 'terminal_id' => '67322946', + 'posnet_id' => '27426', + 'env' => 'test', +]; + +$template_title = 'Post Auth Order'; + +require '../../template/_header.php'; + +try { + $pos = new \Mews\Pos\Pos($account); +} catch (\Mews\Pos\Exceptions\BankNotFoundException $e) { + var_dump($e->getCode(), $e->getMessage()); +} catch (\Mews\Pos\Exceptions\BankClassNullException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$order = [ + 'id' => '2018102949E0', + 'host_ref_num' => '018711533790000181', + 'amount' => '100', + 'currency' => 'TRY', + 'installment' => '2', + 'transaction' => 'post', +]; + +try { + $pos->prepare($order); +} catch (\Mews\Pos\Exceptions\UnsupportedTransactionTypeException $e) { + var_dump($e->getCode(), $e->getMessage()); +} + +$payment = $pos->payment(); + +$response = $payment->response; +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Post Auth Order is successful!' : 'Post Auth Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/ykb/regular/refund.php b/app/Core/Payment/Pos/ex/examples/ykb/regular/refund.php new file mode 100644 index 0000000..71f89c5 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/regular/refund.php @@ -0,0 +1,36 @@ +bank->refund([ + 'order_id' => '20181113DCDB', + 'amount' => '1', + 'currency' => 'TRY', +]); + +$response = $refund->response; +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Refund Order is successful!' : 'Refund Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/ykb/regular/response.php b/app/Core/Payment/Pos/ex/examples/ykb/regular/response.php new file mode 100644 index 0000000..0b73b49 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/regular/response.php @@ -0,0 +1,119 @@ +getMethod() !== 'POST') { + echo new \Symfony\Component\HttpFoundation\RedirectResponse($base_url); + exit(); +} + +$order_id = date('Ymd') . strtoupper(substr(uniqid(sha1(time())),0,4)); +$amount = (double) 100; + +$order = [ + 'id' => $order_id, + 'name' => 'John Doe', // optional + 'email' => 'mail@customer.com', // optional + 'user_id' => '12', // optional + 'amount' => $amount, + 'installment' => 1, + 'currency' => 'TRY', + 'transaction' => 'pay', // pay => Sale, pre Auth +]; + +$pos->prepare($order); + +$card = [ + 'number' => $request->get('number'), + 'month' => $request->get('month'), + 'year' => $request->get('year'), + 'cvv' => $request->get('cvv'), +]; + +$payment = $pos->payment($card); + +$response = $payment->response; + +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Payment is successful!' : 'Payment is not successful!'; ?> +

+
+
+
Response:
+
response; ?>
+
+
+
+
Status:
+
status; ?>
+
+
+
+
Transaction:
+
transaction; ?>
+
+
+
+
Transaction Type:
+
transaction_type; ?>
+
+
+
+
Order ID:
+
order_id ? $response->order_id : '-'; ?>
+
+
+
+
Group ID:
+
group_id ? $response->group_id : '-'; ?>
+
+
+
+
AuthCode:
+
auth_code ? $response->auth_code : '-'; ?>
+
+
+
+
HostRefNum:
+
host_ref_num ? $response->host_ref_num : '-'; ?>
+
+
+
+
ProcReturnCode:
+
proc_return_code; ?>
+
+
+
+
Code:
+
code; ?>
+
+
+
+
Error Code:
+
error_code ? $response->error_code : '-'; ?>
+
+
+
+
Error Message:
+
error_message ? $response->error_message : '-'; ?>
+
+
+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/examples/ykb/regular/status.php b/app/Core/Payment/Pos/ex/examples/ykb/regular/status.php new file mode 100644 index 0000000..59fec19 --- /dev/null +++ b/app/Core/Payment/Pos/ex/examples/ykb/regular/status.php @@ -0,0 +1,34 @@ +bank->status([ + 'order_id' => '201811133F3F', +]); + +$response = $query->response; +$dump = get_object_vars($response); +?> + +
+

+ isSuccess() ? 'Query Order is successful!' : 'Query Order is not successful!'; ?> +

+
+
All Data Dump:
+
+
+
+
+
+ +
+ + diff --git a/app/Core/Payment/Pos/ex/phpunit.xml b/app/Core/Payment/Pos/ex/phpunit.xml new file mode 100644 index 0000000..d67d7da --- /dev/null +++ b/app/Core/Payment/Pos/ex/phpunit.xml @@ -0,0 +1,25 @@ + + + + + tests + + + + + src + + vendor + + + + diff --git a/app/Core/Payment/Pos/ex/tests/EstPostTest.php b/app/Core/Payment/Pos/ex/tests/EstPostTest.php new file mode 100644 index 0000000..f514aaa --- /dev/null +++ b/app/Core/Payment/Pos/ex/tests/EstPostTest.php @@ -0,0 +1,149 @@ +config = require __DIR__ . '/../config/pos.php'; + + $this->account = (object)[ + 'bank' => 'akbank', + 'model' => '3d', + 'client_id' => 'XXXXXXX', + 'username' => 'XXXXXXX', + 'password' => 'XXXXXXX', + 'store_key' => 'VnM5WZ3sGrPusmWP', + 'env' => 'test', + ]; + + $this->card = (object)[ + 'number' => '5555444433332222', + 'year' => '21', + 'month' => '12', + 'cvv' => '122', + 'name' => 'ahmet', + 'type' => 'visa' + ]; + + $this->order = (object)[ + 'id' => 'order222', + 'name' => 'siparis veren', + 'email' => 'test@test.com', + 'amount' => '100.25', + 'installment' => 0, + 'currency' => 'TRY', + 'success_url' => 'https://domain.com/success', + 'fail_url' => 'https://domain.com/fail_url', + 'lang' => 'tr', + 'rand' => microtime() + ]; + + $this->estpos = new EstPos( + $this->config['banks'][$this->account->bank], + $this->account, + $this->config['currencies']); + } + + public function testInit() + { + $this->assertEquals($this->config['banks'][$this->account->bank], $this->estpos->getConfig()); + $this->assertEquals($this->account, $this->estpos->getAccount()); + $this->assertEquals($this->config['currencies'], $this->estpos->getCurrencies()); + } + + public function testPrepare() + { + + $this->estpos->prepare($this->order, $this->card); + $this->assertEquals($this->card, $this->estpos->getCard()); + $this->assertEquals($this->order, $this->estpos->getOrder()); + } + + public function testGetCardCode() + { + $card = $this->card; + + $card->type = '1'; + $this->estpos->prepare($this->order, $card); + $this->assertEquals($card->type, $this->estpos->getCardCode()); + + $card->type = 'visa'; + $this->estpos->prepare($this->order, $card); + $this->assertNotNull($this->estpos->getCardCode()); + + $card->type = 'master'; + $this->estpos->prepare($this->order, $card); + $this->assertNotNull($this->estpos->getCardCode()); + } + + public function testGet3DFormData() + { + $this->estpos->prepare($this->order, $this->card); + + $form = [ + 'gateway' => $this->config['banks'][$this->account->bank]['urls']['gateway'][$this->account->env], + 'success_url' => $this->order->success_url, + 'fail_url' => $this->order->fail_url, + 'rand' => $this->order->rand, + 'hash' => $this->estpos->create3DHash(), + 'inputs' => [ + 'clientid' => $this->account->client_id, + 'storetype' => $this->account->model, + 'hash' => $this->estpos->create3DHash(), + 'cardType' => $this->estpos->getCardCode(), + 'pan' => $this->card->number, + 'Ecom_Payment_Card_ExpDate_Month' => $this->card->month, + 'Ecom_Payment_Card_ExpDate_Year' => $this->card->year, + 'cv2' => $this->card->cvv, + 'firmaadi' => $this->order->name, + 'Email' => $this->order->email, + 'amount' => $this->order->amount, + 'oid' => $this->order->id, + 'okUrl' => $this->order->success_url, + 'failUrl' => $this->order->fail_url, + 'rnd' => $this->order->rand, + 'lang' => $this->order->lang, + 'currency' => $this->order->currency, + ] + ]; + $this->assertEquals($form, $this->estpos->get3DFormData()); + } + + public function testCheck3DHash() + { + $data = [ + "md" => "478719:0373D10CFD8BDED34FA0546D27D5BE76F8BA4A947D1EC499102AE97B880EB1B9:4242:##400902568", + "cavv" => "BwAQAhIYRwEAABWGABhHEE6v5IU=", + "AuthCode" => "", + "oid" => "880", + "mdStatus" => "4", + "eci" => "06", + "clientid" => "400902568", + "rnd" => "hDx50d0cq7u1vbpWQMae", + "ProcReturnCode" => "N7", + "Response" => "Declined", + "HASH" => "D+B5fFWXEWFqVSkwotyuTPUW800=", + "HASHPARAMS" => "clientid:oid:AuthCode:ProcReturnCode:Response:mdStatus:cavv:eci:md:rnd:", + "HASHPARAMSVAL" => "400902568880N7Declined4BwAQAhIYRwEAABWGABhHEE6v5IU=06478719:0373D10CFD8BDED34FA0546D27D5BE76F8BA4A947D1EC499102AE97B880EB1B9:4242:##400902568hDx50d0cq7u1vbpWQMae" + ]; + + $this->assertTrue($this->estpos->check3DHash($data)); + + $data['mdStatus'] = ''; + $this->assertFalse($this->estpos->check3DHash($data)); + } +} diff --git a/app/Core/Payment/Pos/ex/tests/GarantiPosTest.php b/app/Core/Payment/Pos/ex/tests/GarantiPosTest.php new file mode 100644 index 0000000..dc80dca --- /dev/null +++ b/app/Core/Payment/Pos/ex/tests/GarantiPosTest.php @@ -0,0 +1,98 @@ +config = require __DIR__ . '/../config/pos.php'; + + $this->account = (object)[ + 'bank' => 'akbank', + 'model' => '3d', + 'client_id' => 'XXXXXXX', + 'terminal_id' => '13456', + 'username' => 'XXXXXXX', + 'password' => 'XXXXXXX', + 'store_key' => 'XXXXXXX', + 'env' => 'test', + ]; + + $this->card = (object)[ + 'number' => '5555444433332222', + 'year' => '21', + 'month' => '12', + 'cvv' => '122', + 'name' => 'ahmet', + 'type' => 'visa' + ]; + + $this->order = (object)[ + 'id' => 'order222', + 'name' => 'siparis veren', + 'email' => 'test@test.com', + 'amount' => '100.25', + 'installment' => 0, + 'currency' => 'TRY', + 'success_url' => 'https://domain.com/success', + 'fail_url' => 'https://domain.com/fail_url', + 'lang' => 'tr', + 'rand' => microtime(), + 'ip' => '156.155.154.153' + ]; + + $this->garantiPos = new GarantiPos( + $this->config['banks'][$this->account->bank], + $this->account, + $this->config['currencies']); + } + + public function testInit() + { + $this->assertEquals($this->config['banks'][$this->account->bank], $this->garantiPos->getConfig()); + $this->assertEquals($this->account, $this->garantiPos->getAccount()); + $this->assertEquals($this->config['currencies'], $this->garantiPos->getCurrencies()); + } + + public function testPrepare() + { + + $this->garantiPos->prepare($this->order, $this->card); + $this->assertEquals($this->card, $this->garantiPos->getCard()); + $this->assertEquals($this->order, $this->garantiPos->getOrder()); + } + + public function testGet3DFormData() + { + $this->garantiPos->prepare($this->order, $this->card); + + $form = [ + 'gateway' => $this->config['banks'][$this->account->bank]['urls']['gateway'][$this->account->env], + 'success_url' => $this->order->success_url, + 'fail_url' => $this->order->fail_url, + 'rand' => $this->order->rand + ]; + $actualForm = $this->garantiPos->get3DFormData(); + $this->assertNotEmpty($actualForm['inputs']); + $this->assertNotEmpty($actualForm['hash']); + + unset($actualForm['inputs']); + unset($actualForm['hash']); + $this->assertEquals($form, $actualForm); + } + + +} diff --git a/app/Core/Payment/Pos/ex/tests/PosNetCryptTest.php b/app/Core/Payment/Pos/ex/tests/PosNetCryptTest.php new file mode 100644 index 0000000..d8a81e5 --- /dev/null +++ b/app/Core/Payment/Pos/ex/tests/PosNetCryptTest.php @@ -0,0 +1,35 @@ +crypt = new PosNetCrypt(); + } + + public function testDecrypt(){ + $data = '9ACF38C842B3522415364850EAD1909BD43FD590BE3CBD539AD5FF6C7465973ABD61E8371E03282605ED06C994DF394244B7E7DAD54A046510484FAA724330C4C95A527D7891151E7C195D4136CBD70A87D1BD1F75473CF6B45A3F2FA8231DD71FFB4150E0BF4B133ECAA5ACC82CFD74903E21BC6EECB4B33AF39B8AF0C183A64002CFC125A55685C69A13192F3A9A4FDAC860E90C3FB6D125285E9E687BEFBE05707E131FC7ABE25FE35AB114FAE8A247B8C0F3DBA8AA74396D10564B7A0617EED913ED'; + + $key = '10,10,10,10,10,10,10,10'; + $expected_output = '6706598320;67005551;100;00;YKB_TST_090519001330;0;0;https://setmpos.ykb.com/PosnetWebService/YKBTransactionService;posnettest.ykb.com;2225;N;0;Not authenticated;1557398383820;TL'; + + $dc = $this->crypt->decrypt($data, $key); + $this->assertEquals($expected_output, $dc); + + $data = '1974BC4B9984FF173F9DF305090E44241243CCC9648349C0D607AECACCB55C1F1ED47452B3AD90785F9BCC6AC7E65450D4E72F31B9FC8F9F55A7D109C2BE966C6DD3F3DE12B3457FF0C6FA8BCDBB4B8E5341C1C3DA327992C28354EB3B5C62472C06B8FB6DF34E351206D2CCD5E323FA26EC3EFF2C25656C74C836954F193E634AF391761F88B5D53DBE1FC61A89DB713DFC983D9605D080ACA857196DD3E7B3C52BFCC914CE47961D76B590ECB34B28113A6E4FAA572958D836B09546A9A62D24F829FE18628DF84504FB02'; + $expected_output = '6706598320;67825768;100;00;00000000000000000892;0;0;https://setmpos.ykb.com/PosnetWebService/YKBTransactionService;posnettest.ykb.com;2225;N;9;None 3D - Secure Transaction;1586175747626;TL'; + + $dc = $this->crypt->decrypt($data, $key); + $this->assertEquals($expected_output, $dc); + } + +} diff --git a/app/Core/Payment/Pos/ex/tests/PosNetTest.php b/app/Core/Payment/Pos/ex/tests/PosNetTest.php new file mode 100644 index 0000000..8403ea9 --- /dev/null +++ b/app/Core/Payment/Pos/ex/tests/PosNetTest.php @@ -0,0 +1,110 @@ +account = (object)[ + 'bank' => 'yapikredi', + 'model' => 'regular', + 'client_id' => '6706598320', + 'terminal_id' => '67005551', + 'posnet_id' => '27426', + 'env' => 'test', + 'store_key' => '10,10,10,10,10,10,10,10', + 'model' => '3d' + ]; + + $this->card = (object)[ + 'number' => '5555444433332222', + 'year' => '21', + 'month' => '12', + 'cvv' => '122', + 'name' => 'ahmet', + 'type' => 'visa' + ]; + + $this->order = (object)[ + 'id' => 'YKB_TST_190620093100_024', + 'name' => 'siparis veren', + 'email' => 'test@test.com', + 'amount' => '1.75', + 'installment' => 0, + 'currency' => 'TL', + 'success_url' => 'https://domain.com/success', + 'fail_url' => 'https://domain.com/fail_url', + 'lang' => 'tr', + 'rand' => microtime() + ]; + + + $this->config = require __DIR__ . '/../config/pos.php'; + $this->posnet = new PosNet( + $this->config['banks'][$this->account->bank], + $this->account, + $this->config['currencies']); + } + + public function testInit() + { + $this->assertEquals($this->config['banks'][$this->account->bank], $this->posnet->getConfig()); + $this->assertEquals($this->account, $this->posnet->getAccount()); + $this->assertEquals($this->config['currencies'], $this->posnet->getCurrencies()); + } + + public function testPrepare() + { + $this->posnet->prepare($this->order, $this->card); + $this->assertEquals($this->card, $this->posnet->getCard()); + $this->assertEquals($this->order, $this->posnet->getOrder()); + } + + public function testCreate3DHash(){ + + $this->posnet->prepare($this->order, $this->card); + $this->assertEquals('J/7/Xprj7F/KDf98luVfIGyUPRQzUCqGwpmvz3KT7oQ=', $this->posnet->create3DHash()); + } + + public function testVerifyResponseMAC(){ + + $order = $this->order; + $order->id = '895'; + $order->amount = 1; + $order->currency = 'TL'; + + $account = $this->account; + $account->client_id = '6706598320'; + $account->terminal_id = '67825768'; + $account->store_key = '10,10,10,10,10,10,10,10'; + + $this->posnet->prepare($order, $account); + $data = (object)[ + 'mdStatus' => '9', + 'mac' => 'U2kU/JWjclCvKZjILq8xBJUXhyB4DswKvN+pKfxl0u0=' + ]; + $this->assertTrue($this->posnet->verifyResponseMAC($data)); + + $order->id = '800'; + $this->posnet->prepare($order, $account); + $data = (object)[ + 'mdStatus' => '9', + 'mac' => 'U2kU/JWjclCvKZjILq8xBJUXhyB4DswKvN+pKfxl0u0=' + ]; + $this->assertFalse($this->posnet->verifyResponseMAC($data)); + } +} diff --git a/app/Core/Payment/Pos/ex/tests/PosTest.php b/app/Core/Payment/Pos/ex/tests/PosTest.php new file mode 100644 index 0000000..650e16b --- /dev/null +++ b/app/Core/Payment/Pos/ex/tests/PosTest.php @@ -0,0 +1,94 @@ +config = require __DIR__ . '/../config/pos.php'; + $this->account = [ + 'bank' => 'yapikredi', + 'model' => 'regular', + 'client_id' => '6706598320', + 'terminal_id' => '67322946', + 'posnet_id' => '27426', + 'env' => 'test', + 'store_key' => '10,10,10,10,10,10,10,10' + ]; + + $this->card = [ + 'number' => '5555444433332222', + 'year' => '21', + 'month' => '12', + 'cvv' => '122', + 'name' => 'ahmet', + 'type' => 'visa' + ]; + + $this->order = [ + 'id' => 'order222', + 'name' => 'siparis veren', + 'email' => 'test@test.com', + 'amount' => '100.25', + 'installment' => 0, + 'currency' => 'TRY', + 'success_url' => 'https://domain.com/success', + 'fail_url' => 'https://domain.com/fail_url', + 'lang' => 'tr', + 'rand' => microtime() + ]; + + $this->pos = new Pos($this->account); + } + + public function testInit() + { + $this->assertEquals($this->config['banks'][$this->account['bank']], $this->pos->getConfig()); + $this->assertEquals((object)$this->account, $this->pos->getAccount()); + $this->assertEquals($this->config['currencies'], $this->pos->getCurrencies()); + $this->assertInstanceOf(PosNet::class, $this->pos->bank); + } + + public function testCreateXML() + { + $xml_str = $this->createXML($this->order); + $this->assertIsString($xml_str); + } + + public function testXMLStringToObject() + { + $xml_str = $this->createXML(['order' => $this->order]); + $this->assertEquals((object)$this->order, $this->XMLStringToObject($xml_str)); + } + + public function testPrepare() + { + + $this->pos->prepare($this->order, $this->card); + $this->assertEquals((object)$this->card, $this->pos->getCard()); + //$this->assertEquals((object)$order, $this->pos->getOrder()); + } + + public function testGetGatewayUrl() + { + $this->assertEquals($this->config['banks'][$this->account['bank']]['urls']['gateway'][$this->account['env']], $this->pos->getGatewayUrl()); + } +} diff --git a/app/Core/Payment/QNBPay/QNBPay.php b/app/Core/Payment/QNBPay/QNBPay.php new file mode 100644 index 0000000..7fd729b --- /dev/null +++ b/app/Core/Payment/QNBPay/QNBPay.php @@ -0,0 +1,254 @@ +restClient = new Client(); + + $this->requestUrl = 'https://portal.qnbpay.com.tr/ccpayment'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://test.qnbpay.com.tr/ccpayment'; + } + + + $this->merchantId = $paymentInitializeParam['merchantId']; + $this->merchantKey = $paymentInitializeParam['merchantKey']; + $this->appKey = $paymentInitializeParam['appKey']; + $this->appSecret = $paymentInitializeParam['appSecret']; + + $this->getAccessToken = $this->getAccessToken(); + + } + + private function makeRequest($method, $payloadData) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['Content-Type'] = 'application/json'; + if ($method != 'api/token') { + $requestParams['headers']['Authorization'] = 'Bearer ' . $this->getAccessToken; + } + + + $requestParams['body'] = json_encode($payloadData); + + $result = $this->restClient->post($this->requestUrl . '/' . $method, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if ($getResponseData['status_code'] == 100) { + $response = [ + 'status' => true, + 'serviceResponse' => $getResponseData + ]; + } else { + $response['message'] = $getResponseData['status_description']; + $response['serviceResponse'] = $getResponseData; + } + + + } catch (ClientException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + Log::error($method); + Log::error($payloadData); + Log::error($response); + } + + return $response; + + } + + + private function getAccessToken() + { + + $method = 'api/token'; + $payloadData = [ + 'app_id' => $this->appKey, + 'app_secret' => $this->appSecret, + ]; + + $getTokenDataRequest = $this->makeRequest($method, $payloadData); + + if ($getTokenDataRequest['status']) { + $getTokenData = $getTokenDataRequest['serviceResponse']['data']['token']; + } + + return $getTokenData; + } + + public function generateHashKey($total, $installment, $currency_code, $invoice_id) + { + + $data = $total . '|' . $installment . '|' . $currency_code . '|' . $this->merchantKey . '|' . $invoice_id; + + $iv = substr(sha1(mt_rand()), 0, 16); + $password = sha1($this->appSecret); + + $salt = substr(sha1(mt_rand()), 0, 4); + $saltWithPassword = hash('sha256', $password . $salt); + + $encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv); + + $msg_encrypted_bundle = "$iv:$salt:$encrypted"; + $msg_encrypted_bundle = str_replace('/', '__', $msg_encrypted_bundle); + + return $msg_encrypted_bundle; + } + + public function generateRefundHashKey($invoice_id, $merchant_key, $app_secret) + { + $data = $invoice_id . '|' . $merchant_key; + $iv = substr(sha1(mt_rand()), 0, 16); + $password = sha1($app_secret); + $salt = substr(sha1(mt_rand()), 0, 4); + $saltWithPassword = hash('sha256', $password . $salt); + $encrypted = openssl_encrypt( + $data, 'aes-256-cbc', "$saltWithPassword", null, $iv + ); + $msg_encrypted_bundle = "$iv:$salt:$encrypted"; + $hash_key = str_replace('/', '__', $msg_encrypted_bundle); + return $hash_key; + } + + public function paySmart3D($param) + { + + $response = ['status' => false, 'message' => '']; + try { + + $param['creditCard']['installment'] = $param['creditCard']['installment'] == 0 ? 1 : $param['creditCard']['installment']; + + $generateHashKey = $this->generateHashKey($param['amount'], $param['creditCard']['installment'], $param['currencyCode'], $param['orderId']); + + $items = []; + $items[] = [ + 'name' => 'Booking', + 'price' => $param['amount'], + 'quantity' => 1, + 'description' => $param['orderCode'], + ]; + + $payment3dFormData['gateway'] = $this->requestUrl . '/api/paySmart3D'; + + $payment3dFormData['inputs'] = [ + 'currency_code' => $param['currencyCode'], + 'installments_number' => $param['creditCard']['installment'], + 'invoice_id' => $param['orderId'], + 'invoice_description' => $param['orderCode'], + 'total' => $param['amount'], + 'merchant_key' => $this->merchantKey, + 'items' => json_encode($items), + //'name' => $param['name'], + //'surname' => $param['surname'], + 'hash_key' => $generateHashKey, + 'return_url' => $param['paymentCheckUrl'], + 'cancel_url' => $param['paymentCheckUrl'], + 'cc_holder_name' => $param['creditCard']['holderName'], + 'cc_no' => $param['creditCard']['number'], + 'expiry_month' => $param['creditCard']['expiryMonth'], + 'expiry_year' => $param['creditCard']['expiryYear'], + 'cvv' => $param['creditCard']['cvv'], + 'transaction_type' => 'Auth', + 'is_comission_from_user' => '2', + 'response_method' => 'GET', + ]; + + $response = [ + 'status' => true, + 'data' => $payment3dFormData + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + return $response; + + } + + public function checkPaymentStatus($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $generateHashKey = $this->generateRefundHashKey($orderId, $this->merchantKey, $this->appSecret); + + $method = 'api/checkstatus'; + $payloadData = [ + 'invoice_id' => $orderId, + 'merchant_key' => $this->merchantKey, + 'include_pending_status' => true, + 'hash_key' => $generateHashKey + ]; + + $checkstatusRequest = $this->makeRequest($method, $payloadData); + + if (!$checkstatusRequest['status']) { + throw new ApiErrorException($checkstatusRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $checkstatusRequest['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($checkstatusRequest['serviceResponse'])) { + $response['serviceResponse'] = $checkstatusRequest['serviceResponse']; + } + + return $response; + + } + +} diff --git a/app/Core/Payment/RetailPay/RetailPay.php b/app/Core/Payment/RetailPay/RetailPay.php new file mode 100644 index 0000000..b329376 --- /dev/null +++ b/app/Core/Payment/RetailPay/RetailPay.php @@ -0,0 +1,268 @@ +restClient = new Client(); + + $this->requestUrl = 'https://api.sprynto-psp.com'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://api.sprynto-psp-test.com'; + } + + + $this->userName = $paymentInitializeParam['userName']; + $this->password = $paymentInitializeParam['password']; + $this->ipAddress = isset($paymentInitializeParam['ipAddress']) ? $paymentInitializeParam['ipAddress'] : '185.137.215.118'; + + } + + private function makeRequest($type, $method, $payloadData = []) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['Authorization'] = 'Basic ' . base64_encode($this->userName . ':' . $this->password); + $requestParams['headers']['Content-Type'] = 'application/json'; + + if (!empty($payloadData)) { + $requestParams['body'] = json_encode($payloadData); + } + + $result = $this->restClient->request($type, $this->requestUrl . '/' . $method, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if ($getResponseData) { + $response = [ + 'status' => true, + 'serviceResponse' => $getResponseData + ]; + } + + } catch (RequestException $e) { + + $message = null; + if ($e->hasResponse()) { + $responseError = $e->getResponse(); + $responseErrorBody = $responseError->getBody()->getContents(); + $responseErrorArray = json_decode($responseErrorBody, true); + if (isset($responseErrorArray['failure_message'])) { + $message = $responseErrorArray['failure_message']; + } + } else { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + } + + $response['message'] = $message; + Log::debug($message); + + } catch (ClientException | ServerException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + $response['message'] = $message; + Log::debug($message); + + } + + if (!$response['status']) { + Log::error($method); + Log::error($payloadData); + Log::error($response); + } + + return $response; + + } + + public function ordersAuthorize($param) + { + + $response = ['status' => false, 'message' => '']; + try { + + $param['creditCard']['installment'] = $param['creditCard']['installment'] == 0 ? 1 : $param['creditCard']['installment']; + + $method = 'orders/authorize'; + $payloadData = [ + 'amount' => $param['amount'], + 'pan' => $param['creditCard']['number'], + 'card' => [ + 'cvv' => $param['creditCard']['cvv'], + 'holder' => $param['creditCard']['holderName'], + 'expiration_month' => $param['creditCard']['expiryMonth'], + 'expiration_year' => $param['creditCard']['expiryYear'], + ], + 'client' => [ + 'name' => $param['client']['name'], + 'email' => $param['client']['email'], + 'phone' => $param['client']['phone'], + ], + 'location' => [ + 'ip' => $this->ipAddress + ], + 'currency' => $param['currencyCode'], + 'merchant_order_id' => $param['orderId'], + 'description' => 'Order Code: ' . $param['orderCode'], + 'options' => [ + 'force3d' => 1, + 'return_url' => $param['paymentCheckUrl'], + 'secure3d20_return_url' => $param['paymentCheckUrl'], + ] + + ]; + + + \Illuminate\Support\Facades\Log::debug(json_encode($payloadData)); + + $checkRequest = $this->makeRequest('POST', $method, $payloadData); + + //dd($checkRequest); + + if (!$checkRequest['status']) { + throw new ApiErrorException($checkRequest['message']); + } + + $serviceResponse = $checkRequest['serviceResponse']; + $serviceResponseOrder = reset($serviceResponse['orders']); + + $response = [ + 'status' => true, + 'data' => $serviceResponseOrder + ]; + + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + return $response; + + } + + public function orderCharge($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $method = 'orders/' . $orderId . '/charge'; + $orderCharge = $this->makeRequest('POST', $method); + + if (!$orderCharge['status']) { + throw new ApiErrorException($orderCharge['message']); + } + + + $serviceResponse = $orderCharge['serviceResponse']; + $serviceResponseOrder = reset($serviceResponse['orders']); + + + if ($serviceResponseOrder['status'] != 'charged') { + if (isset($serviceResponseOrder['failure_message'])) { + throw new ApiErrorException($serviceResponseOrder['failure_message']); + } else { + throw new ApiErrorException('authorized, but not charged'); + } + + } + + $response = [ + 'status' => true, + 'data' => $serviceResponseOrder + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($checkStatusRequest['serviceResponse'])) { + $response['serviceResponse'] = $checkStatusRequest['serviceResponse']; + } + + return $response; + + } + + public function checkPaymentStatus($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $method = 'orders/' . $orderId; + $checkStatusRequest = $this->makeRequest('GET', $method); + + if (!$checkStatusRequest['status']) { + throw new ApiErrorException($checkStatusRequest['message']); + } + + $serviceResponse = $checkStatusRequest['serviceResponse']; + $serviceResponseOrder = reset($serviceResponse['orders']); + + + if ($serviceResponseOrder['status'] != 'charged') { + if (isset($serviceResponseOrder['failure_message'])) { + throw new ApiErrorException($serviceResponseOrder['failure_message']); + } else { + throw new ApiErrorException('authorized, but not charged'); + } + + } + + $response = [ + 'status' => true, + 'data' => $serviceResponseOrder + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($checkStatusRequest['serviceResponse'])) { + $response['serviceResponse'] = $checkStatusRequest['serviceResponse']; + } + + return $response; + + } + +} diff --git a/app/Core/Payment/Sipay/Sipay.php b/app/Core/Payment/Sipay/Sipay.php new file mode 100644 index 0000000..9355856 --- /dev/null +++ b/app/Core/Payment/Sipay/Sipay.php @@ -0,0 +1,253 @@ +restClient = new Client(); + + $this->requestUrl = 'https://app.sipay.com.tr/ccpayment'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://provisioning.sipay.com.tr/ccpayment'; + } + + + $this->merchantId = $paymentInitializeParam['merchantId']; + $this->merchantKey = $paymentInitializeParam['merchantKey']; + $this->appKey = $paymentInitializeParam['appKey']; + $this->appSecret = $paymentInitializeParam['appSecret']; + + $this->getAccessToken = $this->getAccessToken(); + + } + + private function makeRequest($method, $payloadData) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['Content-Type'] = 'application/json'; + if ($method != 'api/token') { + $requestParams['headers']['Authorization'] = 'Bearer ' . $this->getAccessToken; + } + + + $requestParams['body'] = json_encode($payloadData); + + $result = $this->restClient->post($this->requestUrl . '/' . $method, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if ($getResponseData['status_code'] == 100) { + $response = [ + 'status' => true, + 'serviceResponse' => $getResponseData + ]; + } else { + $response['message'] = $getResponseData['status_description']; + $response['serviceResponse'] = $getResponseData; + } + + + } catch (ClientException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + Log::error($method); + Log::error($payloadData); + Log::error($response); + } + + return $response; + + } + + private function getAccessToken() + { + + $method = 'api/token'; + $payloadData = [ + 'app_id' => $this->appKey, + 'app_secret' => $this->appSecret, + ]; + + $getTokenDataRequest = $this->makeRequest($method, $payloadData); + + if ($getTokenDataRequest['status']) { + $getTokenData = $getTokenDataRequest['serviceResponse']['data']['token']; + } + + return $getTokenData; + } + + public function generateHashKey($total, $installment, $currency_code, $invoice_id) + { + + $data = $total . '|' . $installment . '|' . $currency_code . '|' . $this->merchantKey . '|' . $invoice_id; + + $iv = substr(sha1(mt_rand()), 0, 16); + $password = sha1($this->appSecret); + + $salt = substr(sha1(mt_rand()), 0, 4); + $saltWithPassword = hash('sha256', $password . $salt); + + $encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv); + + $msg_encrypted_bundle = "$iv:$salt:$encrypted"; + $msg_encrypted_bundle = str_replace('/', '__', $msg_encrypted_bundle); + + return $msg_encrypted_bundle; + } + + public function generateRefundHashKey($invoice_id, $merchant_key, $app_secret) + { + $data = $invoice_id . '|' . $merchant_key; + $iv = substr(sha1(mt_rand()), 0, 16); + $password = sha1($app_secret); + $salt = substr(sha1(mt_rand()), 0, 4); + $saltWithPassword = hash('sha256', $password . $salt); + $encrypted = openssl_encrypt( + $data, 'aes-256-cbc', "$saltWithPassword", null, $iv + ); + $msg_encrypted_bundle = "$iv:$salt:$encrypted"; + $hash_key = str_replace('/', '__', $msg_encrypted_bundle); + return $hash_key; + } + + public function paySmart3D($param) + { + + $response = ['status' => false, 'message' => '']; + try { + + $param['creditCard']['installment'] = $param['creditCard']['installment'] == 0 ? 1 : $param['creditCard']['installment']; + + $generateHashKey = $this->generateHashKey($param['amount'], $param['creditCard']['installment'], $param['currencyCode'], $param['orderId']); + + $items = []; + $items[] = [ + 'name' => 'Booking', + 'price' => $param['amount'], + 'quantity' => 1, + 'description' => $param['orderCode'], + ]; + + $payment3dFormData['gateway'] = $this->requestUrl . '/api/paySmart3D'; + + $payment3dFormData['inputs'] = [ + 'currency_code' => $param['currencyCode'], + 'installments_number' => $param['creditCard']['installment'], + 'invoice_id' => $param['orderId'], + 'invoice_description' => $param['orderCode'], + 'total' => $param['amount'], + 'merchant_key' => $this->merchantKey, + 'items' => json_encode($items), + //'name' => $param['name'], + //'surname' => $param['surname'], + 'hash_key' => $generateHashKey, + 'return_url' => $param['paymentCheckUrl'], + 'cancel_url' => $param['paymentCheckUrl'], + 'cc_holder_name' => $param['creditCard']['holderName'], + 'cc_no' => $param['creditCard']['number'], + 'expiry_month' => $param['creditCard']['expiryMonth'], + 'expiry_year' => $param['creditCard']['expiryYear'], + 'cvv' => $param['creditCard']['cvv'], + 'transaction_type' => 'Auth', + 'is_comission_from_user' => '2', + 'response_method' => 'GET', + ]; + + $response = [ + 'status' => true, + 'data' => $payment3dFormData + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + return $response; + + } + + public function checkPaymentStatus($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $generateHashKey = $this->generateRefundHashKey($orderId, $this->merchantKey, $this->appSecret); + + $method = 'api/checkstatus'; + $payloadData = [ + 'invoice_id' => $orderId, + 'merchant_key' => $this->merchantKey, + 'include_pending_status' => true, + 'hash_key' => $generateHashKey + ]; + + $checkstatusRequest = $this->makeRequest($method, $payloadData); + + if (!$checkstatusRequest['status']) { + throw new ApiErrorException($checkstatusRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $checkstatusRequest['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($checkstatusRequest['serviceResponse'])) { + $response['serviceResponse'] = $checkstatusRequest['serviceResponse']; + } + + return $response; + + } + +} diff --git a/app/Core/Payment/TBCBankGeorgia/TBCBankGeorgia.php b/app/Core/Payment/TBCBankGeorgia/TBCBankGeorgia.php new file mode 100644 index 0000000..08db9cf --- /dev/null +++ b/app/Core/Payment/TBCBankGeorgia/TBCBankGeorgia.php @@ -0,0 +1,230 @@ +restClient = new Client(['http_errors' => false]); + + $this->requestUrl = 'https://api.tbcbank.ge/v1'; + + $this->clientId = $paymentInitializeParam['clientId']; + $this->clientSecret = $paymentInitializeParam['clientSecret']; + $this->apiKey = '3pkukzNSrBd5G7BbgG2ksu4gCzgZGkGy'; + + $tbcBankTokenCacheKey = 'TBCBankGeorgia'; + $accessToken = Cache::get($tbcBankTokenCacheKey); + if (empty($accessToken)) { + $accessToken = $this->getAccessToken(); + if ($accessToken['status']) { + $accessToken = $accessToken['token']; + } + Cache::put($tbcBankTokenCacheKey, $accessToken, 60 * 60 * 23); // 23h + } + + $this->accessToken = $accessToken; + + } + + private function makeRequest($method, $payloadData, $methodType = 'POST') + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['apikey'] = $this->apiKey; + $requestParams['headers']['Content-Type'] = 'application/json'; + $requestParams['headers']['Authorization'] = 'Bearer ' . $this->accessToken; + $requestParams['body'] = json_encode($payloadData); + + $result = $this->restClient->request($methodType, $this->requestUrl . '/' . $method, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if (isset($getResponseData['httpStatusCode']) && $getResponseData['httpStatusCode'] == 200) { + + $response = [ + 'status' => true, + 'serviceResponse' => $getResponseData + ]; + + } else { + throw new Exception($getResponseData['status'] . ': ' . $getResponseData['detail']); + } + + + } catch (ClientException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } + + if (!$response['status']) { + Log::error($method); + Log::error($payloadData); + Log::error($response); + } + + return $response; + + } + + private function getAccessToken() + { + + $getTokenData = null; + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['apikey'] = $this->apiKey; + $requestParams['headers']['Content-Type'] = 'application/x-www-form-urlencoded'; + + $requestParams['form_params']['client_id'] = $this->clientId; + $requestParams['form_params']['client_secret'] = $this->clientSecret; + + + $result = $this->restClient->request('POST', $this->requestUrl . '/tpay/access-token', $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if (isset($getResponseData['error_code'])) { + throw new ApiErrorException($getResponseData['error_message']); + } + + $response['status'] = true; + $response['token'] = $getResponseData['access_token']; + + } catch (ApiErrorException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function paymentInitialize($param) + { + + $response = ['status' => false, 'message' => '']; + try { + + //5, Pan (Payment with card) + //7, Internet Bank Login + //9, Apple Pay + //14 Google Pay + + $localeLanguageCode = app('translator')->getLocale(); + $paymentInitializeParam = [ + 'amount' => [ + 'currency' => $param['currencyCode'], + 'total' => $param['amount'] + ], + 'returnurl' => $param['paymentCheckUrl'], + 'userIpAddress' => $param['ipAddress'], + 'expirationMinutes' => 5, + 'methods' => [5, 7, 9, 14], + 'preAuth' => false, + 'language' => $localeLanguageCode == 'ka' ? 'KA' : 'EN', + 'merchantPaymentId' => $param['orderId'], + ]; + + $paymentInitialize = $this->makeRequest('tpay/payments', $paymentInitializeParam); + + if (!$paymentInitialize['status']) { + throw new ApiErrorException($paymentInitialize['message']); + } + + $response = [ + 'status' => true, + 'data' => $paymentInitialize['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + return $response; + + } + + public function checkoutOrderInfo($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $method = 'tpay/payments/' . $orderId; + $payloadData = []; + + $checkoutOrderInfoRequest = $this->makeRequest($method, $payloadData, 'GET'); + + if (!$checkoutOrderInfoRequest['status']) { + throw new ApiErrorException($checkoutOrderInfoRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $checkoutOrderInfoRequest['serviceResponse'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($checkoutOrderInfoRequest['serviceResponse'])) { + $response['serviceResponse'] = $checkoutOrderInfoRequest['serviceResponse']; + } + + return $response; + + } + +} diff --git a/app/Core/Payment/WeePay/WeePay.php b/app/Core/Payment/WeePay/WeePay.php new file mode 100644 index 0000000..f554acd --- /dev/null +++ b/app/Core/Payment/WeePay/WeePay.php @@ -0,0 +1,283 @@ +restClient = new Client(); + + $this->requestUrl = 'https://api.weepay.co'; + if ($paymentInitializeParam['env'] == 'test') { + $this->requestUrl = 'https://testapi.weepay.co'; + } + + + $this->merchantId = $paymentInitializeParam['merchantId']; + $this->apiKey = $paymentInitializeParam['apiKey']; + $this->secretKey = $paymentInitializeParam['secretKey']; + $this->ipAddress = isset($paymentInitializeParam['ipAddress']) ? $paymentInitializeParam['ipAddress'] : '185.137.215.118'; + + $this->currencyMapping = [ + 'TRY' => 'TL', + 'USD' => 'USD', + 'EUR' => 'EUR', + 'GBP' => 'GBP', + ]; + + } + + private function makeRequest($method, $payloadData) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParams['headers']['Content-Type'] = 'application/json'; + + $requestParams['body'] = json_encode($payloadData); + + $result = $this->restClient->post($this->requestUrl . '/' . $method, $requestParams); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody, 1) : []; + + if ($getResponseData['status'] == 'success') { + $response = [ + 'status' => true, + 'serviceResponse' => $getResponseData + ]; + } + + } catch (ClientException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + $getResponseErrorBody = $e->getResponse()->getBody()->getContents(); + $getResponseError = $getResponseErrorBody ? json_decode($getResponseErrorBody, 1) : []; + + + $errorList = []; + if(is_array($getResponseError['message'])) { + foreach ($getResponseError['message'] as $errorKey => $error) { + $errorList[] = implode(', ', $error); + } + } else { + $errorList[] = $getResponseError['message']; + } + + $message = implode(', ', $errorList); + $response['message'] = $message; + Log::debug($message); + + } catch (ServerException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::debug($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + Log::error($method); + Log::error($payloadData); + Log::error($response); + } + + return $response; + + } + + + public function generateHashKey($total, $installment, $currency_code, $invoice_id) + { + + $data = $total . '|' . $installment . '|' . $currency_code . '|' . $this->merchantKey . '|' . $invoice_id; + + $iv = substr(sha1(mt_rand()), 0, 16); + $password = sha1($this->appSecret); + + $salt = substr(sha1(mt_rand()), 0, 4); + $saltWithPassword = hash('sha256', $password . $salt); + + $encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv); + + $msg_encrypted_bundle = "$iv:$salt:$encrypted"; + $msg_encrypted_bundle = str_replace('/', '__', $msg_encrypted_bundle); + + return $msg_encrypted_bundle; + } + + public function generateRefundHashKey($invoice_id, $merchant_key, $app_secret) + { + $data = $invoice_id . '|' . $merchant_key; + $iv = substr(sha1(mt_rand()), 0, 16); + $password = sha1($app_secret); + $salt = substr(sha1(mt_rand()), 0, 4); + $saltWithPassword = hash('sha256', $password . $salt); + $encrypted = openssl_encrypt( + $data, 'aes-256-cbc', "$saltWithPassword", null, $iv + ); + $msg_encrypted_bundle = "$iv:$salt:$encrypted"; + $hash_key = str_replace('/', '__', $msg_encrypted_bundle); + return $hash_key; + } + + public function paySmart3D($param) + { + + $response = ['status' => false, 'message' => '']; + try { + + $param['creditCard']['installment'] = $param['creditCard']['installment'] == 0 ? 1 : $param['creditCard']['installment']; + + $items = []; + $items[] = [ + 'productId' => $param['orderCode'], + 'name' => 'Booking', + 'productPrice' => $param['amount'], + 'itemType' => 'VIRTUAL', + ]; + + $method = 'Payment/PaymentRequestThreeD'; + $payloadData = [ + 'Auth' => [ + 'bayiId' => $this->merchantId, + 'apiKey' => $this->apiKey, + 'secretKey' => $this->secretKey, + ], + 'Data' => [ + 'orderId' => $param['orderId'], + 'currency' => isset($this->currencyMapping[$param['currencyCode']]) ? $this->currencyMapping[$param['currencyCode']] : $param['currencyCode'], + 'locale' => 'tr', + 'paidPrice' => $param['amount'], + 'ipAddress' => $this->ipAddress, + 'cardHolderName' => $param['creditCard']['holderName'], + 'cardNumber' => $param['creditCard']['number'], + 'expireMonth' => $param['creditCard']['expiryMonth'], + 'expireYear' => mb_substr($param['creditCard']['expiryYear'], 2, 2), + 'cvcNumber' => $param['creditCard']['cvv'], + 'installmentNumber' => $param['creditCard']['installment'], + 'description' => $param['orderCode'], + 'callBackUrl' => $param['paymentCheckUrl'] + ], + 'Customer' => [ + 'customerId' => $param['orderId'], + 'customerName' => $param['orderId'], + 'customerSurname' => $param['orderId'], + 'gsmNumber' => $param['orderId'], + 'email' => 'support@extranetwork.com', + 'identityNumber' => '11111111111', + 'city' => $param['orderId'], + 'country' => $param['orderId'], + ], + 'BillingAddress' => [ + 'contactName' => $param['orderId'], + 'address' => $param['orderId'], + 'city' => $param['orderId'], + 'country' => $param['orderId'], + ], + 'Products' => $items + + ]; + + $checkRequest = $this->makeRequest($method, $payloadData); + + if (!$checkRequest['status']) { + throw new ApiErrorException($checkRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $checkRequest['serviceResponse'] + ]; + + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + return $response; + + } + + public function checkPaymentStatus($orderId) + { + + $response = ['status' => false, 'message' => '']; + try { + + $method = 'GetPayment/Detail'; + $payloadData = [ + 'Auth' => [ + 'bayiId' => $this->merchantId, + 'apiKey' => $this->apiKey, + 'secretKey' => $this->secretKey, + ], + 'Data' => [ + 'ipAddress' => $this->ipAddress, + 'orderId' => $orderId, + 'locale' => 'tr' + ] + ]; + + $checkStatusRequest = $this->makeRequest($method, $payloadData); + + if (!$checkStatusRequest['status']) { + throw new ApiErrorException($checkStatusRequest['message']); + } + + if ($checkStatusRequest['serviceResponse']['paymentStatus'] != 'SUCCESS') { + throw new ApiErrorException($checkStatusRequest['message']); + } + + $response = [ + 'status' => true, + 'data' => $checkStatusRequest['serviceResponse']['data'] + ]; + + } catch (ApiErrorException $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + } catch (Exception $e) { + $response = ['status' => false, 'message' => $e->getMessage()]; + Log::error($response); + } + + if (isset($checkStatusRequest['serviceResponse'])) { + $response['serviceResponse'] = $checkStatusRequest['serviceResponse']; + } + + return $response; + + } + +} diff --git a/app/Core/Permission/AbstractRelatedPermission.php b/app/Core/Permission/AbstractRelatedPermission.php new file mode 100644 index 0000000..f6392ec --- /dev/null +++ b/app/Core/Permission/AbstractRelatedPermission.php @@ -0,0 +1,153 @@ +userService = $userService; + $this->permissionGroupUserMappingService = $permissionGroupUserMappingService; + $this->permissionGroupService = $permissionGroupService; + $this->permissionService = $permissionService; + } + + protected function checkIfUserIdSet () + { + if ($this->userData) + { + return $this->userData; + }else + { + throw new Exception("You need to call 'setUser' function first"); + } + } + + + public function setUserWithPermissionId ( $userId,$propertyId,$permission_id ) + { + $permission_id = $permission_id ? $permission_id : 0; + return $this->setUserData ($userId,$propertyId,$permission_id ); + } + + + private function setUserData ($userId,$propertyId,$permission_id ) + { + $userCriteria = + [ + "criteria"=> + [ + ["field"=>"id","condition"=>"=","value"=>$userId], + ["field"=>"status","condition"=>"=","value"=>1] + ], + "addAppends"=>["isPropertyAdmin"], + "firstRow"=>true + ]; + $userData = $this->userService->getUserList($userCriteria); + if(!$userData) + { + throw new Exception("User Not Found"); + } + + + $isUserRelatedActiveProperty = $this->userService->isUserRelatedActiveProperty($userId,$propertyId); + if ( !$isUserRelatedActiveProperty) + { + throw new Exception("User : ".$userId." Has No Relation With Property : ".$propertyId); + } + + $permissionCriteria = + [ + "criteria"=> + [ + ["field"=>"id","condition"=>"=","value"=>$permission_id] + ], + "with"=>["permissionGroupMapping"], + "firstRow"=>true + ]; + + $permissionData = $this->permissionService->findByCriteria($permissionCriteria); + $permissionData = $permissionData ? $permissionData : []; + + if ( !$permissionData && (!$userData["isSuperUser"] && !$userData["isPropertyAdmin"])) + { + throw new Exception("PermissionId Has Not Found : ".json_encode($permission_id)); + } + + $permissionGroupInfo = fillOnUndefined($permissionData,"permission_group_mapping"); + + $userData["property_id"] = $isUserRelatedActiveProperty["property_id"]; + $userData["property"] = $isUserRelatedActiveProperty["property"]; + + $this->userData = $userData; + $this->userData["relatedParameters"] = []; + $this->userData["selectedGroupMapping"] = []; + try + { + if ($this->userData[ "isSuperUser" ]) + { + return false; + } + $this->userData["is_admin"] = false; + $userRelatedParametersCriteria = + [ + "criteria"=> + [ + ["field"=>"user_id","condition"=>"=","value"=>$userId], + ["field"=>"property_id","condition"=>"=","value"=>$propertyId], + ], + "with"=>["permissionGroup"], + ]; + $userRelatedParams = $this->permissionGroupUserMappingService->findByCriteria($userRelatedParametersCriteria); + $this->userData["userAllRelatedParams"] = $userRelatedParams; + $this->userData["permission_group"] = []; + foreach ($userRelatedParams as $perParam) + { + if ($perParam["permission_group"]) + { + $this->userData["permission_group"][] = $perParam["permission_group"]; + } + + if ($perParam["permission_group"]["is_admin"]) + { + $this->userData["is_admin"] = true; + $this->userData["selectedGroupMapping"] = $perParam; + return false; + } + + if ($permissionGroupInfo && $perParam["permission_group"]["id"] == $permissionGroupInfo["permission_group_id"]) + { + $this->userData["relatedParameters"] = $perParam["relatedParametersArray"]; + $this->userData["selectedGroupMapping"] = $perParam; + } + } + + } catch ( Exception $e ) + { + return false; + } + } +} diff --git a/app/Core/Permission/RoutePermissionAuthorize.php b/app/Core/Permission/RoutePermissionAuthorize.php new file mode 100644 index 0000000..d64440e --- /dev/null +++ b/app/Core/Permission/RoutePermissionAuthorize.php @@ -0,0 +1,171 @@ +permissionGroupUserMappingService = $permissionGroupUserMappingService; + $this->userService = $userService; + $this->permissionService = $permissionService; + $this->request = $request; + $this->currentRoute = $this->request->route(); + } + + + public function setCurrentRoute ( Route $route ) + { + $this->currentRoute = $route; + $this->routePermissionData = $this->getRoutePermissionData(); + } + + protected function getRouteAliasName(){ + + $url = collect($this->request->route()); + $getNameArray = $url->where('as' , '!=', null)->first(); + return fillOnUndefined($getNameArray, 'as'); + + } + + protected function getRoutePermissionData () + { + + if ( !$this->currentRoute) + { + throw new Exception("Current Route is Null"); + } + + if ($this->routePermissionData) + { + return $this->routePermissionData; + } + + $criteria = + [ + "criteria" => + [ + [ "field" => "code", "condition" => "=", "value" => $this->getRouteAliasName() ] + ], + "with" => [ "permissionGroupMapping.permissionGroup"], + "firstRow" => 1 + ]; + $result = $this->permissionService->findByCriteria ( $criteria ); + return $result; + } + + public function getRoutePermissionGroupId () + { + return fillOnUndefined($this->getRoutePermissionData(),"permission_group_mapping.permission_group.id"); + } + + public function isUserAuthorizedForCurrentRoute ( $params ) + { + try + { + $user_id = $params['user_id'] ; + $property_id = $params['property_id'] ; + + $userCriteria = + [ + "criteria"=> + [ + ["field"=>"id","condition"=>"=","value"=>$user_id], + ["field"=>"status","condition"=>"=","value"=>1] + ], + "firstRow"=>1 + ]; + + $userData = $this->userService->select($userCriteria); + + if ( !$userData['data'] ) + { + return false; + } + + $userData = $userData['data'] ; + + + if ($userData["user_type"] == 1) + { + return true; + } + + $userGroupCriteria = + [ + "criteria" => + [ + ["field" => "user_id", "condition" => "=", "value" => $user_id], + ["field" => "property_id", "condition" => "=", "value" => $property_id], + ["field" => "status", "condition" => "=", "value" => 1] + ], + "with" => ["permissionGroup", "permissionGroupMapping"] + ]; + + $userGroups = $this->permissionGroupUserMappingService->findByCriteria($userGroupCriteria); + + + if ( !$userGroups) + { + return false; + } + + foreach ($userGroups as $perGroup) + { + if(isset($perGroup["permission_group"]["is_admin"]) && $perGroup["permission_group"]["is_admin"]) + { + return true; + } + } + + + if ( !$routePermissionData = $this->getRoutePermissionData()) + { + return false; + } + + + foreach ($userGroups as $perGroup) + { + foreach ($perGroup["permission_group_mapping"] as $perPermissionGroup) + { + if($routePermissionData["id"] == $perPermissionGroup["permission_id"]) + { + return true; + } + } + } + return false; + + } catch ( Exception $e ) + { + $message = $e->getFile()." ".$e->getLine()." ".$e->getMessage(); + Log::error($message); + return false; + } + } + +} \ No newline at end of file diff --git a/app/Core/Repository/ApiAccessToken/ApiAccessTokenRepository.php b/app/Core/Repository/ApiAccessToken/ApiAccessTokenRepository.php new file mode 100644 index 0000000..79f1195 --- /dev/null +++ b/app/Core/Repository/ApiAccessToken/ApiAccessTokenRepository.php @@ -0,0 +1,19 @@ +apiAccessToken = $apiAccessToken; + $this->defaultModel = $this->apiAccessToken; + } + +} diff --git a/app/Core/Repository/ApplicationCache/ApplicationCacheRepository.php b/app/Core/Repository/ApplicationCache/ApplicationCacheRepository.php new file mode 100644 index 0000000..0cc2ba6 --- /dev/null +++ b/app/Core/Repository/ApplicationCache/ApplicationCacheRepository.php @@ -0,0 +1,57 @@ +cache = $cache; + $this->carbon = $carbon; + + } + + private function createCacheStoreKey ( $cacheId ) + { + return $this->cachePrefix.$cacheId; + } + + public function storeCache($cacheId , $param){ + $result = $param; + $result["cacheId"] = $cacheId; + $cacheSignature = $this->createCacheStoreKey($cacheId); + $expiresAt = $this->carbon->addMinutes($this->cacheLife/60)->toDateTimeString(); + + $this->cache->put($cacheSignature, $result, $this->cacheLife); + + return [ + 'cacheId' => $cacheId, + 'expiresAt' => $expiresAt + ]; + + } + + public function getCache($cacheId){ + + $cacheId = $this->createCacheStoreKey($cacheId); + $cacheInfo = $this->cache->get($cacheId); + return $cacheInfo; + + } + +} \ No newline at end of file diff --git a/app/Core/Repository/AwardsCertificateCategory/AwardsCertificateCategoryRepository.php b/app/Core/Repository/AwardsCertificateCategory/AwardsCertificateCategoryRepository.php new file mode 100644 index 0000000..c219501 --- /dev/null +++ b/app/Core/Repository/AwardsCertificateCategory/AwardsCertificateCategoryRepository.php @@ -0,0 +1,19 @@ +awardsCertificateCategory = $awardsCertificateCategory; + $this->defaultModel = $this->awardsCertificateCategory; + } + +} diff --git a/app/Core/Repository/Booking/BookingAddonRepository.php b/app/Core/Repository/Booking/BookingAddonRepository.php new file mode 100644 index 0000000..66a9cf7 --- /dev/null +++ b/app/Core/Repository/Booking/BookingAddonRepository.php @@ -0,0 +1,19 @@ +bookingAddon = $bookingAddon; + $this->defaultModel = $this->bookingAddon; + } + +} diff --git a/app/Core/Repository/Booking/BookingContactRepository.php b/app/Core/Repository/Booking/BookingContactRepository.php new file mode 100644 index 0000000..af70bdc --- /dev/null +++ b/app/Core/Repository/Booking/BookingContactRepository.php @@ -0,0 +1,19 @@ +bookingContact = $bookingContact; + $this->defaultModel = $this->bookingContact; + } + +} diff --git a/app/Core/Repository/Booking/BookingPaymentDataCheckRepository.php b/app/Core/Repository/Booking/BookingPaymentDataCheckRepository.php new file mode 100644 index 0000000..ea42248 --- /dev/null +++ b/app/Core/Repository/Booking/BookingPaymentDataCheckRepository.php @@ -0,0 +1,19 @@ +bookingPaymentDataCheck = $bookingPaymentDataCheck; + $this->defaultModel = $this->bookingPaymentDataCheck; + } + +} diff --git a/app/Core/Repository/Booking/BookingPaymentDataRepository.php b/app/Core/Repository/Booking/BookingPaymentDataRepository.php new file mode 100644 index 0000000..de892c1 --- /dev/null +++ b/app/Core/Repository/Booking/BookingPaymentDataRepository.php @@ -0,0 +1,19 @@ +bookingPaymentData = $bookingPaymentData; + $this->defaultModel = $this->bookingPaymentData; + } + +} diff --git a/app/Core/Repository/Booking/BookingPaymentRepository.php b/app/Core/Repository/Booking/BookingPaymentRepository.php new file mode 100644 index 0000000..0b7abef --- /dev/null +++ b/app/Core/Repository/Booking/BookingPaymentRepository.php @@ -0,0 +1,19 @@ +bookingPayment = $bookingPayment; + $this->defaultModel = $this->bookingPayment; + } + +} diff --git a/app/Core/Repository/Booking/BookingRepository.php b/app/Core/Repository/Booking/BookingRepository.php new file mode 100644 index 0000000..ce412c5 --- /dev/null +++ b/app/Core/Repository/Booking/BookingRepository.php @@ -0,0 +1,19 @@ +booking = $booking; + $this->defaultModel = $this->booking; + } + +} diff --git a/app/Core/Repository/Booking/BookingRoomPaxRepository.php b/app/Core/Repository/Booking/BookingRoomPaxRepository.php new file mode 100644 index 0000000..e97900d --- /dev/null +++ b/app/Core/Repository/Booking/BookingRoomPaxRepository.php @@ -0,0 +1,19 @@ +bookingRoomPax = $bookingRoomPax; + $this->defaultModel = $this->bookingRoomPax; + } + +} diff --git a/app/Core/Repository/Booking/BookingRoomRepository.php b/app/Core/Repository/Booking/BookingRoomRepository.php new file mode 100644 index 0000000..fc7d47e --- /dev/null +++ b/app/Core/Repository/Booking/BookingRoomRepository.php @@ -0,0 +1,19 @@ +bookingRoom = $bookingRoom; + $this->defaultModel = $this->bookingRoom; + } + +} diff --git a/app/Core/Repository/Booking/BookingStatusRepository.php b/app/Core/Repository/Booking/BookingStatusRepository.php new file mode 100644 index 0000000..03cb0bf --- /dev/null +++ b/app/Core/Repository/Booking/BookingStatusRepository.php @@ -0,0 +1,19 @@ +bookingStatus = $bookingStatus; + $this->defaultModel = $this->bookingStatus; + } + +} diff --git a/app/Core/Repository/Booking/BookingTicketRepository.php b/app/Core/Repository/Booking/BookingTicketRepository.php new file mode 100644 index 0000000..175d527 --- /dev/null +++ b/app/Core/Repository/Booking/BookingTicketRepository.php @@ -0,0 +1,19 @@ +bookingTicket = $bookingTicket; + $this->defaultModel = $this->bookingTicket; + } + +} diff --git a/app/Core/Repository/Booking/PropertyChannelCouponRepository.php b/app/Core/Repository/Booking/PropertyChannelCouponRepository.php new file mode 100644 index 0000000..74aac61 --- /dev/null +++ b/app/Core/Repository/Booking/PropertyChannelCouponRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/ChannelManager/ChannelManagerRepository.php b/app/Core/Repository/ChannelManager/ChannelManagerRepository.php new file mode 100644 index 0000000..e2d95ec --- /dev/null +++ b/app/Core/Repository/ChannelManager/ChannelManagerRepository.php @@ -0,0 +1,19 @@ +channelManager = $channelManager; + $this->defaultModel = $this->channelManager; + } + +} diff --git a/app/Core/Repository/ChannelManagerBooking/ChannelManagerBookingRepository.php b/app/Core/Repository/ChannelManagerBooking/ChannelManagerBookingRepository.php new file mode 100644 index 0000000..2b35fa1 --- /dev/null +++ b/app/Core/Repository/ChannelManagerBooking/ChannelManagerBookingRepository.php @@ -0,0 +1,19 @@ +channelManagerBooking = $channelManagerBooking; + $this->defaultModel = $this->channelManagerBooking; + } + +} diff --git a/app/Core/Repository/ChannelManagerLog/ChannelManagerLogRepository.php b/app/Core/Repository/ChannelManagerLog/ChannelManagerLogRepository.php new file mode 100644 index 0000000..f7dda73 --- /dev/null +++ b/app/Core/Repository/ChannelManagerLog/ChannelManagerLogRepository.php @@ -0,0 +1,19 @@ +channelManagerLog = $channelManagerLog; + $this->defaultModel = $this->channelManagerLog; + } + +} diff --git a/app/Core/Repository/ChannelManagerMapping/ChannelManagerMappingRepository.php b/app/Core/Repository/ChannelManagerMapping/ChannelManagerMappingRepository.php new file mode 100644 index 0000000..457f901 --- /dev/null +++ b/app/Core/Repository/ChannelManagerMapping/ChannelManagerMappingRepository.php @@ -0,0 +1,19 @@ +channelManagerMapping = $channelManagerMapping; + $this->defaultModel = $this->channelManagerMapping; + } + +} diff --git a/app/Core/Repository/ChannelManagerPropertyMapping/ChannelManagerPropertyMappingRepository.php b/app/Core/Repository/ChannelManagerPropertyMapping/ChannelManagerPropertyMappingRepository.php new file mode 100644 index 0000000..4937917 --- /dev/null +++ b/app/Core/Repository/ChannelManagerPropertyMapping/ChannelManagerPropertyMappingRepository.php @@ -0,0 +1,19 @@ +channelManagerPropertyMapping = $channelManagerPropertyMapping; + $this->defaultModel = $this->channelManagerPropertyMapping; + } + +} diff --git a/app/Core/Repository/ChannelManagerPropertyRateMapping/ChannelManagerPropertyRateMappingRepository.php b/app/Core/Repository/ChannelManagerPropertyRateMapping/ChannelManagerPropertyRateMappingRepository.php new file mode 100644 index 0000000..2e2beb1 --- /dev/null +++ b/app/Core/Repository/ChannelManagerPropertyRateMapping/ChannelManagerPropertyRateMappingRepository.php @@ -0,0 +1,19 @@ +channelManagerPropertyRateMapping = $channelManagerPropertyRateMapping; + $this->defaultModel = $this->channelManagerPropertyRateMapping; + } + +} diff --git a/app/Core/Repository/Country/CountryRepository.php b/app/Core/Repository/Country/CountryRepository.php new file mode 100644 index 0000000..0f84bbf --- /dev/null +++ b/app/Core/Repository/Country/CountryRepository.php @@ -0,0 +1,19 @@ +country = $country; + $this->defaultModel = $this->country; + } + +} diff --git a/app/Core/Repository/Currency/CurrencyRepository.php b/app/Core/Repository/Currency/CurrencyRepository.php new file mode 100644 index 0000000..766585e --- /dev/null +++ b/app/Core/Repository/Currency/CurrencyRepository.php @@ -0,0 +1,20 @@ +currency = $currency; + $this->defaultModel = $this->currency; + } +} diff --git a/app/Core/Repository/CurrencyRates/CurrencyRatesRepository.php b/app/Core/Repository/CurrencyRates/CurrencyRatesRepository.php new file mode 100644 index 0000000..e43e82e --- /dev/null +++ b/app/Core/Repository/CurrencyRates/CurrencyRatesRepository.php @@ -0,0 +1,19 @@ +currencyRates = $currencyRates; + $this->defaultModel = $this->currencyRates; + } + +} diff --git a/app/Core/Repository/EleqouentAbstractRepository.php b/app/Core/Repository/EleqouentAbstractRepository.php new file mode 100644 index 0000000..07c050c --- /dev/null +++ b/app/Core/Repository/EleqouentAbstractRepository.php @@ -0,0 +1,522 @@ + "id", "condition" => "=", "value" => $id]; + $param["firstRow"] = true; + + return $this->findByCriteria($model, $param, $column); + } + + protected function all(Model $model) + { + return $this->findByCriteria($model); + } + + private function withCriteria($get, $param) + { + foreach ($param as $key => $value) { + $get->with([$value['method'] => function ($query) use ($value) { + + if (isset($value['orderBy'])) { + foreach ($value['orderBy'] as $orderByKey => $orderByValue) { + $query->orderBy($orderByValue['field'], $orderByValue['value']); + } + } + + if (isset($value['field'])) { + foreach ($value['field'] as $fieldValue) { + $query->where($fieldValue['field'], $fieldValue['condition'], $fieldValue['value']); + } + } + + if (isset($value['whereIn'])) { + foreach ($value['whereIn'] as $fieldValue) { + $query->whereIn($fieldValue['field'], $fieldValue['value']); + } + } + + }]); + } + return $get; + } + + private function withBuild(Builder $builder, array $withParams) + { + + foreach ($withParams as $perParamKey => $perParamValue) { + if (!is_array($perParamValue)) { + $builder->with($perParamValue); + continue; + } else { + $builder->with($perParamKey); + } + + if (isset($perParamValue["with"])) { + $builder = $this->withBuild($builder, $perParamValue); + } + + } + + return $builder; + + } + + protected function findByCriteria(Model $model, $param = null, $column = ['*']) + { + try { + if (isset($param["removeAppends"])) { + $model->removeAppends($param["removeAppends"]); + } else { + $model->removeAppends([]); + } + + if (isset($param["addAppends"])) { + $model->addAppends($param["addAppends"]); + } else { + $model->addAppends([]); + } + + $get = $model->on('mysql'); + + + if (isset($param["selectRaw"])) { + $get = $get->select(DB::raw($param["selectRaw"])); + } + + if (isset($param["criteria"])) { + $get->where( + function ($query) use ($param) { + if (isset($param['whereOr'])) { + foreach ($param['whereOr'] as $key => $value) { + $query->whereOr($value['field'], $value['condition'], $value['value']); + } + } + + if (isset($param['criteria'])) { + foreach ($param['criteria'] as $key => $value) { + $query->where($value['field'], $value['condition'], $value['value']); + } + } + } + ); + } + + if (isset($param['criteriaWhere'])) { + $whereType = fillOnUndefined($param['criteriaWhere'], 'whereType', 'where'); + $get->$whereType ( + function ($query) use ($param) { + foreach ($param['criteriaWhere'] as $criteria) { + $whereType = $criteria['whereType']; + $query->$whereType( + function ($q) use ($criteria) { + foreach ($criteria['params'] as $key => $value) { + if (isset($value['where'])) { + if ($value['where'] == 'OR') { + $q->whereOr($value['field'], $value['condition'], $value['value']); + } else { + $q->where($value['field'], $value['condition'], $value['value']); + } + } + } + } + ); + } + } + ); + } + + if (isset($param["criteriaWhereRaw"])) { + $get->where( + function ($query) use ($param) { + if (isset($param['criteriaWhereRaw'])) { + foreach ($param['criteriaWhereRaw'] as $key => $value) { + $query->whereRaw($value['query'], $value['binds']); + } + } + } + ); + } + + + if (isset($param["whereOr"])) { + $get->where( + function ($query) use ($param) { + if (isset($param['whereOr'])) { + foreach ($param['whereOr'] as $key => $value) { + $query->orWhere($value['field'], $value['condition'], $value['value']); + } + } + + } + ); + } + + if (isset($param['whereIn'])) { + foreach ($param['whereIn'] as $whereInKey => $whereInValue) { + $get->whereIn($whereInValue['field'], $whereInValue['value']); + } + } + + + if (isset($param['whereNotIn'])) { + foreach ($param['whereNotIn'] as $whereInKey => $whereInValue) { + $get->whereNotIn($whereInValue['field'], $whereInValue['value']); + } + } + + + if (isset($param['has'])) { + foreach ($param["has"] as $perHas) { + $get->has($perHas["title"], $perHas["condition"], $perHas["value"]); + } + } + + if (isset($param['whereHas'])) { + foreach ($param['whereHas'] as $key => $value) { + $get->whereHas($value['title'], function ($get) use ($value) { + foreach ($value['criteria']['param'] as $criteria) { + $queryType = $value['criteria']['type']; + + if ($queryType == 'whereIn') { + $get->whereIn($criteria['field'], $criteria['value']); + } else { + $get->$queryType($criteria['field'], $criteria['condition'], $criteria['value']); + } + } + }); + } + } + + /*Ex With Algorithm*/ + if (isset($param['with'])) { + foreach ($param['with'] as $withKey => $withValue) { + $get->with($withValue); + } + } + + if (isset($param['withCriteria'])) { + $get = $this->withCriteria($get, $param['withCriteria']); + } + + if (isset($param['orderBy'])) { + foreach ($param['orderBy'] as $orderByKey => $orderByValue) { + $get->orderBy($orderByValue['field'], $orderByValue['value']); + } + } + + if (isset($param['groupBy'])) { + foreach ($param['groupBy'] as $perOrderBy) { + $get->groupBy($perOrderBy); + } + } + + if (isset($param['skip'])) { + $get->skip($param['skip']); + } + + if (isset($param['take'])) { + $get->take($param['take']); + } + + if (isset($param['sum'])) { + return $get->sum($param['sum']); + } + + if (isset($param['count'])) { + return $get->count(); + } + + if (isset($param["toSql"])) { + return $get->toSql(); + } + $get = isset($param["firstRow"]) ? $get->first($column) : $get->get($column); + if (isset($param['keyBy'])) { + $get = $get->keyBy($param['keyBy']); + } + if (isset($param["getRaw"])) { + return $get; + } + + return ($get) ? $get->toArray() : []; + + } catch (Exception $e) { + Log::error($e->getMessage()); + } + } + + protected function update(Model $model, $id, $param) + { + try { + $update = $model->where('id', $id)->update($param); + + $update = $update ? $this->find($model, $id) : false; + + return output(['data' => $update]); + } catch (Exception $e) { + $message = $e->getMessage(); + + return output(['status' => -1, 'message' => $message]); + } + + } + + protected function updateWhere(Model $model, $value, $param) + { + try { + $update = $model->where( + function ($query) use ($value) { + if (isset($value['criteria'])) { + foreach ($value['criteria'] as $key => $value) { + $query->where($value['field'], $value['condition'], $value['value']); + } + } + } + ); + + if (isset($value['orderBy'])) { + foreach ($value['orderBy'] as $orderByKey => $orderByValue) { + $update->orderBy($orderByValue['field'], $orderByValue['value']); + } + } + + $update->first(); + $update = $update->update($param); + + $update = $update ? true : false; + + return output(['data' => $update]); + } catch (Exception $e) { + $message = $e->getTraceAsString(); + + return output(['status' => -1, 'message' => $message]); + } + + } + + protected function updateWhereIn(Model $model, $id, $param) + { + try { + $update = $model->whereIn('id', $id)->update($param); + $update = $update ? $update : false; + return output(['data' => $update]); + } catch (Exception $e) { + $message = $e->getMessage(); + return output(['status' => -1, 'message' => $message]); + } + } + + protected function updateWhereBulk(Model $model, $value, $param) + { + try { + $update = $model->where( + function ($query) use ($value) { + if (isset($value['criteria'])) { + foreach ($value['criteria'] as $key => $value) { + $query->where($value['field'], $value['condition'], $value['value']); + } + } + } + ); + + if (isset($value['orderBy'])) { + foreach ($value['orderBy'] as $orderByKey => $orderByValue) { + $update->orderBy($orderByValue['field'], $orderByValue['value']); + } + } + + $update = $update->update($param); + + $update = $update ? true : false; + + return output(['data' => $update]); + } catch (Exception $e) { + $message = $e->getTraceAsString(); + + return output(['status' => -1, 'message' => $message]); + } + + } + + protected function create(Model $model, $data) + { + try { + $create = $model->create($data); + + return output(['data' => $create->toArray()]); + } catch (Exception $e) { + $message = $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $message]); + } + } + + protected function insert(Model $model, $data) + { + try { + $create = $model->insert($data); + + return output(['data' => $create]); + } catch (QueryException $e) { + $message = $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $message]); + } catch (Exception $e) { + $message = $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $message]); + } + } + + protected function createAll(Model $model, $data) + { + try { + $create = []; + foreach ($data as $perData) { + $result = $this->create($model, $perData); + + if ($result["status"] != "success") { + throw new Exception($result["message"]); + } + + $create[] = $result["data"]; + } + + return output(['data' => $create]); + } catch (Exception $e) { + $message = $e->getTraceAsString(); + Log::error($message); + return output(['status' => -1, 'message' => $message]); + } + } + + protected function updateOrCreate(Model $model, array $criteria, array $saveData) + { + try { + + + $findRow = $this->findByCriteria + ( + $model, + ["criteria" => $criteria, "firstRow" => 1, "getRaw" => 1] + ); + + + + $model->on("mysql"); + if ($findRow) { + $result = $this->update($model, $findRow["id"], $saveData); + } else { + $result = $this->create($model, $saveData); + } + + return $result; + } catch (Exception $e) { + $message = $e->getTraceAsString(); + Log::error($message); + return output(['status' => -1, 'message' => $message]); + } + } + + protected function delete(Model $model, array $param) + { + try { + $param["removeAppends"] = ["*"]; + $param["with"] = []; + $deleteRows = $this->findByCriteria($model, $param, ["id"]); + + if (!$deleteRows) { + return null; + } + $deleteRows = singleElementArray($deleteRows); + $deleteIds = pickItemFromArray("id", $deleteRows); + $result = $model->whereIn("id", $deleteIds); + if ($result->count() > 0) { + $deletedRows = $result->get()->toArray(); + if ($result->delete()) { + return $deletedRows; + } + } + return null; + } catch (Exception $e) { + $message = $e->getTraceAsString(); + Log::error($message); + return output(['status' => -1, 'message' => $message]); + } + } + + protected function destroy(Model $model, array $param) + { + try { + + if (!$param) { + return null; + } + + $deletedRows = $model->destroy($param); + + return output(['data' => $deletedRows]);; + } catch (Exception $e) { + $message = $e->getTraceAsString(); + Log::error($message); + return output(['status' => -1, 'message' => $message]); + } + } + + + //Todo: Bu fonksiyon iyileştirilecek + protected function deleteById(Model $model, $id) + { + try { + $result = $model->whereIn("id", $id); + if ($result->count() > 0) { + $deletedRows = $result->get()->toArray(); + if ($result->delete()) { + return $deletedRows; + } + } + return null; + } catch (Exception $e) { + $message = $e->getTraceAsString(); + Log::error($message); + return output(['status' => -1, 'message' => $message]); + } + } + + public function __call($method, $args) + { + try { + if (method_exists($this, $method)) { + array_unshift($args, $this->defaultModel); + return call_user_func_array(array(&$this, $method), $args); + } else { + throw new Exception("'" . __CLASS__ . "' does not have '" . $method . "' method", 1); + } + } catch (Exception $e) { + die($e->getMessage()); + } + } + +} diff --git a/app/Core/Repository/GeneralTimezone/GeneralTimezoneRepository.php b/app/Core/Repository/GeneralTimezone/GeneralTimezoneRepository.php new file mode 100644 index 0000000..6579acb --- /dev/null +++ b/app/Core/Repository/GeneralTimezone/GeneralTimezoneRepository.php @@ -0,0 +1,19 @@ +GeneralTimezone = $GeneralTimezone; + $this->defaultModel = $this->GeneralTimezone; + } + +} diff --git a/app/Core/Repository/Ip2NationCountries/Ip2NationCountriesRepository.php b/app/Core/Repository/Ip2NationCountries/Ip2NationCountriesRepository.php new file mode 100644 index 0000000..bf8ce08 --- /dev/null +++ b/app/Core/Repository/Ip2NationCountries/Ip2NationCountriesRepository.php @@ -0,0 +1,19 @@ +ip2nationCountries = $ip2nationCountries; + $this->defaultModel = $this->ip2nationCountries; + } + +} diff --git a/app/Core/Repository/IpNation/IpNationRepository.php b/app/Core/Repository/IpNation/IpNationRepository.php new file mode 100644 index 0000000..fb38c54 --- /dev/null +++ b/app/Core/Repository/IpNation/IpNationRepository.php @@ -0,0 +1,19 @@ +ipNation = $ipNation; + $this->defaultModel = $this->ipNation; + } + +} diff --git a/app/Core/Repository/Jobs/JobsRepository.php b/app/Core/Repository/Jobs/JobsRepository.php new file mode 100644 index 0000000..07992ca --- /dev/null +++ b/app/Core/Repository/Jobs/JobsRepository.php @@ -0,0 +1,19 @@ +jobs = $jobs; + $this->defaultModel = $this->jobs; + } + +} diff --git a/app/Core/Repository/Language/LanguageRepository.php b/app/Core/Repository/Language/LanguageRepository.php new file mode 100644 index 0000000..2f314d2 --- /dev/null +++ b/app/Core/Repository/Language/LanguageRepository.php @@ -0,0 +1,19 @@ +language = $language; + $this->defaultModel = $this->language; + } + +} diff --git a/app/Core/Repository/LanguageBase/LanguageBaseRepository.php b/app/Core/Repository/LanguageBase/LanguageBaseRepository.php new file mode 100644 index 0000000..4c4b185 --- /dev/null +++ b/app/Core/Repository/LanguageBase/LanguageBaseRepository.php @@ -0,0 +1,19 @@ +languageBase = $languageBase; + $this->defaultModel = $this->languageBase; + } + +} diff --git a/app/Core/Repository/LanguageTranslate/LanguageTranslateRepository.php b/app/Core/Repository/LanguageTranslate/LanguageTranslateRepository.php new file mode 100644 index 0000000..856e6e7 --- /dev/null +++ b/app/Core/Repository/LanguageTranslate/LanguageTranslateRepository.php @@ -0,0 +1,19 @@ +languageTranslate = $languageTranslate; + $this->defaultModel = $this->languageTranslate; + } + +} diff --git a/app/Core/Repository/Offer/OfferRepository.php b/app/Core/Repository/Offer/OfferRepository.php new file mode 100644 index 0000000..d73f9a2 --- /dev/null +++ b/app/Core/Repository/Offer/OfferRepository.php @@ -0,0 +1,19 @@ +offer = $offer; + $this->defaultModel = $this->offer; + } + +} diff --git a/app/Core/Repository/OfferAccommodationMapping/OfferAccommodationMappingRepository.php b/app/Core/Repository/OfferAccommodationMapping/OfferAccommodationMappingRepository.php new file mode 100644 index 0000000..2eb30a1 --- /dev/null +++ b/app/Core/Repository/OfferAccommodationMapping/OfferAccommodationMappingRepository.php @@ -0,0 +1,19 @@ +offerAccommodationMapping = $offerAccommodationMapping; + $this->defaultModel = $this->offerAccommodationMapping; + } + +} diff --git a/app/Core/Repository/OfferCancellationPolicy/OfferCancellationPolicyRepository.php b/app/Core/Repository/OfferCancellationPolicy/OfferCancellationPolicyRepository.php new file mode 100644 index 0000000..758bcdd --- /dev/null +++ b/app/Core/Repository/OfferCancellationPolicy/OfferCancellationPolicyRepository.php @@ -0,0 +1,19 @@ +offerCancellationPolicy = $offerCancellationPolicy; + $this->defaultModel = $this->offerCancellationPolicy; + } + +} diff --git a/app/Core/Repository/OfferConfirmType/OfferConfirmTypeRepository.php b/app/Core/Repository/OfferConfirmType/OfferConfirmTypeRepository.php new file mode 100644 index 0000000..f486a1b --- /dev/null +++ b/app/Core/Repository/OfferConfirmType/OfferConfirmTypeRepository.php @@ -0,0 +1,19 @@ +offerConfirmType = $offerConfirmType; + $this->defaultModel = $this->offerConfirmType; + } + +} diff --git a/app/Core/Repository/OfferContactMapping/OfferContactMappingRepository.php b/app/Core/Repository/OfferContactMapping/OfferContactMappingRepository.php new file mode 100644 index 0000000..0583440 --- /dev/null +++ b/app/Core/Repository/OfferContactMapping/OfferContactMappingRepository.php @@ -0,0 +1,19 @@ +offerContactMapping = $offerContactMapping; + $this->defaultModel = $this->offerContactMapping; + } + +} diff --git a/app/Core/Repository/OfferFactMapping/OfferFactMappingRepository.php b/app/Core/Repository/OfferFactMapping/OfferFactMappingRepository.php new file mode 100644 index 0000000..0572ad7 --- /dev/null +++ b/app/Core/Repository/OfferFactMapping/OfferFactMappingRepository.php @@ -0,0 +1,19 @@ +offerFactMapping = $offerFactMapping; + $this->defaultModel = $this->offerFactMapping; + } + +} diff --git a/app/Core/Repository/OfferImportantNotes/OfferImportantNotesRepository.php b/app/Core/Repository/OfferImportantNotes/OfferImportantNotesRepository.php new file mode 100644 index 0000000..c0ca6bb --- /dev/null +++ b/app/Core/Repository/OfferImportantNotes/OfferImportantNotesRepository.php @@ -0,0 +1,19 @@ +offerImportantNotes = $offerImportantNotes; + $this->defaultModel = $this->offerImportantNotes; + } + +} diff --git a/app/Core/Repository/OfferPaymentType/OfferPaymentTypeRepository.php b/app/Core/Repository/OfferPaymentType/OfferPaymentTypeRepository.php new file mode 100644 index 0000000..66002e5 --- /dev/null +++ b/app/Core/Repository/OfferPaymentType/OfferPaymentTypeRepository.php @@ -0,0 +1,19 @@ +offerPaymentType = $offerPaymentType; + $this->defaultModel = $this->offerPaymentType; + } + +} diff --git a/app/Core/Repository/OfferPhotoMapping/OfferPhotoMappingRepository.php b/app/Core/Repository/OfferPhotoMapping/OfferPhotoMappingRepository.php new file mode 100644 index 0000000..5f2590f --- /dev/null +++ b/app/Core/Repository/OfferPhotoMapping/OfferPhotoMappingRepository.php @@ -0,0 +1,19 @@ +offerPhotoMapping = $offerPhotoMapping; + $this->defaultModel = $this->offerPhotoMapping; + } + +} diff --git a/app/Core/Repository/OfferPrice/OfferPriceRepository.php b/app/Core/Repository/OfferPrice/OfferPriceRepository.php new file mode 100644 index 0000000..f0842d0 --- /dev/null +++ b/app/Core/Repository/OfferPrice/OfferPriceRepository.php @@ -0,0 +1,19 @@ +offerPrice = $offerPrice; + $this->defaultModel = $this->offerPrice; + } + +} diff --git a/app/Core/Repository/OfferRoomMapping/OfferRoomMappingRepository.php b/app/Core/Repository/OfferRoomMapping/OfferRoomMappingRepository.php new file mode 100644 index 0000000..59e95dc --- /dev/null +++ b/app/Core/Repository/OfferRoomMapping/OfferRoomMappingRepository.php @@ -0,0 +1,19 @@ +offerRoomMapping = $offerRoomMapping; + $this->defaultModel = $this->offerRoomMapping; + } + +} diff --git a/app/Core/Repository/PaymentBinNumber/PaymentBinNumberRepository.php b/app/Core/Repository/PaymentBinNumber/PaymentBinNumberRepository.php new file mode 100644 index 0000000..9f0d1ff --- /dev/null +++ b/app/Core/Repository/PaymentBinNumber/PaymentBinNumberRepository.php @@ -0,0 +1,19 @@ +paymentBinNumber = $paymentBinNumber; + $this->defaultModel = $this->paymentBinNumber; + } + +} diff --git a/app/Core/Repository/PaymentTransaction/PaymentTransactionRepository.php b/app/Core/Repository/PaymentTransaction/PaymentTransactionRepository.php new file mode 100644 index 0000000..2922185 --- /dev/null +++ b/app/Core/Repository/PaymentTransaction/PaymentTransactionRepository.php @@ -0,0 +1,19 @@ +paymentTransaction = $paymentTransaction; + $this->defaultModel = $this->paymentTransaction; + } + +} diff --git a/app/Core/Repository/PaymentType/PaymentTypeRepository.php b/app/Core/Repository/PaymentType/PaymentTypeRepository.php new file mode 100644 index 0000000..7ec0ae4 --- /dev/null +++ b/app/Core/Repository/PaymentType/PaymentTypeRepository.php @@ -0,0 +1,19 @@ +paymentType = $paymentType; + $this->defaultModel = $this->paymentType; + } + +} diff --git a/app/Core/Repository/Permission/PermissionRepository.php b/app/Core/Repository/Permission/PermissionRepository.php new file mode 100644 index 0000000..597c9e9 --- /dev/null +++ b/app/Core/Repository/Permission/PermissionRepository.php @@ -0,0 +1,27 @@ +permission = $permission; + $this->permissionMenuModel = $permissionMenuModel; + $this->defaultModel = $this->permission; + } + + public function permissionMenuModelFindByCriteria ( array $params ) + { + return $this->findByCriteria($this->permissionMenuModel,$params); + } + +} diff --git a/app/Core/Repository/PermissionGroup/PermissionGroupRepository.php b/app/Core/Repository/PermissionGroup/PermissionGroupRepository.php new file mode 100644 index 0000000..43c3115 --- /dev/null +++ b/app/Core/Repository/PermissionGroup/PermissionGroupRepository.php @@ -0,0 +1,19 @@ +permissionGroup = $permissionGroup; + $this->defaultModel = $this->permissionGroup; + } + +} diff --git a/app/Core/Repository/PermissionGroupMapping/PermissionGroupMappingRepository.php b/app/Core/Repository/PermissionGroupMapping/PermissionGroupMappingRepository.php new file mode 100644 index 0000000..28e35ba --- /dev/null +++ b/app/Core/Repository/PermissionGroupMapping/PermissionGroupMappingRepository.php @@ -0,0 +1,19 @@ +permissionGroupMapping = $permissionGroupMapping; + $this->defaultModel = $this->permissionGroupMapping; + } + +} diff --git a/app/Core/Repository/PermissionGroupUserMapping/PermissionGroupUserMappingRepository.php b/app/Core/Repository/PermissionGroupUserMapping/PermissionGroupUserMappingRepository.php new file mode 100644 index 0000000..c244de7 --- /dev/null +++ b/app/Core/Repository/PermissionGroupUserMapping/PermissionGroupUserMappingRepository.php @@ -0,0 +1,19 @@ +permissionGroupUserMapping = $permissionGroupUserMapping; + $this->defaultModel = $this->permissionGroupUserMapping; + } + +} diff --git a/app/Core/Repository/PhotoGoogleLabel/PhotoGoogleLabelRepository.php b/app/Core/Repository/PhotoGoogleLabel/PhotoGoogleLabelRepository.php new file mode 100644 index 0000000..1ae8d27 --- /dev/null +++ b/app/Core/Repository/PhotoGoogleLabel/PhotoGoogleLabelRepository.php @@ -0,0 +1,20 @@ +photoGoogleLabel = $photoGoogleLabel; + $this->defaultModel = $this->photoGoogleLabel; + } + +} diff --git a/app/Core/Repository/PlaceCategoryField/PlaceCategoryFieldRepository.php b/app/Core/Repository/PlaceCategoryField/PlaceCategoryFieldRepository.php new file mode 100644 index 0000000..3009903 --- /dev/null +++ b/app/Core/Repository/PlaceCategoryField/PlaceCategoryFieldRepository.php @@ -0,0 +1,19 @@ +placeCategoryField = $placeCategoryField; + $this->defaultModel = $this->placeCategoryField; + } + +} diff --git a/app/Core/Repository/PlaceCategoryFieldMapping/PlaceCategoryFieldMappingRepository.php b/app/Core/Repository/PlaceCategoryFieldMapping/PlaceCategoryFieldMappingRepository.php new file mode 100644 index 0000000..e050564 --- /dev/null +++ b/app/Core/Repository/PlaceCategoryFieldMapping/PlaceCategoryFieldMappingRepository.php @@ -0,0 +1,19 @@ +placeCategoryFieldMapping = $placeCategoryFieldMapping; + $this->defaultModel = $this->placeCategoryFieldMapping; + } + +} diff --git a/app/Core/Repository/PlaceCategoryFieldOption/PlaceCategoryFieldOptionRepository.php b/app/Core/Repository/PlaceCategoryFieldOption/PlaceCategoryFieldOptionRepository.php new file mode 100644 index 0000000..5d3be07 --- /dev/null +++ b/app/Core/Repository/PlaceCategoryFieldOption/PlaceCategoryFieldOptionRepository.php @@ -0,0 +1,19 @@ +placeCategoryFieldOption = $placeCategoryFieldOption; + $this->defaultModel = $this->placeCategoryFieldOption; + } + +} diff --git a/app/Core/Repository/PlaceCategoryFieldOptionFieldMapping/PlaceCategoryFieldOptionFieldMappingRepository.php b/app/Core/Repository/PlaceCategoryFieldOptionFieldMapping/PlaceCategoryFieldOptionFieldMappingRepository.php new file mode 100644 index 0000000..d58df22 --- /dev/null +++ b/app/Core/Repository/PlaceCategoryFieldOptionFieldMapping/PlaceCategoryFieldOptionFieldMappingRepository.php @@ -0,0 +1,19 @@ +placeCategoryFieldOptionFieldMapping = $placeCategoryFieldOptionFieldMapping; + $this->defaultModel = $this->placeCategoryFieldOptionFieldMapping; + } + +} diff --git a/app/Core/Repository/Product/ProductParameterMappingRepository.php b/app/Core/Repository/Product/ProductParameterMappingRepository.php new file mode 100644 index 0000000..478c11c --- /dev/null +++ b/app/Core/Repository/Product/ProductParameterMappingRepository.php @@ -0,0 +1,20 @@ +productParameterMapping = $productParameterMapping; + $this->defaultModel = $this->productParameterMapping; + } + +} diff --git a/app/Core/Repository/Product/ProductRepository.php b/app/Core/Repository/Product/ProductRepository.php new file mode 100644 index 0000000..76b4def --- /dev/null +++ b/app/Core/Repository/Product/ProductRepository.php @@ -0,0 +1,20 @@ +product = $product; + $this->defaultModel = $this->product; + } + +} \ No newline at end of file diff --git a/app/Core/Repository/Product/PropertyProductMappingRepository.php b/app/Core/Repository/Product/PropertyProductMappingRepository.php new file mode 100644 index 0000000..f54ad76 --- /dev/null +++ b/app/Core/Repository/Product/PropertyProductMappingRepository.php @@ -0,0 +1,20 @@ +propertyProductMapping = $propertyProductMapping; + $this->defaultModel = $this->propertyProductMapping; + } + +} \ No newline at end of file diff --git a/app/Core/Repository/PromotionType/PromotionTypeRepository.php b/app/Core/Repository/PromotionType/PromotionTypeRepository.php new file mode 100644 index 0000000..7ae03ae --- /dev/null +++ b/app/Core/Repository/PromotionType/PromotionTypeRepository.php @@ -0,0 +1,20 @@ +promotionType = $promotionType; + $this->defaultModel = $this->promotionType; + } + +} diff --git a/app/Core/Repository/Property/PropertyRepository.php b/app/Core/Repository/Property/PropertyRepository.php new file mode 100644 index 0000000..4b33f2a --- /dev/null +++ b/app/Core/Repository/Property/PropertyRepository.php @@ -0,0 +1,19 @@ +Property = $Property; + $this->defaultModel = $this->Property; + } + +} diff --git a/app/Core/Repository/PropertyAdditionalInfo/PropertyAdditionalInfoKeyRepository.php b/app/Core/Repository/PropertyAdditionalInfo/PropertyAdditionalInfoKeyRepository.php new file mode 100644 index 0000000..dca5700 --- /dev/null +++ b/app/Core/Repository/PropertyAdditionalInfo/PropertyAdditionalInfoKeyRepository.php @@ -0,0 +1,19 @@ +propertyAdditionalInfoKey = $propertyAdditionalInfoKey; + $this->defaultModel = $this->propertyAdditionalInfoKey; + } + +} diff --git a/app/Core/Repository/PropertyAdditionalInfo/PropertyAdditionalInfoRepository.php b/app/Core/Repository/PropertyAdditionalInfo/PropertyAdditionalInfoRepository.php new file mode 100644 index 0000000..6b40c18 --- /dev/null +++ b/app/Core/Repository/PropertyAdditionalInfo/PropertyAdditionalInfoRepository.php @@ -0,0 +1,19 @@ +propertyAdditionalInfo= $propertyAdditionalInfo; + $this->defaultModel = $this->propertyAdditionalInfo; + } + +} diff --git a/app/Core/Repository/PropertyAddon/PropertyAddonRepository.php b/app/Core/Repository/PropertyAddon/PropertyAddonRepository.php new file mode 100644 index 0000000..5f58ee4 --- /dev/null +++ b/app/Core/Repository/PropertyAddon/PropertyAddonRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyAddon/PropertyChannelAddonRepository.php b/app/Core/Repository/PropertyAddon/PropertyChannelAddonRepository.php new file mode 100644 index 0000000..4aa6011 --- /dev/null +++ b/app/Core/Repository/PropertyAddon/PropertyChannelAddonRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyAvailabilityType/PropertyAvailabilityTypeRepository.php b/app/Core/Repository/PropertyAvailabilityType/PropertyAvailabilityTypeRepository.php new file mode 100644 index 0000000..3031069 --- /dev/null +++ b/app/Core/Repository/PropertyAvailabilityType/PropertyAvailabilityTypeRepository.php @@ -0,0 +1,19 @@ +propertyAvailabilityType = $propertyAvailabilityType; + $this->defaultModel = $this->propertyAvailabilityType; + } + +} diff --git a/app/Core/Repository/PropertyAwardsCertificate/PropertyAwardsCertificateRepository.php b/app/Core/Repository/PropertyAwardsCertificate/PropertyAwardsCertificateRepository.php new file mode 100644 index 0000000..1c71933 --- /dev/null +++ b/app/Core/Repository/PropertyAwardsCertificate/PropertyAwardsCertificateRepository.php @@ -0,0 +1,20 @@ +propertyAwardsCertificate = $propertyAwardsCertificate; + $this->defaultModel = $this->propertyAwardsCertificate; + + } + +} diff --git a/app/Core/Repository/PropertyBookingEngine/PropertyBookingEngineRepository.php b/app/Core/Repository/PropertyBookingEngine/PropertyBookingEngineRepository.php new file mode 100644 index 0000000..0753014 --- /dev/null +++ b/app/Core/Repository/PropertyBookingEngine/PropertyBookingEngineRepository.php @@ -0,0 +1,19 @@ +propertyBookingEngine = $propertyBookingEngine; + $this->defaultModel = $this->propertyBookingEngine; + } + +} diff --git a/app/Core/Repository/PropertyBookingEngine/PropertyBookingEngineSearchRepository.php b/app/Core/Repository/PropertyBookingEngine/PropertyBookingEngineSearchRepository.php new file mode 100644 index 0000000..a1bfd0b --- /dev/null +++ b/app/Core/Repository/PropertyBookingEngine/PropertyBookingEngineSearchRepository.php @@ -0,0 +1,19 @@ +propertyBookingEngineSearch = $propertyBookingEngineSearch; + $this->defaultModel = $this->propertyBookingEngineSearch; + } + +} diff --git a/app/Core/Repository/PropertyBookingPaymentType/PropertyBookingPaymentTypeRepository.php b/app/Core/Repository/PropertyBookingPaymentType/PropertyBookingPaymentTypeRepository.php new file mode 100644 index 0000000..a106287 --- /dev/null +++ b/app/Core/Repository/PropertyBookingPaymentType/PropertyBookingPaymentTypeRepository.php @@ -0,0 +1,19 @@ +propertyBookingPaymentType = $propertyBookingPaymentType; + $this->defaultModel = $this->propertyBookingPaymentType; + } + +} diff --git a/app/Core/Repository/PropertyBookingType/PropertyBookingTypeRepository.php b/app/Core/Repository/PropertyBookingType/PropertyBookingTypeRepository.php new file mode 100644 index 0000000..3b8e41a --- /dev/null +++ b/app/Core/Repository/PropertyBookingType/PropertyBookingTypeRepository.php @@ -0,0 +1,19 @@ +propertyBookingType = $propertyBookingType; + $this->defaultModel = $this->propertyBookingType; + } + +} diff --git a/app/Core/Repository/PropertyBrand/PropertyBrandRepository.php b/app/Core/Repository/PropertyBrand/PropertyBrandRepository.php new file mode 100644 index 0000000..401f8a9 --- /dev/null +++ b/app/Core/Repository/PropertyBrand/PropertyBrandRepository.php @@ -0,0 +1,19 @@ +propertyBrand = $propertyBrand; + $this->defaultModel = $this->propertyBrand; + } + +} diff --git a/app/Core/Repository/PropertyCancellationPolicy/PropertyCancellationPolicyRepository.php b/app/Core/Repository/PropertyCancellationPolicy/PropertyCancellationPolicyRepository.php new file mode 100644 index 0000000..8bcc1d1 --- /dev/null +++ b/app/Core/Repository/PropertyCancellationPolicy/PropertyCancellationPolicyRepository.php @@ -0,0 +1,19 @@ +propertyCancellationPolicy = $propertyCancellationPolicy; + $this->defaultModel = $this->propertyCancellationPolicy; + } + +} diff --git a/app/Core/Repository/PropertyChain/PropertyChainRepository.php b/app/Core/Repository/PropertyChain/PropertyChainRepository.php new file mode 100644 index 0000000..db73184 --- /dev/null +++ b/app/Core/Repository/PropertyChain/PropertyChainRepository.php @@ -0,0 +1,20 @@ +propertyChain = $propertyChain; + $this->defaultModel = $this->propertyChain; + } + +} diff --git a/app/Core/Repository/PropertyChannel/PropertyChannelContactRepository.php b/app/Core/Repository/PropertyChannel/PropertyChannelContactRepository.php new file mode 100644 index 0000000..28441fd --- /dev/null +++ b/app/Core/Repository/PropertyChannel/PropertyChannelContactRepository.php @@ -0,0 +1,19 @@ +propertyChannelContact = $propertyChannelContact; + $this->defaultModel = $this->propertyChannelContact; + } + +} diff --git a/app/Core/Repository/PropertyChannel/PropertyChannelRepository.php b/app/Core/Repository/PropertyChannel/PropertyChannelRepository.php new file mode 100644 index 0000000..1439e51 --- /dev/null +++ b/app/Core/Repository/PropertyChannel/PropertyChannelRepository.php @@ -0,0 +1,19 @@ +propertyChannel = $propertyChannel; + $this->defaultModel = $this->propertyChannel; + } + +} diff --git a/app/Core/Repository/PropertyChannel/PropertyChannelTaxRepository.php b/app/Core/Repository/PropertyChannel/PropertyChannelTaxRepository.php new file mode 100644 index 0000000..a433ecb --- /dev/null +++ b/app/Core/Repository/PropertyChannel/PropertyChannelTaxRepository.php @@ -0,0 +1,19 @@ +propertyChannelTax = $propertyChannelTax; + $this->defaultModel = $this->propertyChannelTax; + } + +} diff --git a/app/Core/Repository/PropertyChannelBookingPaymentSetup/PropertyChannelBookingPaymentSetupRepository.php b/app/Core/Repository/PropertyChannelBookingPaymentSetup/PropertyChannelBookingPaymentSetupRepository.php new file mode 100644 index 0000000..7ab1092 --- /dev/null +++ b/app/Core/Repository/PropertyChannelBookingPaymentSetup/PropertyChannelBookingPaymentSetupRepository.php @@ -0,0 +1,19 @@ +propertyChannelBookingPaymentSetup = $propertyChannelBookingPaymentSetup; + $this->defaultModel = $this->propertyChannelBookingPaymentSetup; + } + +} diff --git a/app/Core/Repository/PropertyChannelCategory/PropertyChannelCategoryRepository.php b/app/Core/Repository/PropertyChannelCategory/PropertyChannelCategoryRepository.php new file mode 100644 index 0000000..cd4f7e7 --- /dev/null +++ b/app/Core/Repository/PropertyChannelCategory/PropertyChannelCategoryRepository.php @@ -0,0 +1,19 @@ +propertyChannelCategory = $propertyChannelCategory; + $this->defaultModel = $this->propertyChannelCategory; + } + +} diff --git a/app/Core/Repository/PropertyChannelGroup/PropertyChannelGroupChannelMappingRepository.php b/app/Core/Repository/PropertyChannelGroup/PropertyChannelGroupChannelMappingRepository.php new file mode 100644 index 0000000..6ebeb2f --- /dev/null +++ b/app/Core/Repository/PropertyChannelGroup/PropertyChannelGroupChannelMappingRepository.php @@ -0,0 +1,19 @@ +propertyChannelGroupChannelMapping = $propertyChannelGroupChannelMapping; + $this->defaultModel = $this->propertyChannelGroupChannelMapping; + } + +} diff --git a/app/Core/Repository/PropertyChannelGroup/PropertyChannelGroupRepository.php b/app/Core/Repository/PropertyChannelGroup/PropertyChannelGroupRepository.php new file mode 100644 index 0000000..ad613a6 --- /dev/null +++ b/app/Core/Repository/PropertyChannelGroup/PropertyChannelGroupRepository.php @@ -0,0 +1,19 @@ +propertyChannelGroup = $propertyChannelGroup; + $this->defaultModel = $this->propertyChannelGroup; + } + +} diff --git a/app/Core/Repository/PropertyChannelMapping/PropertyChannelMappingRepository.php b/app/Core/Repository/PropertyChannelMapping/PropertyChannelMappingRepository.php new file mode 100644 index 0000000..461854f --- /dev/null +++ b/app/Core/Repository/PropertyChannelMapping/PropertyChannelMappingRepository.php @@ -0,0 +1,19 @@ +userPropertyMapping = $userPropertyMapping; + $this->defaultModel = $this->userPropertyMapping; + } + +} diff --git a/app/Core/Repository/PropertyChannelRoomRateCancellationPolicyMapping/PropertyChannelRoomRateCancellationPolicyMappingRepository.php b/app/Core/Repository/PropertyChannelRoomRateCancellationPolicyMapping/PropertyChannelRoomRateCancellationPolicyMappingRepository.php new file mode 100644 index 0000000..5a02c44 --- /dev/null +++ b/app/Core/Repository/PropertyChannelRoomRateCancellationPolicyMapping/PropertyChannelRoomRateCancellationPolicyMappingRepository.php @@ -0,0 +1,19 @@ +propertyChannelRoomRateCancellationPolicyMapping = $propertyChannelRoomRateCancellationPolicyMapping; + $this->defaultModel = $this->propertyChannelRoomRateCancellationPolicyMapping; + } + +} diff --git a/app/Core/Repository/PropertyChannelRoomRatePricingPolicyAdultMapping/PropertyChannelRoomRatePricingPolicyAdultMappingRepository.php b/app/Core/Repository/PropertyChannelRoomRatePricingPolicyAdultMapping/PropertyChannelRoomRatePricingPolicyAdultMappingRepository.php new file mode 100644 index 0000000..7ecf2aa --- /dev/null +++ b/app/Core/Repository/PropertyChannelRoomRatePricingPolicyAdultMapping/PropertyChannelRoomRatePricingPolicyAdultMappingRepository.php @@ -0,0 +1,19 @@ +propertyChannelRoomRatePricingPolicyAdultMapping = $propertyChannelRoomRatePricingPolicyAdultMapping; + $this->defaultModel = $this->propertyChannelRoomRatePricingPolicyAdultMapping; + } + +} diff --git a/app/Core/Repository/PropertyChannelRoomRatePricingPolicyChildMapping/PropertyChannelRoomRatePricingPolicyChildMappingRepository.php b/app/Core/Repository/PropertyChannelRoomRatePricingPolicyChildMapping/PropertyChannelRoomRatePricingPolicyChildMappingRepository.php new file mode 100644 index 0000000..14a0a6d --- /dev/null +++ b/app/Core/Repository/PropertyChannelRoomRatePricingPolicyChildMapping/PropertyChannelRoomRatePricingPolicyChildMappingRepository.php @@ -0,0 +1,19 @@ +propertyChannelRoomRatePricingPolicyChildMapping = $propertyChannelRoomRatePricingPolicyChildMapping; + $this->defaultModel = $this->propertyChannelRoomRatePricingPolicyChildMapping; + } + +} diff --git a/app/Core/Repository/PropertyCompetitorGroup/PropertyCompetitorGroupRepository.php b/app/Core/Repository/PropertyCompetitorGroup/PropertyCompetitorGroupRepository.php new file mode 100644 index 0000000..42b527c --- /dev/null +++ b/app/Core/Repository/PropertyCompetitorGroup/PropertyCompetitorGroupRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyCompetitorGroupMapping/PropertyCompetitorGroupMappingRepository.php b/app/Core/Repository/PropertyCompetitorGroupMapping/PropertyCompetitorGroupMappingRepository.php new file mode 100644 index 0000000..c77c1bb --- /dev/null +++ b/app/Core/Repository/PropertyCompetitorGroupMapping/PropertyCompetitorGroupMappingRepository.php @@ -0,0 +1,18 @@ +model = $model; + $this->defaultModel = $this->model; + } +} diff --git a/app/Core/Repository/PropertyCompetitorMapping/PropertyCompetitorMappingRepository.php b/app/Core/Repository/PropertyCompetitorMapping/PropertyCompetitorMappingRepository.php new file mode 100644 index 0000000..8eaba2b --- /dev/null +++ b/app/Core/Repository/PropertyCompetitorMapping/PropertyCompetitorMappingRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyConfig/PropertyConfigRepository.php b/app/Core/Repository/PropertyConfig/PropertyConfigRepository.php new file mode 100644 index 0000000..bad326c --- /dev/null +++ b/app/Core/Repository/PropertyConfig/PropertyConfigRepository.php @@ -0,0 +1,19 @@ +propertyConfig = $propertyConfig; + $this->defaultModel = $this->propertyConfig; + } + +} diff --git a/app/Core/Repository/PropertyContact/PropertyContactRepository.php b/app/Core/Repository/PropertyContact/PropertyContactRepository.php new file mode 100644 index 0000000..961fb3b --- /dev/null +++ b/app/Core/Repository/PropertyContact/PropertyContactRepository.php @@ -0,0 +1,19 @@ +propertyContact = $propertyContact; + $this->defaultModel = $this->propertyContact; + } + +} diff --git a/app/Core/Repository/PropertyContent/PropertyContentRepository.php b/app/Core/Repository/PropertyContent/PropertyContentRepository.php new file mode 100644 index 0000000..380742d --- /dev/null +++ b/app/Core/Repository/PropertyContent/PropertyContentRepository.php @@ -0,0 +1,19 @@ +propertyContent = $propertyContent; + $this->defaultModel = $this->propertyContent; + } + +} diff --git a/app/Core/Repository/PropertyContentCategory/PropertyContentCategoryRepository.php b/app/Core/Repository/PropertyContentCategory/PropertyContentCategoryRepository.php new file mode 100644 index 0000000..38374d2 --- /dev/null +++ b/app/Core/Repository/PropertyContentCategory/PropertyContentCategoryRepository.php @@ -0,0 +1,19 @@ +propertyContentCategory = $propertyContentCategory; + $this->defaultModel = $this->propertyContentCategory; + } + +} diff --git a/app/Core/Repository/PropertyExecutive/PropertyExecutiveRepository.php b/app/Core/Repository/PropertyExecutive/PropertyExecutiveRepository.php new file mode 100644 index 0000000..31f4903 --- /dev/null +++ b/app/Core/Repository/PropertyExecutive/PropertyExecutiveRepository.php @@ -0,0 +1,19 @@ +propertyExecutive = $propertyExecutive; + $this->defaultModel = $this->propertyExecutive; + } + +} diff --git a/app/Core/Repository/PropertyExecutiveType/PropertyExecutiveTypeRepository.php b/app/Core/Repository/PropertyExecutiveType/PropertyExecutiveTypeRepository.php new file mode 100644 index 0000000..fd6a3cf --- /dev/null +++ b/app/Core/Repository/PropertyExecutiveType/PropertyExecutiveTypeRepository.php @@ -0,0 +1,19 @@ +propertyExecutiveType = $propertyExecutiveType; + $this->defaultModel = $this->propertyExecutiveType; + } + +} diff --git a/app/Core/Repository/PropertyFact/PropertyFactRepository.php b/app/Core/Repository/PropertyFact/PropertyFactRepository.php new file mode 100644 index 0000000..0ca5ce6 --- /dev/null +++ b/app/Core/Repository/PropertyFact/PropertyFactRepository.php @@ -0,0 +1,19 @@ +propertyFact = $propertyFact; + $this->defaultModel = $this->propertyFact; + } + +} diff --git a/app/Core/Repository/PropertyFactAttribute/PropertyFactAttributeRepository.php b/app/Core/Repository/PropertyFactAttribute/PropertyFactAttributeRepository.php new file mode 100644 index 0000000..ce68f47 --- /dev/null +++ b/app/Core/Repository/PropertyFactAttribute/PropertyFactAttributeRepository.php @@ -0,0 +1,19 @@ +propertyFactAttribute = $propertyFactAttribute; + $this->defaultModel = $this->propertyFactAttribute; + } + +} diff --git a/app/Core/Repository/PropertyFactMapping/PropertyFactMappingRepository.php b/app/Core/Repository/PropertyFactMapping/PropertyFactMappingRepository.php new file mode 100644 index 0000000..d474caf --- /dev/null +++ b/app/Core/Repository/PropertyFactMapping/PropertyFactMappingRepository.php @@ -0,0 +1,19 @@ +propertyFactMapping = $propertyFactMapping; + $this->defaultModel = $this->propertyFactMapping; + } + +} diff --git a/app/Core/Repository/PropertyGroup/PropertyGroupRepository.php b/app/Core/Repository/PropertyGroup/PropertyGroupRepository.php new file mode 100644 index 0000000..adf1959 --- /dev/null +++ b/app/Core/Repository/PropertyGroup/PropertyGroupRepository.php @@ -0,0 +1,19 @@ +propertyGroup = $propertyGroup; + $this->defaultModel = $this->propertyGroup; + } + +} diff --git a/app/Core/Repository/PropertyGroupMapping/PropertyGroupMappingRepository.php b/app/Core/Repository/PropertyGroupMapping/PropertyGroupMappingRepository.php new file mode 100644 index 0000000..17e52e0 --- /dev/null +++ b/app/Core/Repository/PropertyGroupMapping/PropertyGroupMappingRepository.php @@ -0,0 +1,19 @@ +propertyGroupMapping = $propertyGroupMapping; + $this->defaultModel = $this->propertyGroupMapping; + } + +} diff --git a/app/Core/Repository/PropertyInvoice/PropertyInvoiceRepository.php b/app/Core/Repository/PropertyInvoice/PropertyInvoiceRepository.php new file mode 100644 index 0000000..9419ade --- /dev/null +++ b/app/Core/Repository/PropertyInvoice/PropertyInvoiceRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyLanguageSpoken/PropertyLanguageSpokenRepository.php b/app/Core/Repository/PropertyLanguageSpoken/PropertyLanguageSpokenRepository.php new file mode 100644 index 0000000..a762760 --- /dev/null +++ b/app/Core/Repository/PropertyLanguageSpoken/PropertyLanguageSpokenRepository.php @@ -0,0 +1,19 @@ +PropertyLanguageSpoken = $PropertyLanguageSpoken; + $this->defaultModel = $this->PropertyLanguageSpoken; + } + +} diff --git a/app/Core/Repository/PropertyModule/PropertyModuleRepository.php b/app/Core/Repository/PropertyModule/PropertyModuleRepository.php new file mode 100644 index 0000000..95f7723 --- /dev/null +++ b/app/Core/Repository/PropertyModule/PropertyModuleRepository.php @@ -0,0 +1,20 @@ +propertyModule = $propertyModule; + $this->defaultModel = $this->propertyModule; + } + +} diff --git a/app/Core/Repository/PropertyModuleMapping/PropertyModuleMappingRepository.php b/app/Core/Repository/PropertyModuleMapping/PropertyModuleMappingRepository.php new file mode 100644 index 0000000..b2d7322 --- /dev/null +++ b/app/Core/Repository/PropertyModuleMapping/PropertyModuleMappingRepository.php @@ -0,0 +1,20 @@ +propertyModuleMapping = $propertyModuleMapping; + $this->defaultModel = $this->propertyModuleMapping; + } + +} diff --git a/app/Core/Repository/PropertyNonrefundable/PropertyNonrefundableRepository.php b/app/Core/Repository/PropertyNonrefundable/PropertyNonrefundableRepository.php new file mode 100644 index 0000000..770bdbd --- /dev/null +++ b/app/Core/Repository/PropertyNonrefundable/PropertyNonrefundableRepository.php @@ -0,0 +1,19 @@ +propertyNonrefundable = $propertyNonrefundable; + $this->defaultModel = $this->propertyNonrefundable; + } + +} diff --git a/app/Core/Repository/PropertyPaymentInstallment/PropertyPaymentInstallmentRepository.php b/app/Core/Repository/PropertyPaymentInstallment/PropertyPaymentInstallmentRepository.php new file mode 100644 index 0000000..09a9f92 --- /dev/null +++ b/app/Core/Repository/PropertyPaymentInstallment/PropertyPaymentInstallmentRepository.php @@ -0,0 +1,19 @@ +propertyPaymentInstallment = $propertyPaymentInstallment; + $this->defaultModel = $this->propertyPaymentInstallment; + } + +} diff --git a/app/Core/Repository/PropertyPaymentMapping/PropertyPaymentMappingRepository.php b/app/Core/Repository/PropertyPaymentMapping/PropertyPaymentMappingRepository.php new file mode 100644 index 0000000..dc0cf1c --- /dev/null +++ b/app/Core/Repository/PropertyPaymentMapping/PropertyPaymentMappingRepository.php @@ -0,0 +1,19 @@ +propertyPaymentMapping = $propertyPaymentMapping; + $this->defaultModel = $this->propertyPaymentMapping; + } + +} diff --git a/app/Core/Repository/PropertyPhoto/PropertyPhotoRepository.php b/app/Core/Repository/PropertyPhoto/PropertyPhotoRepository.php new file mode 100644 index 0000000..1f2015e --- /dev/null +++ b/app/Core/Repository/PropertyPhoto/PropertyPhotoRepository.php @@ -0,0 +1,20 @@ +propertyPhoto = $propertyPhoto; + $this->defaultModel = $this->propertyPhoto; + } + +} diff --git a/app/Core/Repository/PropertyPhotoCategory/PropertyPhotoCategoryRepository.php b/app/Core/Repository/PropertyPhotoCategory/PropertyPhotoCategoryRepository.php new file mode 100644 index 0000000..e2677d1 --- /dev/null +++ b/app/Core/Repository/PropertyPhotoCategory/PropertyPhotoCategoryRepository.php @@ -0,0 +1,20 @@ +propertyPhotoCategory = $propertyPhotoCategory; + $this->defaultModel = $this->propertyPhotoCategory; + } + +} diff --git a/app/Core/Repository/PropertyPhotoCategoryLabelMapping/PropertyPhotoCategoryLabelMappingRepository.php b/app/Core/Repository/PropertyPhotoCategoryLabelMapping/PropertyPhotoCategoryLabelMappingRepository.php new file mode 100644 index 0000000..529bbfe --- /dev/null +++ b/app/Core/Repository/PropertyPhotoCategoryLabelMapping/PropertyPhotoCategoryLabelMappingRepository.php @@ -0,0 +1,20 @@ +propertyPhotoCategoryLabelMapping = $propertyPhotoCategoryLabelMapping; + $this->defaultModel = $this->propertyPhotoCategoryLabelMapping; + } + +} diff --git a/app/Core/Repository/PropertyPlace/PropertyPlaceRepository.php b/app/Core/Repository/PropertyPlace/PropertyPlaceRepository.php new file mode 100644 index 0000000..da786e0 --- /dev/null +++ b/app/Core/Repository/PropertyPlace/PropertyPlaceRepository.php @@ -0,0 +1,19 @@ +propertyPlace = $propertyPlace; + $this->defaultModel = $this->propertyPlace; + } + +} diff --git a/app/Core/Repository/PropertyPlaceCategory/PropertyPlaceCategoryRepository.php b/app/Core/Repository/PropertyPlaceCategory/PropertyPlaceCategoryRepository.php new file mode 100644 index 0000000..76e21c2 --- /dev/null +++ b/app/Core/Repository/PropertyPlaceCategory/PropertyPlaceCategoryRepository.php @@ -0,0 +1,19 @@ +propertyPlaceCategory = $propertyPlaceCategory; + $this->defaultModel = $this->propertyPlaceCategory; + } + +} diff --git a/app/Core/Repository/PropertyPlaceCategoryFieldValue/PropertyPlaceCategoryFieldValueRepository.php b/app/Core/Repository/PropertyPlaceCategoryFieldValue/PropertyPlaceCategoryFieldValueRepository.php new file mode 100644 index 0000000..4b88389 --- /dev/null +++ b/app/Core/Repository/PropertyPlaceCategoryFieldValue/PropertyPlaceCategoryFieldValueRepository.php @@ -0,0 +1,19 @@ +propertyPlaceCategoryFieldValue = $propertyPlaceCategoryFieldValue; + $this->defaultModel = $this->propertyPlaceCategoryFieldValue; + } + +} diff --git a/app/Core/Repository/PropertyPlaceFact/PropertyPlaceFactRepository.php b/app/Core/Repository/PropertyPlaceFact/PropertyPlaceFactRepository.php new file mode 100644 index 0000000..310cf83 --- /dev/null +++ b/app/Core/Repository/PropertyPlaceFact/PropertyPlaceFactRepository.php @@ -0,0 +1,19 @@ +propertyPlaceFact = $propertyPlaceFact; + $this->defaultModel = $this->propertyPlaceFact; + } + +} diff --git a/app/Core/Repository/PropertyPlaceFactMapping/PropertyPlaceFactMappingRepository.php b/app/Core/Repository/PropertyPlaceFactMapping/PropertyPlaceFactMappingRepository.php new file mode 100644 index 0000000..044eb9b --- /dev/null +++ b/app/Core/Repository/PropertyPlaceFactMapping/PropertyPlaceFactMappingRepository.php @@ -0,0 +1,19 @@ +propertyPlaceFactMapping = $propertyPlaceFactMapping; + $this->defaultModel = $this->propertyPlaceFactMapping; + } + +} diff --git a/app/Core/Repository/PropertyPlaceFactTitle/PropertyPlaceFactTitleRepository.php b/app/Core/Repository/PropertyPlaceFactTitle/PropertyPlaceFactTitleRepository.php new file mode 100644 index 0000000..877981f --- /dev/null +++ b/app/Core/Repository/PropertyPlaceFactTitle/PropertyPlaceFactTitleRepository.php @@ -0,0 +1,19 @@ +propertyPlaceFactTitle = $propertyPlaceFactTitle; + $this->defaultModel = $this->propertyPlaceFactTitle; + } + +} diff --git a/app/Core/Repository/PropertyPlaceFactTitleFactMapping/PropertyPlaceFactTitleFactMappingRepository.php b/app/Core/Repository/PropertyPlaceFactTitleFactMapping/PropertyPlaceFactTitleFactMappingRepository.php new file mode 100644 index 0000000..3f6100c --- /dev/null +++ b/app/Core/Repository/PropertyPlaceFactTitleFactMapping/PropertyPlaceFactTitleFactMappingRepository.php @@ -0,0 +1,19 @@ +propertyPlaceFactTitleFactMapping = $propertyPlaceFactTitleFactMapping; + $this->defaultModel = $this->propertyPlaceFactTitleFactMapping; + } + +} diff --git a/app/Core/Repository/PropertyPlacePhotoMapping/PropertyPlacePhotoMappingRepository.php b/app/Core/Repository/PropertyPlacePhotoMapping/PropertyPlacePhotoMappingRepository.php new file mode 100644 index 0000000..a32bda5 --- /dev/null +++ b/app/Core/Repository/PropertyPlacePhotoMapping/PropertyPlacePhotoMappingRepository.php @@ -0,0 +1,19 @@ +propertyPlacePhotoMapping = $propertyPlacePhotoMapping; + $this->defaultModel = $this->propertyPlacePhotoMapping; + } + +} diff --git a/app/Core/Repository/PropertyPlaceWorkingHour/PropertyPlaceWorkingHourRepository.php b/app/Core/Repository/PropertyPlaceWorkingHour/PropertyPlaceWorkingHourRepository.php new file mode 100644 index 0000000..b1c18c6 --- /dev/null +++ b/app/Core/Repository/PropertyPlaceWorkingHour/PropertyPlaceWorkingHourRepository.php @@ -0,0 +1,19 @@ +propertyPlaceWorkingHour = $propertyPlaceWorkingHour; + $this->defaultModel = $this->propertyPlaceWorkingHour; + } + +} diff --git a/app/Core/Repository/PropertyPricingPolicyAdult/PropertyPricingPolicyAdultRepository.php b/app/Core/Repository/PropertyPricingPolicyAdult/PropertyPricingPolicyAdultRepository.php new file mode 100644 index 0000000..0fac2d9 --- /dev/null +++ b/app/Core/Repository/PropertyPricingPolicyAdult/PropertyPricingPolicyAdultRepository.php @@ -0,0 +1,19 @@ +propertyPricingPolicyAdult = $propertyPricingPolicyAdult; + $this->defaultModel = $this->propertyPricingPolicyAdult; + } + +} diff --git a/app/Core/Repository/PropertyPricingPolicyChild/PropertyPricingPolicyChildRepository.php b/app/Core/Repository/PropertyPricingPolicyChild/PropertyPricingPolicyChildRepository.php new file mode 100644 index 0000000..bbe406d --- /dev/null +++ b/app/Core/Repository/PropertyPricingPolicyChild/PropertyPricingPolicyChildRepository.php @@ -0,0 +1,19 @@ +propertyPricingPolicyChild = $propertyPricingPolicyChild; + $this->defaultModel = $this->propertyPricingPolicyChild; + } + +} diff --git a/app/Core/Repository/PropertyPromotion/PropertyPromotionRepository.php b/app/Core/Repository/PropertyPromotion/PropertyPromotionRepository.php new file mode 100644 index 0000000..41356f5 --- /dev/null +++ b/app/Core/Repository/PropertyPromotion/PropertyPromotionRepository.php @@ -0,0 +1,20 @@ +propertyPromotion = $propertyPromotion; + $this->defaultModel = $this->propertyPromotion; + } + +} diff --git a/app/Core/Repository/PropertyPromotionMapping/PropertyPromotionMappingRepository.php b/app/Core/Repository/PropertyPromotionMapping/PropertyPromotionMappingRepository.php new file mode 100644 index 0000000..3a9b6ab --- /dev/null +++ b/app/Core/Repository/PropertyPromotionMapping/PropertyPromotionMappingRepository.php @@ -0,0 +1,20 @@ +propertyPromotionMapping = $propertyPromotionMapping; + $this->defaultModel = $this->propertyPromotionMapping; + } + +} diff --git a/app/Core/Repository/PropertyQuickPricingMapping/PropertyQuickPricingMappingRepository.php b/app/Core/Repository/PropertyQuickPricingMapping/PropertyQuickPricingMappingRepository.php new file mode 100644 index 0000000..78a38f9 --- /dev/null +++ b/app/Core/Repository/PropertyQuickPricingMapping/PropertyQuickPricingMappingRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyRoom/PropertyRoomRepository.php b/app/Core/Repository/PropertyRoom/PropertyRoomRepository.php new file mode 100644 index 0000000..281ba77 --- /dev/null +++ b/app/Core/Repository/PropertyRoom/PropertyRoomRepository.php @@ -0,0 +1,19 @@ +propertyRoom = $propertyRoom; + $this->defaultModel = $this->propertyRoom; + } + +} diff --git a/app/Core/Repository/PropertyRoomAvailability/PropertyRoomAvailabilityQueueRepository.php b/app/Core/Repository/PropertyRoomAvailability/PropertyRoomAvailabilityQueueRepository.php new file mode 100644 index 0000000..8c4b782 --- /dev/null +++ b/app/Core/Repository/PropertyRoomAvailability/PropertyRoomAvailabilityQueueRepository.php @@ -0,0 +1,19 @@ +propertyRoomAvailabilityQueue = $propertyRoomAvailabilityQueue; + $this->defaultModel = $this->propertyRoomAvailabilityQueue; + } + +} diff --git a/app/Core/Repository/PropertyRoomAvailability/PropertyRoomAvailabilityRepository.php b/app/Core/Repository/PropertyRoomAvailability/PropertyRoomAvailabilityRepository.php new file mode 100644 index 0000000..a2267ba --- /dev/null +++ b/app/Core/Repository/PropertyRoomAvailability/PropertyRoomAvailabilityRepository.php @@ -0,0 +1,19 @@ +propertyRoomAvailability = $propertyRoomAvailability; + $this->defaultModel = $this->propertyRoomAvailability; + } + +} diff --git a/app/Core/Repository/PropertyRoomBed/PropertyRoomBedRepository.php b/app/Core/Repository/PropertyRoomBed/PropertyRoomBedRepository.php new file mode 100644 index 0000000..1acc1c2 --- /dev/null +++ b/app/Core/Repository/PropertyRoomBed/PropertyRoomBedRepository.php @@ -0,0 +1,19 @@ +propertyRoomBed = $propertyRoomBed; + $this->defaultModel = $this->propertyRoomBed; + } + +} diff --git a/app/Core/Repository/PropertyRoomBedType/PropertyRoomBedTypeRepository.php b/app/Core/Repository/PropertyRoomBedType/PropertyRoomBedTypeRepository.php new file mode 100644 index 0000000..be50f15 --- /dev/null +++ b/app/Core/Repository/PropertyRoomBedType/PropertyRoomBedTypeRepository.php @@ -0,0 +1,19 @@ +propertyRoomBedType = $propertyRoomBedType; + $this->defaultModel = $this->propertyRoomBedType; + } + +} diff --git a/app/Core/Repository/PropertyRoomConnected/PropertyRoomConnectedRepository.php b/app/Core/Repository/PropertyRoomConnected/PropertyRoomConnectedRepository.php new file mode 100644 index 0000000..6d0fb12 --- /dev/null +++ b/app/Core/Repository/PropertyRoomConnected/PropertyRoomConnectedRepository.php @@ -0,0 +1,19 @@ +propertyRoomConnected = $propertyRoomConnected; + $this->defaultModel = $this->propertyRoomConnected; + } + +} diff --git a/app/Core/Repository/PropertyRoomFactMapping/PropertyRoomFactMappingRepository.php b/app/Core/Repository/PropertyRoomFactMapping/PropertyRoomFactMappingRepository.php new file mode 100644 index 0000000..82cb310 --- /dev/null +++ b/app/Core/Repository/PropertyRoomFactMapping/PropertyRoomFactMappingRepository.php @@ -0,0 +1,19 @@ +propertyRoomFactMapping = $propertyRoomFactMapping; + $this->defaultModel = $this->propertyRoomFactMapping; + } + +} diff --git a/app/Core/Repository/PropertyRoomPhotoMapping/PropertyRoomPhotoMappingRepository.php b/app/Core/Repository/PropertyRoomPhotoMapping/PropertyRoomPhotoMappingRepository.php new file mode 100644 index 0000000..66545e7 --- /dev/null +++ b/app/Core/Repository/PropertyRoomPhotoMapping/PropertyRoomPhotoMappingRepository.php @@ -0,0 +1,19 @@ +propertyRoomPhotoMapping = $propertyRoomPhotoMapping; + $this->defaultModel = $this->propertyRoomPhotoMapping; + } + +} diff --git a/app/Core/Repository/PropertyRoomPricingType/PropertyRoomPricingTypeRepository.php b/app/Core/Repository/PropertyRoomPricingType/PropertyRoomPricingTypeRepository.php new file mode 100644 index 0000000..f7352ff --- /dev/null +++ b/app/Core/Repository/PropertyRoomPricingType/PropertyRoomPricingTypeRepository.php @@ -0,0 +1,19 @@ +propertyRoomPricingType = $propertyRoomPricingType; + $this->defaultModel = $this->propertyRoomPricingType; + } + +} diff --git a/app/Core/Repository/PropertyRoomRate/PropertyRoomRateRepository.php b/app/Core/Repository/PropertyRoomRate/PropertyRoomRateRepository.php new file mode 100644 index 0000000..5dd8cbb --- /dev/null +++ b/app/Core/Repository/PropertyRoomRate/PropertyRoomRateRepository.php @@ -0,0 +1,19 @@ +propertyRoomRate = $propertyRoomRate; + $this->defaultModel = $this->propertyRoomRate; + } + +} diff --git a/app/Core/Repository/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingRepository.php b/app/Core/Repository/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingRepository.php new file mode 100644 index 0000000..cae8c67 --- /dev/null +++ b/app/Core/Repository/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingRepository.php @@ -0,0 +1,19 @@ +propertyRoomRateChannelMapping = $propertyRoomRateChannelMapping; + $this->defaultModel = $this->propertyRoomRateChannelMapping; + } + +} diff --git a/app/Core/Repository/PropertyRoomRateInclusionMapping/PropertyRoomRateInclusionMappingRepository.php b/app/Core/Repository/PropertyRoomRateInclusionMapping/PropertyRoomRateInclusionMappingRepository.php new file mode 100644 index 0000000..bb399f9 --- /dev/null +++ b/app/Core/Repository/PropertyRoomRateInclusionMapping/PropertyRoomRateInclusionMappingRepository.php @@ -0,0 +1,19 @@ +propertyRoomRateInclusionMapping = $propertyRoomRateInclusionMapping; + $this->defaultModel = $this->propertyRoomRateInclusionMapping; + } + +} diff --git a/app/Core/Repository/PropertyRoomRateMapping/PropertyRoomRateMappingRepository.php b/app/Core/Repository/PropertyRoomRateMapping/PropertyRoomRateMappingRepository.php new file mode 100644 index 0000000..2e89d5a --- /dev/null +++ b/app/Core/Repository/PropertyRoomRateMapping/PropertyRoomRateMappingRepository.php @@ -0,0 +1,19 @@ +propertyRoomRateMapping = $propertyRoomRateMapping; + $this->defaultModel = $this->propertyRoomRateMapping; + } + +} diff --git a/app/Core/Repository/PropertyRoomRateMappingSetup/PropertyRoomRateMappingSetupRepository.php b/app/Core/Repository/PropertyRoomRateMappingSetup/PropertyRoomRateMappingSetupRepository.php new file mode 100644 index 0000000..f9ce3ce --- /dev/null +++ b/app/Core/Repository/PropertyRoomRateMappingSetup/PropertyRoomRateMappingSetupRepository.php @@ -0,0 +1,19 @@ +propertyRoomRateMappingSetup = $propertyRoomRateMappingSetup; + $this->defaultModel = $this->propertyRoomRateMappingSetup; + } + +} diff --git a/app/Core/Repository/PropertyRoomRatePrice/PropertyRoomRatePriceQueueRepository.php b/app/Core/Repository/PropertyRoomRatePrice/PropertyRoomRatePriceQueueRepository.php new file mode 100644 index 0000000..427cd50 --- /dev/null +++ b/app/Core/Repository/PropertyRoomRatePrice/PropertyRoomRatePriceQueueRepository.php @@ -0,0 +1,19 @@ +propertyRoomRatePriceQueue = $propertyRoomRatePriceQueue; + $this->defaultModel = $this->propertyRoomRatePriceQueue; + } + +} diff --git a/app/Core/Repository/PropertyRoomRatePrice/PropertyRoomRatePriceRepository.php b/app/Core/Repository/PropertyRoomRatePrice/PropertyRoomRatePriceRepository.php new file mode 100644 index 0000000..b1adfb0 --- /dev/null +++ b/app/Core/Repository/PropertyRoomRatePrice/PropertyRoomRatePriceRepository.php @@ -0,0 +1,19 @@ +propertyRoomRatePrice = $propertyRoomRatePrice; + $this->defaultModel = $this->propertyRoomRatePrice; + } + +} diff --git a/app/Core/Repository/PropertyRoomSizeType/PropertyRoomSizeTypeRepository.php b/app/Core/Repository/PropertyRoomSizeType/PropertyRoomSizeTypeRepository.php new file mode 100644 index 0000000..cd07732 --- /dev/null +++ b/app/Core/Repository/PropertyRoomSizeType/PropertyRoomSizeTypeRepository.php @@ -0,0 +1,20 @@ +propertyRoomSizeType = $propertyRoomSizeType; + $this->defaultModel = $this->propertyRoomSizeType; + } + +} diff --git a/app/Core/Repository/PropertyRoomType/PropertyRoomTypeRepository.php b/app/Core/Repository/PropertyRoomType/PropertyRoomTypeRepository.php new file mode 100644 index 0000000..3e18246 --- /dev/null +++ b/app/Core/Repository/PropertyRoomType/PropertyRoomTypeRepository.php @@ -0,0 +1,19 @@ +propertyRoomType = $propertyRoomType; + $this->defaultModel = $this->propertyRoomType; + } + +} diff --git a/app/Core/Repository/PropertyRoomViewMapping/PropertyRoomViewMappingRepository.php b/app/Core/Repository/PropertyRoomViewMapping/PropertyRoomViewMappingRepository.php new file mode 100644 index 0000000..ea59fcb --- /dev/null +++ b/app/Core/Repository/PropertyRoomViewMapping/PropertyRoomViewMappingRepository.php @@ -0,0 +1,19 @@ +propertyRoomViewMapping = $propertyRoomViewMapping; + $this->defaultModel = $this->propertyRoomViewMapping; + } + +} diff --git a/app/Core/Repository/PropertyRoomViewType/PropertyRoomViewTypeRepository.php b/app/Core/Repository/PropertyRoomViewType/PropertyRoomViewTypeRepository.php new file mode 100644 index 0000000..063522b --- /dev/null +++ b/app/Core/Repository/PropertyRoomViewType/PropertyRoomViewTypeRepository.php @@ -0,0 +1,20 @@ +propertyRoomViewType = $propertyRoomViewType; + $this->defaultModel = $this->propertyRoomViewType; + } + +} diff --git a/app/Core/Repository/PropertySummary/PropertySummaryRepository.php b/app/Core/Repository/PropertySummary/PropertySummaryRepository.php new file mode 100644 index 0000000..50fbdbc --- /dev/null +++ b/app/Core/Repository/PropertySummary/PropertySummaryRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyType/PropertyTypeRepository.php b/app/Core/Repository/PropertyType/PropertyTypeRepository.php new file mode 100644 index 0000000..3af30f4 --- /dev/null +++ b/app/Core/Repository/PropertyType/PropertyTypeRepository.php @@ -0,0 +1,20 @@ +propertyType = $propertyType; + $this->defaultModel = $this->propertyType; + } + +} diff --git a/app/Core/Repository/PropertyUnit/PropertyUnitRepository.php b/app/Core/Repository/PropertyUnit/PropertyUnitRepository.php new file mode 100644 index 0000000..483b376 --- /dev/null +++ b/app/Core/Repository/PropertyUnit/PropertyUnitRepository.php @@ -0,0 +1,19 @@ +propertyUnit = $propertyUnit; + $this->defaultModel = $this->propertyUnit; + } + +} diff --git a/app/Core/Repository/PropertyWeb/PropertyWebComponentMappingRepository.php b/app/Core/Repository/PropertyWeb/PropertyWebComponentMappingRepository.php new file mode 100644 index 0000000..f72d039 --- /dev/null +++ b/app/Core/Repository/PropertyWeb/PropertyWebComponentMappingRepository.php @@ -0,0 +1,19 @@ +propertyWebComponentMapping = $propertyWebComponentMapping; + $this->defaultModel = $this->propertyWebComponentMapping; + } + +} diff --git a/app/Core/Repository/PropertyWeb/PropertyWebComponentRepository.php b/app/Core/Repository/PropertyWeb/PropertyWebComponentRepository.php new file mode 100644 index 0000000..d2793b6 --- /dev/null +++ b/app/Core/Repository/PropertyWeb/PropertyWebComponentRepository.php @@ -0,0 +1,19 @@ +propertyWebComponent = $propertyWebComponent; + $this->defaultModel = $this->propertyWebComponent; + } + +} diff --git a/app/Core/Repository/PropertyWeb/PropertyWebGroupRepository.php b/app/Core/Repository/PropertyWeb/PropertyWebGroupRepository.php new file mode 100644 index 0000000..cb83ad7 --- /dev/null +++ b/app/Core/Repository/PropertyWeb/PropertyWebGroupRepository.php @@ -0,0 +1,19 @@ +propertyWebGroup = $propertyWebGroup; + $this->defaultModel = $this->propertyWebGroup; + } + +} diff --git a/app/Core/Repository/PropertyWeb/PropertyWebMetaMappingRepository.php b/app/Core/Repository/PropertyWeb/PropertyWebMetaMappingRepository.php new file mode 100644 index 0000000..17c6c06 --- /dev/null +++ b/app/Core/Repository/PropertyWeb/PropertyWebMetaMappingRepository.php @@ -0,0 +1,19 @@ +propertyWebMetaMapping = $propertyWebMetaMapping; + $this->defaultModel = $this->propertyWebMetaMapping; + } + +} diff --git a/app/Core/Repository/PropertyWeb/PropertyWebMetaRepository.php b/app/Core/Repository/PropertyWeb/PropertyWebMetaRepository.php new file mode 100644 index 0000000..bf8051a --- /dev/null +++ b/app/Core/Repository/PropertyWeb/PropertyWebMetaRepository.php @@ -0,0 +1,19 @@ +propertyWebMeta = $propertyWebMeta; + $this->defaultModel = $this->propertyWebMeta; + } + +} diff --git a/app/Core/Repository/PropertyWeb/PropertyWebMetaTagRepository.php b/app/Core/Repository/PropertyWeb/PropertyWebMetaTagRepository.php new file mode 100644 index 0000000..c7e18d4 --- /dev/null +++ b/app/Core/Repository/PropertyWeb/PropertyWebMetaTagRepository.php @@ -0,0 +1,19 @@ +propertyWebMetaTag = $propertyWebMetaTag; + $this->defaultModel = $this->propertyWebMetaTag; + } + +} diff --git a/app/Core/Repository/PropertyWeb/PropertyWebRepository.php b/app/Core/Repository/PropertyWeb/PropertyWebRepository.php new file mode 100644 index 0000000..cc5aed8 --- /dev/null +++ b/app/Core/Repository/PropertyWeb/PropertyWebRepository.php @@ -0,0 +1,19 @@ +propertyWeb = $propertyWeb; + $this->defaultModel = $this->propertyWeb; + } + +} diff --git a/app/Core/Repository/PropertyWeb/PropertyWebReviewRepository.php b/app/Core/Repository/PropertyWeb/PropertyWebReviewRepository.php new file mode 100644 index 0000000..1203780 --- /dev/null +++ b/app/Core/Repository/PropertyWeb/PropertyWebReviewRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyWebAboutUs/PropertyWebAboutUsRepository.php b/app/Core/Repository/PropertyWebAboutUs/PropertyWebAboutUsRepository.php new file mode 100644 index 0000000..da5cb92 --- /dev/null +++ b/app/Core/Repository/PropertyWebAboutUs/PropertyWebAboutUsRepository.php @@ -0,0 +1,18 @@ +propertyWebAboutUs = $propertyWebAboutUs; + $this->defaultModel = $this->propertyWebAboutUs; + } + +} diff --git a/app/Core/Repository/PropertyWebColorMapping/PropertyWebColorMappingRepository.php b/app/Core/Repository/PropertyWebColorMapping/PropertyWebColorMappingRepository.php new file mode 100644 index 0000000..a24c127 --- /dev/null +++ b/app/Core/Repository/PropertyWebColorMapping/PropertyWebColorMappingRepository.php @@ -0,0 +1,19 @@ +propertyWebColorMapping = $propertyWebColorMapping; + $this->defaultModel = $this->propertyWebColorMapping; + } + +} diff --git a/app/Core/Repository/PropertyWebContent/PropertyWebContentCategoryRepository.php b/app/Core/Repository/PropertyWebContent/PropertyWebContentCategoryRepository.php new file mode 100644 index 0000000..e6c6e7a --- /dev/null +++ b/app/Core/Repository/PropertyWebContent/PropertyWebContentCategoryRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyWebContent/PropertyWebContentRepository.php b/app/Core/Repository/PropertyWebContent/PropertyWebContentRepository.php new file mode 100644 index 0000000..321d460 --- /dev/null +++ b/app/Core/Repository/PropertyWebContent/PropertyWebContentRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyWebLanguageMapping/PropertyWebLanguageMappingRepository.php b/app/Core/Repository/PropertyWebLanguageMapping/PropertyWebLanguageMappingRepository.php new file mode 100644 index 0000000..e7f3b82 --- /dev/null +++ b/app/Core/Repository/PropertyWebLanguageMapping/PropertyWebLanguageMappingRepository.php @@ -0,0 +1,19 @@ +propertyWebLanguageMapping = $propertyWebLanguageMapping; + $this->defaultModel = $this->propertyWebLanguageMapping; + } + +} diff --git a/app/Core/Repository/PropertyWebLog/PropertyWebLogRepository.php b/app/Core/Repository/PropertyWebLog/PropertyWebLogRepository.php new file mode 100644 index 0000000..ec64dab --- /dev/null +++ b/app/Core/Repository/PropertyWebLog/PropertyWebLogRepository.php @@ -0,0 +1,19 @@ +propertyWebLog = $propertyWebLog; + $this->defaultModel = $this->propertyWebLog; + } + +} diff --git a/app/Core/Repository/PropertyWebMenu/PropertyWebMenuRepository.php b/app/Core/Repository/PropertyWebMenu/PropertyWebMenuRepository.php new file mode 100644 index 0000000..2123b78 --- /dev/null +++ b/app/Core/Repository/PropertyWebMenu/PropertyWebMenuRepository.php @@ -0,0 +1,19 @@ +propertyWebMenu = $propertyWebMenu; + $this->defaultModel = $this->propertyWebMenu; + } + +} diff --git a/app/Core/Repository/PropertyWebMenuMapping/PropertyWebMenuMappingRepository.php b/app/Core/Repository/PropertyWebMenuMapping/PropertyWebMenuMappingRepository.php new file mode 100644 index 0000000..884c3db --- /dev/null +++ b/app/Core/Repository/PropertyWebMenuMapping/PropertyWebMenuMappingRepository.php @@ -0,0 +1,19 @@ +propertyWebMenuMapping = $propertyWebMenuMapping; + $this->defaultModel = $this->propertyWebMenuMapping; + } + +} diff --git a/app/Core/Repository/PropertyWebPhotoMapping/PropertyWebPhotoMappingRepository.php b/app/Core/Repository/PropertyWebPhotoMapping/PropertyWebPhotoMappingRepository.php new file mode 100644 index 0000000..4456526 --- /dev/null +++ b/app/Core/Repository/PropertyWebPhotoMapping/PropertyWebPhotoMappingRepository.php @@ -0,0 +1,19 @@ +propertyWebPhotoMapping = $propertyWebPhotoMapping; + $this->defaultModel = $this->propertyWebPhotoMapping; + } + +} diff --git a/app/Core/Repository/PropertyWebPlaceMapping/PropertyWebPlaceMappingRepository.php b/app/Core/Repository/PropertyWebPlaceMapping/PropertyWebPlaceMappingRepository.php new file mode 100644 index 0000000..54c6458 --- /dev/null +++ b/app/Core/Repository/PropertyWebPlaceMapping/PropertyWebPlaceMappingRepository.php @@ -0,0 +1,19 @@ +propertyWebPlaceMapping = $propertyWebPlaceMapping; + $this->defaultModel = $this->propertyWebPlaceMapping; + } + +} diff --git a/app/Core/Repository/PropertyWebPopup/PropertyWebPopupRepository.php b/app/Core/Repository/PropertyWebPopup/PropertyWebPopupRepository.php new file mode 100644 index 0000000..3009d54 --- /dev/null +++ b/app/Core/Repository/PropertyWebPopup/PropertyWebPopupRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/PropertyWebRoomMapping/PropertyWebRoomMappingRepository.php b/app/Core/Repository/PropertyWebRoomMapping/PropertyWebRoomMappingRepository.php new file mode 100644 index 0000000..510eeab --- /dev/null +++ b/app/Core/Repository/PropertyWebRoomMapping/PropertyWebRoomMappingRepository.php @@ -0,0 +1,19 @@ +propertyWebRoomMapping = $propertyWebRoomMapping; + $this->defaultModel = $this->propertyWebRoomMapping; + } + +} diff --git a/app/Core/Repository/PropertyWebSetup/PropertyWebSetupRepository.php b/app/Core/Repository/PropertyWebSetup/PropertyWebSetupRepository.php new file mode 100644 index 0000000..2997cb3 --- /dev/null +++ b/app/Core/Repository/PropertyWebSetup/PropertyWebSetupRepository.php @@ -0,0 +1,19 @@ +propertyWebSetup = $propertyWebSetup; + $this->defaultModel = $this->propertyWebSetup; + } + +} diff --git a/app/Core/Repository/PropertyWebTemplate/PropertyWebTemplateRepository.php b/app/Core/Repository/PropertyWebTemplate/PropertyWebTemplateRepository.php new file mode 100644 index 0000000..19876bf --- /dev/null +++ b/app/Core/Repository/PropertyWebTemplate/PropertyWebTemplateRepository.php @@ -0,0 +1,19 @@ +propertyWebTemplate = $propertyWebTemplate; + $this->defaultModel = $this->propertyWebTemplate; + } + +} diff --git a/app/Core/Repository/ReputationManagement/PropertyReviewCategoryRepository.php b/app/Core/Repository/ReputationManagement/PropertyReviewCategoryRepository.php new file mode 100644 index 0000000..4fff1ad --- /dev/null +++ b/app/Core/Repository/ReputationManagement/PropertyReviewCategoryRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/ReputationManagement/PropertyReviewChannelMappingRepository.php b/app/Core/Repository/ReputationManagement/PropertyReviewChannelMappingRepository.php new file mode 100644 index 0000000..e69bb04 --- /dev/null +++ b/app/Core/Repository/ReputationManagement/PropertyReviewChannelMappingRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/ReputationManagement/PropertyReviewChannelRepository.php b/app/Core/Repository/ReputationManagement/PropertyReviewChannelRepository.php new file mode 100644 index 0000000..0d20b32 --- /dev/null +++ b/app/Core/Repository/ReputationManagement/PropertyReviewChannelRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/ReputationManagement/PropertyReviewRepository.php b/app/Core/Repository/ReputationManagement/PropertyReviewRepository.php new file mode 100644 index 0000000..7f6d85b --- /dev/null +++ b/app/Core/Repository/ReputationManagement/PropertyReviewRepository.php @@ -0,0 +1,19 @@ +model = $model; + $this->defaultModel = $this->model; + } + +} diff --git a/app/Core/Repository/ServiceLog/ServiceLogRepository.php b/app/Core/Repository/ServiceLog/ServiceLogRepository.php new file mode 100644 index 0000000..f2da8c2 --- /dev/null +++ b/app/Core/Repository/ServiceLog/ServiceLogRepository.php @@ -0,0 +1,19 @@ +siteConfig = $serviceLog; + $this->defaultModel = $this->siteConfig; + } + +} diff --git a/app/Core/Repository/SiteConfig/SiteConfigRepository.php b/app/Core/Repository/SiteConfig/SiteConfigRepository.php new file mode 100644 index 0000000..6fa51bd --- /dev/null +++ b/app/Core/Repository/SiteConfig/SiteConfigRepository.php @@ -0,0 +1,19 @@ +siteConfig = $siteConfig; + $this->defaultModel = $this->siteConfig; + } + +} diff --git a/app/Core/Repository/TempProperty/TempPropertyRepository.php b/app/Core/Repository/TempProperty/TempPropertyRepository.php new file mode 100644 index 0000000..0719606 --- /dev/null +++ b/app/Core/Repository/TempProperty/TempPropertyRepository.php @@ -0,0 +1,70 @@ +tempProperty = $tempProperty; + $this->defaultModel = $this->tempProperty; + } + + public function searchTempProperty($param, $column = ['*']) + { + $get = $this->tempProperty->on('mysql') + ->orWhere( + function ($query) use ($param) { + if (isset($param['orWhere'])) + { + foreach ($param['orWhere'] as $key => $value) + { + $query->orWhere($value['field'], $value['condition'], '%'.$value['value'].'%' ); + } + } + } + ); + + if (isset($param['orWhere'])) + { + $rawBindings = []; + $increment = 1; + $likeTokens = [':fieldName',':fieldName%','%:fieldName','%:fieldName%']; + + $rawSqlPiece = 'case '; + foreach ($likeTokens as $perLikeToken) + { + foreach ($param['orWhere'] as $key => $value) + { + $rawSqlPiece.= 'when '.$value['field'].' LIKE ? then '.$increment.' '; + $increment++; + } + } + foreach ($likeTokens as $perLikeToken) + { + foreach ($param['orWhere'] as $key => $value) + { + $rawBindings[] = str_replace(":fieldName",$value['value'],$perLikeToken); + } + } + + $rawSqlPiece .= 'end'; + } + + if (isset($rawSqlPiece)) + { + $get->orderByRaw($rawSqlPiece . " ASC",$rawBindings); + } + $get->take(10); + $get = isset($param["firstRow"]) ? $get->first($column) : $get->get($column); + + return ($get) ? $get->toArray() : []; + } + +} diff --git a/app/Core/Repository/User/UserRepository.php b/app/Core/Repository/User/UserRepository.php new file mode 100644 index 0000000..bc21ef4 --- /dev/null +++ b/app/Core/Repository/User/UserRepository.php @@ -0,0 +1,19 @@ +user = $user; + $this->defaultModel = $this->user; + } + +} diff --git a/app/Core/Repository/UserPropertyMapping/UserPropertyMappingRepository.php b/app/Core/Repository/UserPropertyMapping/UserPropertyMappingRepository.php new file mode 100644 index 0000000..dd34061 --- /dev/null +++ b/app/Core/Repository/UserPropertyMapping/UserPropertyMappingRepository.php @@ -0,0 +1,19 @@ +userPropertyMapping = $userPropertyMapping; + $this->defaultModel = $this->userPropertyMapping; + } + +} diff --git a/app/Core/Repository/VWDestination/VWDestinationRepository.php b/app/Core/Repository/VWDestination/VWDestinationRepository.php new file mode 100644 index 0000000..1134c10 --- /dev/null +++ b/app/Core/Repository/VWDestination/VWDestinationRepository.php @@ -0,0 +1,19 @@ +VWDestination = $VWDestination; + $this->defaultModel = $this->VWDestination; + } + +} diff --git a/app/Core/Service/ApiAccessTokenService.php b/app/Core/Service/ApiAccessTokenService.php new file mode 100644 index 0000000..12e2a53 --- /dev/null +++ b/app/Core/Service/ApiAccessTokenService.php @@ -0,0 +1,114 @@ +apiAccessTokenRepository = $apiAccessTokenRepository; + + } + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $data = $this->apiAccessTokenRepository->findByCriteria($param, $column); + if(!$data){ + throw new ApiErrorException(lang('An unknown error occurred')); + } + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $tokenData = + [ + "token" => fillOnUndefined($param, "token"), + "expire_date" => fillOnUndefined($param, "expire_date"), + "user_id" => fillOnUndefined($param, "user_id"), + "invalidate" => fillOnUndefined($param, "invalidate"), + "created_at" => time(), + "updated_at" => time(), + + ]; + $tokenCreateResult = $this->apiAccessTokenRepository->create($tokenData); + + if ($tokenCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $tokenCreateResult["data"]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null ]; + try { + + $updateResult = $this->apiAccessTokenRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $updateResult["data"]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } +} diff --git a/app/Core/Service/ApplicationCacheService.php b/app/Core/Service/ApplicationCacheService.php new file mode 100644 index 0000000..fc57372 --- /dev/null +++ b/app/Core/Service/ApplicationCacheService.php @@ -0,0 +1,103 @@ +applicationCacheRepository = $applicationCacheRepository ; + $this->request = $request ; + } + + public function applicationCacheCreate($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + if(!fillOnUndefined($params, 'token')){ + throw new ApiErrorException(lang('Token Required')); + } + $cacheId = md5($params['token']) ; + unset($params['token']); + + $cacheResult = $this->applicationCacheRepository->storeCache($cacheId, $params); + $response = [ + 'status' => true, + 'data' => $cacheResult, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + + + } + + public function getApplicationCache($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $token = $this->request->header('authToken'); + + + if(!$token){ + throw new ApiErrorException(lang('Token Required')); + } + $cacheId = md5($token) ; + + + $cacheResult = $this->applicationCacheRepository->getCache($cacheId); + + if(!$cacheResult){ + + throw new ApiErrorException(lang('Cache Info not found')); + } + + $response = [ + 'status' => true, + 'data' => $cacheResult, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + + + } +} \ No newline at end of file diff --git a/app/Core/Service/BookingAddonService.php b/app/Core/Service/BookingAddonService.php new file mode 100644 index 0000000..2df790f --- /dev/null +++ b/app/Core/Service/BookingAddonService.php @@ -0,0 +1,155 @@ +bookingAddonRepository = $bookingAddonRepository; + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $insertData = [ + 'booking_id' => fillOnUndefined($params, 'booking_id'), + 'property_channel_addon_id' => fillOnUndefined($params, 'property_channel_addon_id'), + 'count' => fillOnUndefined($params, 'count'), + 'amount' => fillOnUndefined($params, 'amount'), + 'total' => fillOnUndefined($params, 'total'), + 'currency_code' => fillOnUndefined($params, 'currency_code'), + 'attribute' => fillOnUndefined($params, 'attribute'), + 'status' => fillOnUndefined($params, 'status', 1), + ]; + + $createResult = $this->bookingAddonRepository->create($insertData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $createResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingAddonRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->bookingAddonRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function delete($ids = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteResult = $this->bookingAddonRepository->destroy($ids); + + if ($deleteResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult['data'] // affected row count + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + +} diff --git a/app/Core/Service/BookingContactService.php b/app/Core/Service/BookingContactService.php new file mode 100644 index 0000000..c22dcee --- /dev/null +++ b/app/Core/Service/BookingContactService.php @@ -0,0 +1,137 @@ +bookingContactRepository = $bookingContactRepository; + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $insertData = [ + 'booking_id' => fillOnUndefined($params, 'booking_id'), + 'name' => fillOnUndefined($params, 'name'), + 'surname' => fillOnUndefined($params, 'surname'), + 'phone_code' => fillOnUndefined($params, 'phone_code'), + 'phone_number' => fillOnUndefined($params, 'phone_number'), + 'email' => fillOnUndefined($params, 'email'), + 'country_code' => fillOnUndefined($params, 'country_code'), + 'note' => fillOnUndefined($params, 'note'), + 'invoice_request' => fillOnUndefined($params, 'invoice_request'), + 'language_code' => fillOnUndefined($params, 'language_code', 'en'), + 'extra_param' => fillOnUndefined($params, 'extra_param'), + 'status' => fillOnUndefined($params, 'status', 1), + ]; + + $userCreateResult = $this->bookingContactRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingContactRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->bookingContactRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/BookingPaymentService.php b/app/Core/Service/BookingPaymentService.php new file mode 100644 index 0000000..d465207 --- /dev/null +++ b/app/Core/Service/BookingPaymentService.php @@ -0,0 +1,386 @@ +bookingPaymentRepository = $bookingPaymentRepository; + $this->bookingPaymentDataRepository = $bookingPaymentDataRepository; + $this->bookingPaymentDataCheckRepository = $bookingPaymentDataCheckRepository; + $this->bookingRepository = $bookingRepository ; + $this->propertyPaymentMappingRepository = $propertyPaymentMappingRepository ; + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $insertData = [ + 'booking_id' => fillOnUndefined($params, 'booking_id'), + 'payment_code' => fillOnUndefined($params, 'payment_code'), + 'payment_type_code' => fillOnUndefined($params, 'payment_type_code'), + 'payment_source_code' => fillOnUndefined($params, 'payment_source_code'), + 'extra_param' => fillOnUndefined($params, 'extra_param'), + 'total' => fillOnUndefined($params, 'total'), + 'currency_code' => fillOnUndefined($params, 'currency_code'), + 'status' => fillOnUndefined($params, 'status', 1), + ]; + + $userCreateResult = $this->bookingPaymentRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingPaymentRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->bookingPaymentRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPaymentDashboard($param){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + + $requestData = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')] + ], + 'orderBy' => [["field" => "id", "value" => "DESC"]], + 'with' => ['bookingPaymentTransaction'], + ]; + + $bookingList = $this->bookingRepository->findByCriteria($requestData, ['id', 'channel_id','booking_code', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'created_at', 'updated_at', 'status']); + $bookings = collect($bookingList); + + $bookingPayments = $bookings->map(function ($booking){ + + $paymentTransactions = collect($booking['booking_payment_transaction']) ; + $allTransactions = $paymentTransactions->count(); + $confirmedTransactions = $paymentTransactions->where('status' , '=' , 1)->count(); + $pendingTransactions = $paymentTransactions->where('status' , '=' , 2)->count(); + $errorTransactions = $paymentTransactions->where('status' , '=' , 0)->count(); + $startTransactions = $paymentTransactions->where('status' , '=' , 3)->count(); + $cancelTransactions = $paymentTransactions->where('status' , '=' , 4)->count(); + + return [ + 'booking_id' => $booking['id'], + 'all_transactions' => $allTransactions, + 'confirmed_transactions' => $confirmedTransactions, + 'pending_transactions' => $pendingTransactions, + 'error_transactions' => $errorTransactions, + 'start_transactions' => $startTransactions, + 'cancel_transactions' => $cancelTransactions, + ] ; + + })->values()->all() ; + + $requestPropertyPaymentMapping = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + ]; + + $propertyPaymentMappingList = $this->propertyPaymentMappingRepository->findByCriteria($requestPropertyPaymentMapping); + $paymentMappings = collect($propertyPaymentMappingList)->count(); + + $totalAllTransactions = collect($bookingPayments)->sum('all_transactions') ; + $totalConfirmedTransactions = collect($bookingPayments)->sum('confirmed_transactions') ; + $conversionRate = $totalAllTransactions ? ($totalConfirmedTransactions * 100) / $totalAllTransactions : 0; + $totalAllTransactions = collect($bookingPayments)->sum('all_transactions') ; + + + $responseData = [ + 'all_transactions' => $totalAllTransactions, + 'confirmed_transactions' => $totalConfirmedTransactions, + 'conversion_rate' => number_format($conversionRate, 2), + 'active_pos_count' => $paymentMappings + ]; + + $response = [ + 'status' => true, + 'data' => $responseData + ]; + + } 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); + } + + + public function createPaymentData($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $insertData = [ + 'booking_id' => fillOnUndefined($params, 'booking_id'), + 'type' => fillOnUndefined($params, 'type'), + 'data' => fillOnUndefined($params, 'data'), + 'status' => fillOnUndefined($params, 'status', 1), + ]; + + $createResult = $this->bookingPaymentDataRepository->create($insertData); + + if ($createResult['status'] != 'success') { + throw new Exception($createResult['message']); + } + $createData = $createResult["data"]; + $response = [ + 'status' => true, + 'data' => $createData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectPaymentData($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingPaymentDataRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function updatePaymentData($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->bookingPaymentDataRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function createPaymentDataCheck($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + + $insertData = [ + 'booking_id' => fillOnUndefined($params, 'booking_id'), + 'code' => fillOnUndefined($params, 'code'), + 'status' => fillOnUndefined($params, 'status', 2), + 'request_time' => fillOnUndefined($params, 'request_time'), + 'expiry_time' => fillOnUndefined($params, 'expiry_time'), + 'confirm_time' => fillOnUndefined($params, 'confirm_time'), + 'created_by' => fillOnUndefined($params, 'created_by'), + 'updated_by' => fillOnUndefined($params, 'updated_by'), + + ]; + + $userCreateResult = $this->bookingPaymentDataCheckRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectPaymentDataCheck($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingPaymentDataCheckRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + +} diff --git a/app/Core/Service/BookingRoomPaxService.php b/app/Core/Service/BookingRoomPaxService.php new file mode 100644 index 0000000..2d910c3 --- /dev/null +++ b/app/Core/Service/BookingRoomPaxService.php @@ -0,0 +1,131 @@ +bookingRoomPaxRepository = $bookingRoomPaxRepository; + + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $insertData = [ + 'booking_id' => fillOnUndefined($params, 'booking_id'), + 'booking_room_id' => fillOnUndefined($params, 'booking_room_id'), + 'type' => fillOnUndefined($params, 'type'), + 'name' => fillOnUndefined($params, 'name'), + 'surname' => fillOnUndefined($params, 'surname'), + 'gender' => fillOnUndefined($params, 'gender'), + 'citizen' => fillOnUndefined($params, 'citizen'), + 'birth_date' => fillOnUndefined($params, 'birth_date'), + 'status' => fillOnUndefined($params, 'status', 1), + ]; + + $userCreateResult = $this->bookingRoomPaxRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingRoomPaxRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->bookingRoomPaxRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/BookingRoomService.php b/app/Core/Service/BookingRoomService.php new file mode 100644 index 0000000..7d6b7a5 --- /dev/null +++ b/app/Core/Service/BookingRoomService.php @@ -0,0 +1,144 @@ +bookingRoomRepository = $bookingRoomRepository; + + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $insertData = [ + 'booking_id' => fillOnUndefined($params, 'booking_id'), + 'room_order_number' => fillOnUndefined($params, 'room_order_number'), + 'occupancy_code' => fillOnUndefined($params, 'occupancy_code'), + 'checkin_date' => fillOnUndefined($params, 'checkin_date'), + 'checkout_date' => fillOnUndefined($params, 'checkout_date'), + 'rate_key' => fillOnUndefined($params, 'rate_key'), + 'rate_key_code' => fillOnUndefined($params, 'rate_key_code'), + 'availability_id' => fillOnUndefined($params, 'availability_id'), + 'availability_code' => fillOnUndefined($params, 'availability_code'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_name' => fillOnUndefined($params, 'room_name'), + 'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id'), + 'room_rate_name' => fillOnUndefined($params, 'room_rate_name'), + 'cancellation_policy' => fillOnUndefined($params, 'cancellation_policy'), + 'payment_type_code' => fillOnUndefined($params, 'payment_type_code'), + 'daily_amount' => fillOnUndefined($params, 'daily_amount'), + 'extra_param' => fillOnUndefined($params, 'extra_param'), + 'rate_detail' => fillOnUndefined($params, 'rate_detail'), + 'amount' => fillOnUndefined($params, 'amount'), + 'discount_amount' => fillOnUndefined($params, 'discount_amount'), + 'total' => fillOnUndefined($params, 'total'), + 'currency_code' => fillOnUndefined($params, 'currency_code'), + 'status' => fillOnUndefined($params, 'status', 1), + + ]; + + $userCreateResult = $this->bookingRoomRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingRoomRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->bookingRoomRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/BookingService.php b/app/Core/Service/BookingService.php new file mode 100644 index 0000000..1dae9d5 --- /dev/null +++ b/app/Core/Service/BookingService.php @@ -0,0 +1,952 @@ +bookingRepository = $bookingRepository; + $this->bookingRoomRepository = $bookingRoomRepository; + $this->bookingRoomPaxRepository = $bookingRoomPaxRepository; + $this->bookingContactRepository = $bookingContactRepository; + $this->bookingPaymentRepository = $bookingPaymentRepository; + $this->bookingPaymentDataCheckRepository = $bookingPaymentDataCheckRepository; + $this->paymentTransactionRepository = $paymentTransactionRepository; + $this->bookingStatusRepository = $bookingStatusRepository; + $this->propertyBookingEngineRepository = $propertyBookingEngineRepository; + $this->propertyChannelRepository = $propertyChannelRepository; + $this->propertyBookingPaymentTypeRepository = $propertyBookingPaymentTypeRepository; + $this->mailer = $mailer; + + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $insertData = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + 'channel_manager_id' => fillOnUndefined($params, 'channel_manager_id'), + 'booking_code' => fillOnUndefined($params, 'booking_code'), + 'channel_booking_code' => fillOnUndefined($params, 'channel_booking_code'), + 'search_key' => fillOnUndefined($params, 'search_key'), + 'checkin_date' => fillOnUndefined($params, 'checkin_date'), + 'checkout_date' => fillOnUndefined($params, 'checkout_date'), + 'rooms' => fillOnUndefined($params, 'rooms'), + 'payment_type_code' => fillOnUndefined($params, 'payment_type_code'), + 'room_amount' => fillOnUndefined($params, 'room_amount'), + 'addon_amount' => fillOnUndefined($params, 'addon_amount', 0), + 'discount_amount' => fillOnUndefined($params, 'discount_amount', 0), + 'total' => fillOnUndefined($params, 'total'), + 'currency_code' => fillOnUndefined($params, 'currency_code'), + 'channel_discount' => fillOnUndefined($params, 'channel_discount'), + 'channel_markup' => fillOnUndefined($params, 'channel_markup'), + 'channel_currency_code' => fillOnUndefined($params, 'channel_currency_code'), + 'channel_currency_exchange' => fillOnUndefined($params, 'channel_currency_exchange'), + 'channel_token' => fillOnUndefined($params, 'channel_token'), + 'booking_engine_token' => fillOnUndefined($params, 'booking_engine_token'), + 'extra_param' => fillOnUndefined($params, 'extra_param'), + 'status' => fillOnUndefined($params, 'status', 1), + 'reservation_time' => fillOnUndefined($params, 'reservation_time', Carbon::now()->timestamp), + 'coupon_code' => fillOnUndefined($params,'coupon_code') + ]; + + $createResult = $this->bookingRepository->create($insertData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $createData = $createResult['data']; + $response = [ + 'status' => true, + 'data' => $createData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->bookingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult['data']; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getBookingList($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $getListCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ], + 'orderBy' => [/*['field' => 'is_viewed', 'value' => 'ASC'],*/ + ['field' => 'id', 'value' => 'DESC']], + 'with' => ['bookingContact', 'bookingChannel', 'bookingPayment', 'bookingPaymentType', 'bookingStatus', 'bookingActiveMessageCount'], + ]; + + if (isset($param['filter']) && !empty($param['filter'])) { + + $filterParams = [ + 'channel_id' => [ + 'criteriaType' => 'EQUAL' + ], + 'booking_code' => [ + 'criteriaType' => 'LIKE' + ], + 'payment_type_code' => [ + 'criteriaType' => 'LIKE' + ], + 'status' => [ + 'criteriaType' => 'EQUAL' + ], + ]; + + foreach ($param['filter'] as $inputName => $inputValue) { + if (!is_null($inputValue) && key_exists($inputName, $filterParams)) { + if ($filterParams[$inputName]['criteriaType'] == 'EQUAL') { + $getListCriteria['criteria'][] = ['field' => $inputName, 'condition' => '=', 'value' => $inputValue]; + } elseif ($filterParams[$inputName]['criteriaType'] == 'LIKE') { + $getListCriteria['criteria'][] = ['field' => $inputName, 'condition' => 'LIKE', 'value' => '%' . $inputValue . '%']; + } + } + } + + + if (isset($param['filter']['date_type']) && isset($param['filter']['date_range'])) { + if (in_array($param['filter']['date_type'], ['checkin_date', 'checkout_date'])) { + $dateRange = explode(' ', $param['filter']['date_range']); + $getListCriteria['criteria'][] = ['field' => $param['filter']['date_type'], 'condition' => '>=', 'value' => Carbon::parse($dateRange[0])->toDateString()]; + $getListCriteria['criteria'][] = ['field' => $param['filter']['date_type'], 'condition' => '<', 'value' => Carbon::parse($dateRange[1])->addDay()->toDateString()]; + } + + if (in_array($param['filter']['date_type'], ['reservation_time'])) { + $dateRange = explode(' ', $param['filter']['date_range']); + $getListCriteria['criteria'][] = ['field' => $param['filter']['date_type'], 'condition' => '>=', 'value' => Carbon::parse($dateRange[0])->unix()]; + $getListCriteria['criteria'][] = ['field' => $param['filter']['date_type'], 'condition' => '<', 'value' => Carbon::parse($dateRange[1])->addDay()->unix()]; + } + } + + $getListCriteria['take'] = 10000; + + } else { + $getListCriteria['take'] = 200; + } + + $booking = $this->select($getListCriteria, ['id', 'channel_id', 'booking_code', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'reservation_time', 'created_at', 'updated_at', 'status', 'is_viewed', 'channel_booking_code']); + + if ($booking['status'] != 'success') { + throw new ApiErrorException('Property Booking Data not found'); + } + + $bookingStatusColor = [ + 0 => '#CC0000', + 1 => '#007E33', + 2 => '#FF8800', + 3 => '#9933CC', + ]; + + if (!empty($booking['data'])) { + foreach ($booking['data'] as $dataKey => $data) { + $booking['data'][$dataKey]['booking_active_message_count'] = count($data['booking_active_message_count']); + $booking['data'][$dataKey]['booking_status']['bg-color'] = isset($bookingStatusColor[$booking['data'][$dataKey]['status']]) ? $bookingStatusColor[$booking['data'][$dataKey]['status']] : '#E0E0E0'; + $booking['data'][$dataKey]['is_viewed'] = empty($data['is_viewed']) ? false : true; + } + } + + $response = [ + 'status' => true, + 'data' => $booking['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); + } + + + public function getPropertyBookingListFilter($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $filter = []; + + $propertyChannelParam = ['criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]]]; + $propertyChannel = $this->propertyChannelRepository->findByCriteria($propertyChannelParam, ['id', 'name', 'restriction']); + + $propertyChannel = array_filter($propertyChannel, function ($channel) use ($param) { + if (!empty($channel['restriction'])) { + $channelRestriction = json_decode($channel['restriction'], 1); + if (in_array($param['property_id'], $channelRestriction)) { + return $channel; + } + } else { + return $channel; + } + }); + + $filter['channel'] = array_values($propertyChannel); + + $propertyBookingPaymentTypeParam = ['criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]]]; + $propertyBookingPaymentType = $this->propertyBookingPaymentTypeRepository->findByCriteria($propertyBookingPaymentTypeParam, ['code', 'name', 'language_key']); + $filter['payment_type'] = $propertyBookingPaymentType; + + $bookingStatusParam = ['criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]]]; + $bookingStatus = $this->bookingStatusRepository->findByCriteria($bookingStatusParam, ['id', 'name', 'language_key']); + $filter['status'] = $bookingStatus; + + $filter['date_type'] = []; + $filter['date_type'][] = ['code' => 'checkin_date', 'name' => 'Checkin Date', 'language_key' => 'enw-filter-checkin_date']; + $filter['date_type'][] = ['code' => 'checkout_date', 'name' => 'Checkout Date', 'language_key' => 'enw-filter-checkout_date']; + $filter['date_type'][] = ['code' => 'reservation_time', 'name' => 'Booking Date', 'language_key' => 'enw-filter-booking_date']; + + $response = [ + 'status' => true, + 'data' => $filter + ]; + + } 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); + } + + public function getBookingDetail($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $requestData = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($param, 'booking_id')] + ], + 'with' => [ + 'bookingContact', 'bookingChannel', 'bookingAddon.propertyChannelAddon.propertyAddon.fact', + 'bookingRoom.roomRateMapping.propertyRoom.propertyRoomType', 'bookingRoom.roomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'bookingRoom.roomPax.paxCountry', 'bookingPayment', 'bookingPaymentType', 'bookingPaymentTransaction.paymentTypeMapping.paymentType', + 'bookingPaymentData', + 'bookingRoom.roomRateMapping.propertyRoom.propertyRoomBedGroup.propertyRoomBedType', 'bookingRoom.roomRateMapping.propertyRoom.smokingPreference.propertyFact', + 'bookingRoom.smokingFact','propertyBookingEngineSearch' + ], + 'firstRow' => true + ]; + + $booking = $this->select($requestData, ['id', 'property_id', 'channel_id', 'booking_code', 'search_key','channel_booking_code', 'coupon_code', 'reservation_time', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'created_at', 'updated_at', 'status', 'is_viewed']); + + if ($booking['status'] != 'success') { + throw new ApiErrorException('Property Booking Data not found'); + } + + $booking['data']['voucher'] = null; + $propertyBookingEngineParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $booking['data']['property_id']], + //['field' => 'channel_id', 'condition' => '=', 'value' => $booking['data']['channel_id']], + ], + 'firstRow' => true, + ]; + + $propertyBookingEngine = $this->propertyBookingEngineRepository->findByCriteria($propertyBookingEngineParam); + if (!empty($propertyBookingEngine)) { + $booking['data']['voucher'] = Config::get('app.bookingEngineUrl') . '/' . $propertyBookingEngine['token'] . '/' . fillOnUndefined($param, 'locale', 'en') . '/booking-detail/' . $booking['data']['booking_code']; + } + + + if (!empty($booking['data']) && !empty($booking['data']['booking_room'])) { + $bookingRooms = &$booking['data']['booking_room']; + + foreach ($bookingRooms as &$bookingRoom) { + $bookingRoom['cancellation_policy'] = json_decode($bookingRoom['cancellation_policy'], true); + $bookingRoom['rate_detail'] = json_decode($bookingRoom['rate_detail'], true); + + + //80678 + + //property_room_bed_group + //unset($bookingDetail['data']['booking_room'][$roomKey]['room_rate_mapping']['property_room']['property_room_bed_group']); + $propertyRoomBedGroup = collect($bookingRoom['room_rate_mapping']['property_room']['property_room_bed_group'])->groupBy('bed_group'); + $propertyRoomBedGroup = $propertyRoomBedGroup ? $propertyRoomBedGroup->toArray() : null; + + $bookingRoom['property_room_bed_group'] = null; + if(isset($propertyRoomBedGroup[$bookingRoom['property_room_bed_group_id']])) { + $bookingRoom['property_room_bed_group'] = $propertyRoomBedGroup[$bookingRoom['property_room_bed_group_id']]; + } + + } + } + + $booking = $booking['data']; + $booking['booking_payment_transaction'] = collect($booking['booking_payment_transaction'])->map(function ($value) { + $return = $value; + unset($return['params']); + unset($return['extra_params']); + unset($return['response']); + unset($return['extraParamsArray']); + unset($return['paramsArray']); + unset($return['responseArray']); + unset($return['payment_type_mapping']); + + $return['credit_card_number'] = isset($value['paramsArray']['creditCard']['number']) ? $value['paramsArray']['creditCard']['number'] : null; + $return['bank_name'] = isset($value['payment_type_mapping']['payment_type']) ? $value['payment_type_mapping']['payment_type']['name'] : null; + $return['bank_icon'] = isset($value['payment_type_mapping']['payment_type']) ? $value['payment_type_mapping']['payment_type']['icon'] : null; + $return['created_at'] = date('Y-m-d H:i:s', $value['created_at']); + $return['updated_at'] = date('Y-m-d H:i:s', $value['updated_at']); + return $return; + + }); + + $booking['isThereBookingPaymentData'] = !empty($booking['booking_payment_data']) ? true : false; + unset($booking['booking_payment_data']); + + $response = [ + 'status' => true, + 'data' => $booking + ]; + + } 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); + } + + public function getBookingListCount($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $requestData = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1] // status 1 = booking + ], + 'count' => true + + ]; + + if (fillOnUndefined($param, 'channel_id')) { + $requestData['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'channel_id')]; + } + + $booking = $this->select($requestData, ['id']); + + if ($booking['status'] != 'success') { + throw new ApiErrorException('Property Booking Count Data not found'); + } + $response = [ + 'status' => true, + 'data' => $booking['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); + } + + public function getTransactionList($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $requestData = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')] + ], + 'orderBy' => [['field' => 'id', 'value' => 'DESC']], + 'with' => ['bookingDetail.bookingContact', 'paymentTypeMapping.paymentType', 'paymentTransactionStatus', 'parentTransaction'] + ]; + + + if (isset($param['filter']) && !empty($param['filter'])) { + + $filterParams = [ + 'code' => [ + 'criteriaType' => 'LIKE' + ], + 'order_id' => [ + 'criteriaType' => 'LIKE' + ], + 'status' => [ + 'criteriaType' => 'EQUAL' + ], + ]; + + foreach ($param['filter'] as $inputName => $inputValue) { + if (!is_null($inputValue) && key_exists($inputName, $filterParams)) { + if ($filterParams[$inputName]['criteriaType'] == 'EQUAL') { + $requestData['criteria'][] = ['field' => $inputName, 'condition' => '=', 'value' => $inputValue]; + } elseif ($filterParams[$inputName]['criteriaType'] == 'LIKE') { + $requestData['criteria'][] = ['field' => $inputName, 'condition' => 'LIKE', 'value' => '%' . $inputValue . '%']; + } + } + } + + if (isset($param['filter']['date_range'])) { + $dateRange = explode(' ', $param['filter']['date_range']); + $requestData['criteria'][] = ['field' => 'created_at', 'condition' => '>=', 'value' => Carbon::parse($dateRange[0])->timestamp]; + $requestData['criteria'][] = ['field' => 'created_at', 'condition' => '<', 'value' => Carbon::parse($dateRange[1])->addDay()->timestamp]; + } + + $requestData['take'] = 10000; + + } else { + $requestData['take'] = 200; + } + + //dd($requestData); + + + $transactions = $this->paymentTransactionRepository->findByCriteria($requestData); + + $transactions = collect($transactions)->map(function ($value) { + + $return = $value; + unset($return['params']); + unset($return['extra_params']); + unset($return['response']); + unset($return['extraParamsArray']); + unset($return['paramsArray']); + unset($return['responseArray']); + unset($return['payment_type_mapping']); + + $return['credit_card_number'] = isset($value['paramsArray']['creditCard']['number']) ? $value['paramsArray']['creditCard']['number'] : null; + $return['bank_name'] = isset($value['payment_type_mapping']['payment_type']) ? $value['payment_type_mapping']['payment_type']['name'] : null; + $return['bank_icon'] = isset($value['payment_type_mapping']['payment_type']) ? $value['payment_type_mapping']['payment_type']['icon'] : null; + $return['created_at'] = date('Y-m-d H:i:s', $value['created_at']); + $return['updated_at'] = date('Y-m-d H:i:s', $value['updated_at']); + $return['payment_link'] = $value['status'] == 5 && $value['transaction_type'] != 'BKG' ? Config::get('app.paymentFormLink') . $value['order_id'] : null; + $return['status_language_key'] = isset($value['payment_transaction_status']['language_key']) ? $value['payment_transaction_status']['language_key'] : null; + if ($value['parent_transaction']) { + if ($value['parent_transaction']['status'] == 5) { + $return['id'] = $value['parent_transaction']['id']; + } + } + return $return; + + }); + + $response = [ + 'status' => true, + 'data' => $transactions + ]; + + } 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); + } + + public function getBookingDetailedList($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $requestData = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')] + ], + 'orderBy' => [['field' => 'id', 'value' => 'DESC']], + 'with' => ['bookingContact', 'bookingChannel', 'bookingRoom', 'bookingRoom.roomPax.paxCountry', 'bookingPayment', 'bookingPaymentType', 'bookingPaymentTransaction.paymentTypeMapping.paymentType'], + ]; + + if (fillOnUndefined($param, 'channel_id')) { + $requestData['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'channel_id')]; + } + + $booking = $this->select($requestData, ['id', 'channel_id', 'booking_code', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'created_at', 'updated_at', 'status']); + + if ($booking['status'] != 'success') { + throw new ApiErrorException('Property Booking Data not found'); + } + + $response = [ + 'status' => true, + 'data' => $booking['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); + } + + public function getBookingstatusList($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingStatusRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getBookingPayment($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + DB::beginTransaction(); + + try { + + //TODO: Validator + + $confirmCode = fillOnUndefined($param, 'confirmCode'); + + $requestData = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($param, 'booking_id')] + ], + 'with' => ['bookingPaymentData', 'bookingPaymentDataCheck'], + 'firstRow' => true + ]; + + $bookingDetail = $this->select($requestData, ['id', 'property_id', 'channel_id', 'channel_manager_id', 'search_key', 'booking_code', 'channel_booking_code', 'reservation_time', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'created_at', 'updated_at', 'status']); + + if ($bookingDetail['status'] != 'success') { + throw new ApiErrorException('Property Booking Data not found'); + } + + if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) { + throw new ApiErrorException('Booking not found'); + } + + $bookingDetail = $bookingDetail['data']; + + if (in_array($bookingDetail['channel_manager_id'], [null])) { + if (!in_array($bookingDetail['payment_type_code'], ['HTL', 'CHN'])) { + throw new ApiErrorException('Only available for pay at hotel'); + } + } elseif (in_array($bookingDetail['channel_manager_id'], [1, 2])) { + if (!in_array($bookingDetail['payment_type_code'], ['CRD', 'CHN'])) { + throw new ApiErrorException('Only available for pay at credit card'); + } + } + + if (empty($bookingDetail['booking_payment_data'])) { + throw new ApiErrorException('Payment information not available'); + } + + $bookingPaymentDataCheck = $bookingDetail['booking_payment_data_check']; + + + switch ($bookingDetail['channel_manager_id']) { + case '1': + + $bookingPaymentData = $bookingDetail['booking_payment_data']; + + $getBookingPaymentDataCollect = collect($bookingPaymentData); + $isChannelManagerPaymentData = $getBookingPaymentDataCollect->where('type', 'ch')->first(); + + if ($isChannelManagerPaymentData) { + $channelPaymentDataUrl = 'https://www.reseliva.com/siteBase/REST/tsr/?lang=tr&res_id=' . $bookingDetail['search_key'] . '&hash=' . $isChannelManagerPaymentData['data']; + + $response = [ + 'status' => true, + 'data' => [ + 'type' => 'redirect', + 'redirectUrl' => $channelPaymentDataUrl, + 'channel_manager_id' => $bookingDetail['channel_manager_id'] + ], + ]; + + } else { + throw new ApiErrorException('Payment data not found.'); + } + + break; + case '2': + + $isConfirmCodeRequire = true; + + if (empty($confirmCode) && $isConfirmCodeRequire) { + + if (count($bookingPaymentDataCheck) > 2) { + throw new ApiErrorException('Payment information can only be viewed 3 times'); + } + + $unlockCode = rand(1000, 9999); + $paymentDataCheckParam = [ + 'booking_id' => $bookingDetail['id'], + 'code' => md5($unlockCode), + 'status' => 2, + 'expiry_time' => Carbon::now()->addMinutes(30)->timestamp, + 'created_by' => fillOnUndefined($param, 'user_id'), + 'updated_by' => fillOnUndefined($param, 'user_id'), + ]; + + $createPaymentDataCheck = $this->bookingPaymentDataCheckRepository->create($paymentDataCheckParam); + + if ($createPaymentDataCheck['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'type' => 'code', + 'expiry_time' => Carbon::createFromTimestamp($paymentDataCheckParam['expiry_time'])->toDateTimeString(), + 'channel_manager_id' => $bookingDetail['channel_manager_id'] + //'code' => $unlockCode + ] + + ]; + + //BookingPaymentDataCode + $mailParams = [ + 'user_id' => $param['user_id'], + 'booking_id' => $bookingDetail['id'], + 'unlock_code' => $unlockCode + ]; + + $this->mailer->send(new BookingPaymentDataCodeMail($mailParams)); + //BookingPaymentDataCode + + } else { + + if ($isConfirmCodeRequire) { + + $bookingPaymentDataCheckCollect = collect($bookingPaymentDataCheck); + + $bookingPaymentDataCheckRow = $bookingPaymentDataCheckCollect + ->where('code', md5($confirmCode)) + ->where('status', 2) + ->where('expiry_time', '>', Carbon::now()->timestamp) + ->first(); + + if (empty($bookingPaymentDataCheckRow)) { + throw new ApiErrorException('Code could not be verified, please check again'); + } + + } + + $bookingPaymentData = $bookingDetail['booking_payment_data']; + + $getBookingPaymentDataCollect = collect($bookingPaymentData); + $isChannelManagerPaymentData = $getBookingPaymentDataCollect->where('type', 'ch')->sortByDesc('id')->first(); + + $channelService = App::make("App\Core\Service\ChannelManager\Channex"); + + + $sessionTokenParam = [ + 'session_token' => [ + //'scope' => 'card' + 'scope' => 'show_card' + ] + ]; + + $sessionToken = $channelService->getSessionToken($sessionTokenParam); + $sessionToken = $sessionToken['status'] ? $sessionToken['data']['id'] : null; + + $serviceCodeTokenParam = [ + 'session_token' => [ + //'scope' => 'service_code' + 'scope' => 'show_service_code' + ] + ]; + + $serviceCodeToken = $channelService->getSessionToken($serviceCodeTokenParam); + $serviceCodeToken = $serviceCodeToken['status'] ? $serviceCodeToken['data']['id'] : null; + + + $channelPaymentDataUrl = 'https://pci.channex.io/api/v1/show_card?card_token=' . $isChannelManagerPaymentData['data'] . '&session_token=' . $sessionToken . '&service_code_token=' . $serviceCodeToken; + + $response = [ + 'status' => true, + 'data' => [ + 'type' => 'redirect', + 'redirectUrl' => $channelPaymentDataUrl, + 'channel_manager_id' => $bookingDetail['channel_manager_id'] + ], + ]; + + } + + break; + default : + + if (empty($confirmCode)) { + + if (count($bookingPaymentDataCheck) > 2) { + throw new ApiErrorException('Payment information can only be viewed 3 times'); + } + + $unlockCode = rand(1000, 9999); + $paymentDataCheckParam = [ + 'booking_id' => $bookingDetail['id'], + 'code' => md5($unlockCode), + 'status' => 2, + 'expiry_time' => Carbon::now()->addMinutes(30)->timestamp, + 'created_by' => fillOnUndefined($param, 'user_id'), + 'updated_by' => fillOnUndefined($param, 'user_id'), + ]; + + $createPaymentDataCheck = $this->bookingPaymentDataCheckRepository->create($paymentDataCheckParam); + + if ($createPaymentDataCheck['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'type' => 'code', + 'expiry_time' => Carbon::createFromTimestamp($paymentDataCheckParam['expiry_time'])->toDateTimeString(), + 'channel_manager_id' => $bookingDetail['channel_manager_id'] + //'code' => $unlockCode + ] + + ]; + + //BookingPaymentDataCode + $mailParams = [ + 'user_id' => $param['user_id'], + 'booking_id' => $bookingDetail['id'], + 'unlock_code' => $unlockCode + ]; + + $this->mailer->send(new BookingPaymentDataCodeMail($mailParams)); + //$this->mailer->onQueue('bookingPaymentDataCode', new BookingPaymentDataCodeMail($mailParams)); + //BookingPaymentDataCode + + } else { + + if (empty($bookingPaymentDataCheck)) { + throw new ApiErrorException('No request to view payment information yet'); + } + + $bookingPaymentDataCheckCollect = collect($bookingPaymentDataCheck); + + $bookingPaymentDataCheckRow = $bookingPaymentDataCheckCollect + ->where('code', md5($confirmCode)) + ->where('status', 2) + ->where('expiry_time', '>', Carbon::now()->timestamp) + ->first(); + + if (empty($bookingPaymentDataCheckRow)) { + throw new ApiErrorException('Code could not be verified, please check again'); + } + + $bookingPaymentDataEncrypted = collect($bookingDetail['booking_payment_data'])->groupBy('type')->toArray(); + + + $bookingPaymentData = []; + foreach ($bookingPaymentDataEncrypted as $type => $value) { + + if ($type == 'cc') { + $value = collect($value)->sortBy('id')->toArray(); + foreach ($value as $cardValue) { + $bookingPaymentData[$type][] = Crypt::decrypt($cardValue['data']); + } + $bookingPaymentData[$type] = implode(' ', $bookingPaymentData[$type]); + } else { + $cardValue = reset($value); + $bookingPaymentData[$type] = Crypt::decrypt($cardValue['data']); + } + + } + + $this->bookingPaymentDataCheckRepository->update($bookingPaymentDataCheckRow['id'], ['status' => 1, 'confirm_time' => Carbon::now()->timestamp]); + + $bookingPaymentData['bookingCode'] = $bookingDetail['booking_code']; + + $response = [ + 'status' => true, + 'data' => $bookingPaymentData + ]; + + } + + break; + + } + + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/BookingTicketService.php b/app/Core/Service/BookingTicketService.php new file mode 100644 index 0000000..67ba611 --- /dev/null +++ b/app/Core/Service/BookingTicketService.php @@ -0,0 +1,160 @@ +bookingTicketRepository = $bookingTicketRepository; + + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + + $insertData = [ + 'parent_id' => fillOnUndefined($params, 'parent_id'), + 'booking_id' => fillOnUndefined($params, 'booking_id'), + 'code' => fillOnUndefined($params, 'code'), + 'user_id' => fillOnUndefined($params, 'user_id'), + 'message' => fillOnUndefined($params, 'message'), + 'is_note' => fillOnUndefined($params, 'is_note'), + 'is_viewed' => fillOnUndefined($params, 'is_viewed'), + 'is_log' => fillOnUndefined($params, 'is_log'), + 'log' => fillOnUndefined($params, 'log'), + 'ip_address' => fillOnUndefined($params, 'ip_address'), + 'status' => fillOnUndefined($params, 'status', 1) + ]; + + + $userCreateResult = $this->bookingTicketRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->bookingTicketRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function updateWhere($criteria, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->bookingTicketRepository->updateWhereBulk($criteria, $param); + + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->bookingTicketRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/ChannelManager/Athena.php b/app/Core/Service/ChannelManager/Athena.php new file mode 100644 index 0000000..808f1c7 --- /dev/null +++ b/app/Core/Service/ChannelManager/Athena.php @@ -0,0 +1,304 @@ +restClient = $restClient; + $this->mailer = $mailer; + $this->reservationPushUrl = [ + 546 => 'http://46.175.134.18:6161/HotelEx/api/v1/hotel/ors/extranetwork/booking', + 541 => 'http://94.43.46.153:6161/HotelEx/api/v1/hotel/ors/extranetwork/booking', + 545 => 'http://92.51.70.35:6161/HotelEx/api/v1/hotel/ors/extranetwork/booking', + //538 => 'http://92.51.72.105:6161/HotelEx/api/v1/hotel/ors/extranetwork/booking', + 538 => 'http://146.255.233.146:6161/HotelEx/api/v1/hotel/ors/extranetwork/booking', + 548 => 'http://92.51.72.105:6161/HotelEx/api/v1/hotel/ors/extranetwork/booking', + 549 => 'http://92.51.72.105:6161/HotelEx/api/v1/hotel/ors/extranetwork/booking', + 550 => 'http://146.255.239.106:6161/HotelEx/api/v1/hotel/ors/extranetwork/booking' + ]; + + $this->typeMapping = [ + 'Booking' => 'Book', + 'Modify' => 'Modify', + 'Cancel' => 'Cancel' + ]; + + $this->channelManagerId = 5; + $this->channelManagerName = 'Athena'; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + + } + + public function requestPostReservationPushParam($propertyId, $bookingId, $type, $param = []) + { + + $type = $this->typeMapping[$type]; + $bookingDetail = $param['booking_detail']; + + $jsonResponse = []; + $jsonResponse['hotel_id'] = $propertyId; + + + $jsonResponse['reservation']['code'] = $bookingDetail['booking_code']; + $jsonResponse['reservation']['status'] = $type; + + $jsonResponse['reservation']['time'] = Carbon::createFromTimestamp($param['booking_detail']['created_at'])->toDateTimeString(); + + $jsonResponse['reservation']['checkin_date'] = $bookingDetail['checkin_date']; + $jsonResponse['reservation']['checkout_date'] = $bookingDetail['checkout_date']; + $jsonResponse['reservation']['currency'] = $bookingDetail['currency_code']; + $jsonResponse['reservation']['total'] = $bookingDetail['total']; + + + $jsonResponse['reservation']['contact'] = [ + 'name' => $bookingDetail['booking_contact']['name'], + 'surname' => $bookingDetail['booking_contact']['surname'], + 'email' => $bookingDetail['booking_contact']['email'], + 'phone' => $bookingDetail['booking_contact']['phoneFormatted'], + 'note' => $bookingDetail['booking_contact']['note'], + ]; + + + $jsonResponse['reservation']['rooms'] = []; + + $roomKey = 0; + foreach ($bookingDetail['booking_room'] as $roomKey => $roomDetail) { + + if($roomDetail['status'] != 1) { + continue; + } + + $occupancy = occupancyCodeParser($roomDetail['occupancy_code']); + + $diffInDays = Carbon::parse($roomDetail['checkin_date'])->diffInDays(Carbon::parse($roomDetail['checkout_date'])); + + //Additional Fee + $additionalFees = null; + if (!empty($roomDetail['rate_detail'])) { + $rateDetail = json_decode($roomDetail['rate_detail'], 1); + if(isset($rateDetail['taxes']) && $bookingDetail['channel_manager_id'] == 2) { + $additionalFees = $rateDetail['taxes']; + } + } + + $additionalFeeTotal = null; + if(!empty($additionalFees)) { + foreach ($additionalFees as $additionalFee) { + + if(!$additionalFee['is_inclusive']) { + $additionalFeeTotal+=$additionalFee['total_price']; + } + } + } + + $additionalFeePerDay = 0; + if(!empty($additionalFeeTotal)) { + $additionalFeePerDay = moneyDoubleFormatDecimal($additionalFeeTotal / $diffInDays); + } + //Additional Fee + + + $roomDailyAmount = []; + if (!empty($roomDetail['daily_amount'])) { + $roomDetailDailyAmount = json_decode($roomDetail['daily_amount'], 1); + if (!empty($roomDetailDailyAmount) && isset($roomDetailDailyAmount)) { + foreach ($roomDetailDailyAmount as $roomRateAmount) { + $calculatedAmount = $roomRateAmount['amount'] + $additionalFeePerDay; + $roomDailyAmount[] = [ + 'date' => $roomRateAmount['date'], + 'amount' => moneyDoubleFormatDecimal($calculatedAmount), + 'currency_code' => $roomRateAmount['currency_code'], + ]; + } + } + + $totalFromRoomDaily = collect($roomDailyAmount)->sum('amount'); + if($totalFromRoomDaily != $roomDetail['total']) { + $totalFromDifference = moneyDoubleFormatDecimal($roomDetail['total'] - $totalFromRoomDaily); + $roomDailyAmount[count($roomDailyAmount)-1]['amount']+= $totalFromDifference; + } + + } + + $baseRateDaily = moneyDoubleFormatDecimal($roomDetail['total'] / $diffInDays); + if (empty($roomDailyAmount)) { + $currentDate = $roomDetail['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $roomDailyAmount[] = [ + 'date' => $currentDate, + 'amount' => $baseRateDaily, + 'currency_code' => $roomDetail['currency_code'], + ]; + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + } + + $roomPax = []; + foreach ($roomDetail['room_pax'] as $pax) { + $roomPax[] = [ + 'type' => $pax['type'], + 'name' => $pax['name'], + 'surname' => $pax['surname'], + 'gender' => $pax['gender'], + 'citizen' => $pax['citizen'], + 'birth_date' => $pax['birth_date'], + ]; + } + + $jsonResponse['reservation']['rooms'][$roomKey] = [ + 'id' => $roomDetail['id'], + 'room_id' => $roomDetail['room_id'], + 'room_name' => $roomDetail['room_name'], + 'rate_id' => $roomDetail['room_rate_mapping_id'], + 'rate_name' => $roomDetail['room_rate_name'], + 'occupancy' => [ + 'adults' => $occupancy['ADT'], + 'children' => $occupancy['CHD'], + 'age' => $occupancy['AGE'], + ], + 'total' => $roomDetail['total'], + //'totalFromDaily' => collect($roomDailyAmount)->sum('amount'), + 'daily' => $roomDailyAmount, + 'pax' => $roomPax + + ]; + + + } + + $jsonResponse['reservation']['channel'] = [ + 'id' => $bookingDetail['booking_channel']['id'], + 'name' => $bookingDetail['booking_channel']['id'] == 1 ? 'Extranetwork' : $bookingDetail['booking_channel']['name'], + 'booking_code' => !empty($bookingDetail['channel_booking_code']) ? $bookingDetail['channel_booking_code'] : $bookingDetail['booking_code'] + ]; + + $jsonResponse['reservation']['payment'] = [ + 'code' => $bookingDetail['booking_payment_type']['code'], + 'name' => $bookingDetail['booking_payment_type']['name'], + ]; + + //CHN - For Offline Agency Booking Engine + if($bookingDetail['booking_channel']['channel_category_id'] == 2) { + if($bookingDetail['booking_payment']['payment_type_code'] == 'HTL') { + $jsonResponse['reservation']['payment'] = [ + 'code' => 'CHN', + 'name' => 'Channel Manager', + ]; + } + } + + if($bookingDetail['booking_payment']['payment_type_code'] == 'CRD' && $bookingDetail['booking_payment']['payment_source_code'] == 'GST') { + $jsonResponse['reservation']['payment'] = [ + 'code' => 'HTL', + 'name' => 'Pay at Hotel', + ]; + } + + + //Expedia Net Commission + if($jsonResponse['reservation']['total'] != $bookingDetail['booking_payment']['total']) { + + if($jsonResponse['reservation']['total'] == 0) { + $netRate = 0; + } else { + $netRate = $bookingDetail['booking_payment']['total'] / $jsonResponse['reservation']['total']; + } + + foreach ($jsonResponse['reservation']['rooms'] as $roomKey => $roomDetail) { + foreach ($roomDetail['daily'] as $roomDailyKey => $roomDaily) { + $jsonResponse['reservation']['rooms'][$roomKey]['daily'][$roomDailyKey]['amount'] = $roomDaily['amount'] * $netRate; + } + + $jsonResponse['reservation']['rooms'][$roomKey]['total'] = collect($jsonResponse['reservation']['rooms'][$roomKey]['daily'])->sum('amount'); + $jsonResponse['reservation']['rooms'][$roomKey]['total'] = moneyDoubleFormatDecimal($jsonResponse['reservation']['rooms'][$roomKey]['total']); + } + + $jsonResponse['reservation']['total'] = collect($jsonResponse['reservation']['rooms'])->sum('total'); + $jsonResponse['reservation']['total'] = moneyDoubleFormatDecimal($jsonResponse['reservation']['total']); + + } + + return json_encode($jsonResponse); + + } + + public function requestPostReservationPush($jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + + $jsonData = json_decode($jsonPayload,1); + + try { + + if(!isset($this->reservationPushUrl[$jsonData['hotel_id']])) { + throw new ApiErrorException('Athena live reservation no push url'); + } + + $this->restClient = new Client(['http_errors' => false, 'timeout' => 30]); + $res = $this->restClient->post($this->reservationPushUrl[$jsonData['hotel_id']], + [ + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'body' => $jsonPayload, + ] + ); + + $getResponseBody = $res->getBody(); + $getResponseJsonBase = $getResponseBody->getContents(); + + $getResponse = json_decode($getResponseJsonBase, 1); + + if (!$getResponse['success']) { + throw new ApiErrorException($getResponse['errorReason']); + } + + $response = ['status' => true, 'message' => '', 'response' => json_encode($getResponse)]; + + } catch (ApiErrorException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + +} diff --git a/app/Core/Service/ChannelManager/Channex.php b/app/Core/Service/ChannelManager/Channex.php new file mode 100644 index 0000000..8a67d32 --- /dev/null +++ b/app/Core/Service/ChannelManager/Channex.php @@ -0,0 +1,1277 @@ +restClient = $restClient; + $this->mailer = $mailer; + + //if (App::environment() == 'production') { + $this->url = 'https://app.channex.io/api/v1/'; + $this->userApiKey = 'uuyx1aEdP2whpT58mKbyxynLtgo0XQZZTq277Ww2Z8okT4FUhspFvIwrHD54+T6+'; + + $this->pciApiKey = '570751ea538845078a80be6ec0eee6d2'; + $this->pciSecureUrl = 'https://secure.channex.io/'; + /*} else { + $this->url = 'https://staging.channex.io/api/v1/'; + $this->userApiKey = 'uYOZRhv3tVK+AYNcFNWGwFfwz7+iMtqJaWvsT9p6rYA3RZENe9bAri7eIl95SbV+'; + + $this->pciApiKey = '076d9599ffe04159bd280659f13579e4'; + $this->pciSecureUrl = 'https://secure-staging.channex.io/'; + }*/ + + $this->pciUrl = 'https://pci.channex.io/api/v1/'; + + $this->channelManagerId = 2; + $this->channelManagerName = 'Channex'; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + + } + + public function request($type, $method, $jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + $this->restClient = new Client(['http_errors' => false]); + + if ($type == 'POST') { + + $parameter = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'user-api-key' => $this->userApiKey + ] + ]; + + if (!empty($jsonPayload)) { + $parameter['body'] = $jsonPayload; + } + + $res = $this->restClient->request('POST', $this->url . $method, $parameter); + + $getResponseBody = $res->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponse = json_decode($getResponse, 1); + + } else if ($type == 'GET') { + + $res = $this->restClient->request('GET', $this->url . $method, + [ + 'headers' => [ + //'Content-Type' => 'application/x-www-form-urlencoded', + 'user-api-key' => $this->userApiKey + ], + 'query' => $jsonPayload + ] + ); + + $getResponseBody = $res->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponse = json_decode($getResponse, 1); + + + } + + /*Log::debug('__REQ__'); + Log::debug($this->url); + Log::debug($type); + Log::debug($method); + Log::debug($jsonPayload); + Log::debug('__RES__'); + Log::debug($getResponse); + Log::debug(PHP_EOL);*/ + + if ($res->getStatusCode() != 200) { + $errors = singleElementArray($getResponse['errors']); + $firstError = reset($errors); + + Log::debug('__REQ__'); + Log::debug($this->url); + Log::debug($type); + Log::debug($method); + Log::debug($jsonPayload); + Log::debug('__RES__'); + Log::debug($getResponse); + Log::debug(PHP_EOL); + + throw new ApiErrorException($firstError['code'] . ': ' . $firstError['title']); + } + + $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, 'data', [])]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + public function requestPCI($type, $method, $jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + $this->restClient = new Client(['http_errors' => false]); + + $parameter = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'user-api-key' => $this->userApiKey + ] + ]; + + if (!empty($jsonPayload)) { + $parameter['body'] = $jsonPayload; + } + + $res = $this->restClient->request($type, $this->pciUrl . $method, $parameter); + + $getResponseBody = $res->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponse = json_decode($getResponse, 1); + + /*Log::debug('__REQ__'); + Log::debug($this->url); + Log::debug($type); + Log::debug($method); + Log::debug($jsonPayload); + Log::debug($parameter); + Log::debug('__RES__'); + Log::debug($getResponse); + Log::debug(PHP_EOL);*/ + + if (!in_array($res->getStatusCode(), [200, 201, 204])) { + $errors = []; + if (isset($getResponse['errors'])) { + $errors = singleElementArray($getResponse['errors']); + } elseif (isset($getResponse['error'])) { + $errors[] = [ + 'code' => null, + 'title' => $getResponse['error'] + ]; + } + + $firstError = reset($errors); + + Log::debug('__REQ__'); + Log::debug($type); + Log::debug($method); + Log::debug($jsonPayload); + Log::debug('__RES__'); + Log::debug($getResponse); + Log::debug(PHP_EOL); + + throw new ApiErrorException(fillOnUndefined($firstError, 'code') . ': ' . fillOnUndefined($firstError, 'title')); + } + + $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, 'data', [])]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + public function inventoryRoomRateUpdate($method, $jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + + + $request = $this->request('POST', $method, $jsonPayload); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $response = [ + 'status' => true, + 'data' => [ + 'confirmId' => reset($request['data'])['id'] + ] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + public function getSessionToken($param) + { + $response = ['status' => false, 'message' => '']; + + try { + + $request = $this->requestPCI('POST', 'session_tokens?api_key=' . $this->pciApiKey, json_encode($param)); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $response = [ + 'status' => true, + 'data' => $request['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + public function removeCreditCardToken($token) + { + $response = ['status' => false, 'message' => '']; + + try { + + $request = $this->requestPCI('DELETE', 'cards/' . $token . '?api_key=' . $this->pciApiKey, null); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $response = [ + 'status' => true, + 'data' => $request['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + public function getChannelDetails($channelId) + { + $response = ['status' => false, 'message' => '']; + + try { + + $request = $this->request('GET', 'channels/' . $channelId, []); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $response = [ + 'status' => true, + 'data' => $request['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + + /** Pattern Functions */ + + public function availabilityUpdateRequestMultiParam($propertyId, $params) + { + + $idList = []; + + $requestParam['values'] = []; + + ksort($params); + + $dataByRoomDate = []; + foreach ($params as $currentDate => $rooms) { + foreach ($rooms as $roomId => $value) { + $uniqueKey = $value['availability']; + $dataByRoomDate[$roomId][$uniqueKey][$currentDate] = $value; + } + } + + + $requestParam = []; + + + $valueKey = 0; + foreach ($dataByRoomDate as $roomId => $rooms) { + + foreach ($rooms as $uniqueKey => $uniqueValues) { + + $uniqueValueDate = array_keys($uniqueValues); + + $dateKey = 0; + $dateGroup = []; + for ($i = 0; $i < count($uniqueValueDate); $i++) { + $dateGroup[$dateKey][] = $uniqueValueDate[$i]; + if ($i + 1 < count($uniqueValueDate)) { + if (Carbon::parse($uniqueValueDate[$i])->diffInDays(Carbon::parse($uniqueValueDate[$i + 1])) > 1) { + $dateKey++; + } + } + } + + foreach ($uniqueValues as $uniqueValue) { + $idList = array_merge($idList, $uniqueValue['idList']); + } + + $currentValue = reset($uniqueValues); + + + foreach ($dateGroup as $dates) { + + if (count($dates) > 1) { + + $requestParam['values'][$valueKey] = [ + 'property_id' => $propertyId, + 'room_type_id' => $roomId, + 'date_from' => reset($dates), + 'date_to' => last($dates), + 'availability' => $currentValue['availability'] + ]; + + } else { + + $requestParam['values'][$valueKey] = [ + 'property_id' => $propertyId, + 'room_type_id' => $roomId, + 'date' => reset($dates), + 'availability' => $currentValue['availability'] + ]; + + } + + $valueKey++; + + } + + + } + } + + //dd($requestParam); + + return [ + 'idList' => $idList, + 'payload' => json_encode($requestParam) + ]; + } + + public function roomAvailabilityPush($propertyId, $bulkQueueData) + { + $response = ['status' => false, 'message' => '']; + + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true, + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + + $channelManagerPushedIds = []; + $channelManagerNoneMappingIds = []; + $channelManagerPushConfirmCode = []; + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']); + + + $roomAvailabilityQueueForUpdate = []; + $roomAvailabilityQueueForUpdateIdList = []; + $roomAvailabilityQueue = $bulkQueueData; + + + try { + + foreach ($roomAvailabilityQueue as $roomAvailability) { + + + //Dates less than today cannot be updated. + if (Carbon::parse($roomAvailability['date'])->lessThan(Carbon::now()->toDateString())) { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem', + 'logMessage' => '
' . print_r($roomAvailability, true) . '
' + ]; + + //$this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $channelManagerPushedIds[] = $roomAvailability['id']; + + continue; + + } + + $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping.room_id', $roomAvailability['property_room_id'])->first(); + + if (empty($channelManagerRoomRate)) { + //None Mapping! + //$channelManagerPushedIds[] = $roomAvailability['id']; + $channelManagerNoneMappingIds[] = $roomAvailability['id']; + + continue; + } + + + $roomAvailabilityQueueForUpdateIdList[$roomAvailability['date']][] = $roomAvailability['id']; + + + $roomAvailabilityQueueForUpdate[$roomAvailability['date']] + [$channelManagerRoomRate['channel_manager_room_id']] = [ + 'id' => $roomAvailability['id'], + 'idList' => $roomAvailabilityQueueForUpdateIdList[$roomAvailability['date']], + 'date' => $roomAvailability['date'], + 'availability' => $roomAvailability['stop_sell'] == 1 ? 0 : $roomAvailability['availability'] + ]; + + } + + $roomAvailabilityUpdateRequestMultiParam = $this->availabilityUpdateRequestMultiParam($channelManagerPropertyMapping['channel_manager_property_id'], $roomAvailabilityQueueForUpdate); + $request = $this->inventoryRoomRateUpdate('availability', $roomAvailabilityUpdateRequestMultiParam['payload']); + + if ($request['status']) { + + $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomAvailabilityUpdateRequestMultiParam['idList']); + $channelManagerPushConfirmCode[] = $request['data']['confirmId']; + + } else { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateAvailabilityUpdate Error', + 'logMessage' => '
' . print_r(array_merge($request, $roomAvailabilityUpdateRequestMultiParam), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + throw new ApiErrorException($request['message']); + } + + + } catch (ApiErrorException $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . implode(', ', $e->getMessageArr()); + Log::error($message); + //$response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + //$response['message'] = $e->getMessage(); + } + + + } else { + $channelManagerNoneMappingIds = collect($bulkQueueData)->pluck('id')->toArray(); + } + + + $channelManagerPushedIds = array_unique($channelManagerPushedIds); + asort($channelManagerPushedIds); + $channelManagerPushedIds = array_values($channelManagerPushedIds); + + $response = [ + 'status' => true, + 'data' => [ + 'confirmCode' => $channelManagerPushConfirmCode, + 'channelManagerPushedIds' => $channelManagerPushedIds, + 'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds + ] + ]; + + return $response; + } + + + public function roomRateUpdateRequestMultiParam($propertyId, $params) + { + + $idList = []; + + $requestParam['values'] = []; + + ksort($params); + + $dataByRoomRate = []; + foreach ($params as $currentDate => $rooms) { + foreach ($rooms as $roomId => $rates) { + foreach ($rates as $rateId => $value) { + $uniqueKey = $value['amount'] . '|' . $value['stop_sell'] . '|' . $value['min_stay']; + $dataByRoomRate[$rateId][$uniqueKey][$currentDate] = $value; + } + } + } + + //Log::debug(json_encode($dataByRoomRate)); + + $requestParam = []; + + $valueKey = 0; + foreach ($dataByRoomRate as $rateId => $rates) { + + foreach ($rates as $uniqueKey => $uniqueValues) { + + + $uniqueValueDate = array_keys($uniqueValues); + + $dateKey = 0; + $dateGroup = []; + for ($i = 0; $i < count($uniqueValueDate); $i++) { + $dateGroup[$dateKey][] = $uniqueValueDate[$i]; + if ($i + 1 < count($uniqueValueDate)) { + if (Carbon::parse($uniqueValueDate[$i])->diffInDays(Carbon::parse($uniqueValueDate[$i + 1])) > 1) { + $dateKey++; + } + } + } + + foreach ($uniqueValues as $uniqueValue) { + $idList = array_merge($idList, $uniqueValue['idList']); + } + + $currentValue = reset($uniqueValues); + + foreach ($dateGroup as $dates) { + + if (count($dates) > 1) { + + $requestParam['values'][$valueKey] = [ + 'property_id' => $propertyId, + 'rate_plan_id' => $rateId, + 'date_from' => reset($dates), + 'date_to' => last($dates), + 'rate' => moneyDoubleFormatDecimal($currentValue['amount'] * 100), + 'stop_sell' => fillOnUndefined($currentValue, 'stop_sell', 0), + 'min_stay_through' => fillOnUndefined($currentValue, 'min_stay', 1) != 0 ? $currentValue['min_stay'] : 1 + ]; + + /*if (isset($currentValue['min_stay']) && $currentValue['min_stay'] != 0) { + $requestParam['values'][$valueKey]['min_stay_through'] = fillOnUndefined($currentValue, 'min_stay', 1); + }*/ + + + } else { + + $requestParam['values'][$valueKey] = [ + 'property_id' => $propertyId, + 'rate_plan_id' => $rateId, + 'date' => reset($dates), + 'rate' => moneyDoubleFormatDecimal($currentValue['amount'] * 100), + 'stop_sell' => fillOnUndefined($currentValue, 'stop_sell', 0), + 'min_stay_through' => fillOnUndefined($currentValue, 'min_stay', 1) != 0 ? $currentValue['min_stay'] : 1 + ]; + + /*if (isset($currentValue['min_stay']) && $currentValue['min_stay'] != 0) { + $requestParam['values'][$valueKey]['min_stay_through'] = fillOnUndefined($currentValue, 'min_stay', 1); + }*/ + + + } + + if ($requestParam['values'][$valueKey]['rate'] == 0) { + unset($requestParam['values'][$valueKey]['rate']); + $requestParam['values'][$valueKey]['stop_sell'] = 1; + } + + $valueKey++; + + } + + } + } + + + return [ + 'idList' => $idList, + 'payload' => json_encode($requestParam) + ]; + + + } + + public function roomRatePricePush($propertyId, $bulkQueueData) + { + $response = ['status' => false, 'message' => '']; + + try { + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true, + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + $channelManagerPushedIds = []; + $channelManagerNoneMappingIds = []; + $channelManagerPushConfirmCode = null; + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']); + + + $roomRatePriceQueueForUpdate = []; + $roomRatePriceQueueForUpdateIdList = []; + $roomRatePriceQueue = $bulkQueueData; + + + foreach ($roomRatePriceQueue as $roomRatePrice) { + + + //Dates less than today cannot be updated. + if (Carbon::parse($roomRatePrice['date'])->lessThan(Carbon::now()->toDateString())) { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem', + 'logMessage' => '
' . print_r($roomRatePrice, true) . '
' + ]; + + //$this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $channelManagerPushedIds[] = $roomRatePrice['id']; + + continue; + + } + + $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping_id', $roomRatePrice['room_rate_mapping_id'])->first(); + + if (empty($channelManagerRoomRate)) { + //None Mapping! + //$channelManagerPushedIds[] = $roomAvailability['id']; + $channelManagerNoneMappingIds[] = $roomRatePrice['id']; + + //TODO: Burada oda eşleşmiyorsa süreç devam ediyor ama uyarı maili atılabilir. + + continue; + } + + + $roomRatePriceQueueForUpdateIdList + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']] + [$channelManagerRoomRate['channel_manager_room_rate_id']][] = $roomRatePrice['id']; + + + $roomRatePriceQueueForUpdate + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomRatePrice['date']] + [$channelManagerRoomRate['channel_manager_room_id']] + [$channelManagerRoomRate['channel_manager_room_rate_id']] = [ + 'id' => $roomRatePrice['id'], + 'idList' => $roomRatePriceQueueForUpdateIdList[$channelManagerPropertyMapping['channel_manager_property_id']][$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']][$channelManagerRoomRate['channel_manager_room_rate_id']], + 'date' => $roomRatePrice['date'], + 'amount' => $roomRatePrice['amount'], + 'currency' => $roomRatePrice['currency'], + 'stop_sell' => $roomRatePrice['stop_sell'], + 'min_stay' => $roomRatePrice['min_stay'], + ]; + + } + + + foreach ($roomRatePriceQueueForUpdate as $channelManagerPropertyId => $channelManagerProperty) { + + + if (empty($channelManagerProperty)) { + throw new ApiErrorException('$channelManagerProperty is Empty!'); + } + + + ksort($channelManagerProperty); + $roomRateUpdateRequestMultiParam = $this->roomRateUpdateRequestMultiParam($channelManagerPropertyId, $channelManagerProperty); + + $request = $this->inventoryRoomRateUpdate('restrictions', $roomRateUpdateRequestMultiParam['payload']); + + if ($request['status']) { + + $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomRateUpdateRequestMultiParam['idList']); + $channelManagerPushConfirmCode = $request['data']['confirmId']; + + } else { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateUpdate Error', + 'logMessage' => '
' . print_r($request, true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + throw new ApiErrorException($request['message']); + } + + } + + } + + $channelManagerPushedIds = array_unique($channelManagerPushedIds); + asort($channelManagerPushedIds); + $channelManagerPushedIds = array_values($channelManagerPushedIds); + + $response = [ + 'status' => true, + 'data' => [ + 'confirmCode' => $channelManagerPushConfirmCode, + 'channelManagerPushedIds' => $channelManagerPushedIds, + 'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds + ] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + + public function reservationPullFormattedData($propertyId, $channelManagerPropertyId, $param) + { + + $bookingParam = []; + + //TODO: DELETE + /*$propertyId = 545; + $paramX = $param; + $param = []; + $param['attributes'] = $paramX;*/ + + $response = ['status' => false, 'message' => '']; + + //$roomTypes + $roomTypesParam = [ + 'filter[property_id]' => $channelManagerPropertyId + ]; + + $roomTypes = $this->request('GET', 'room_types', $roomTypesParam); + $roomTypes = $roomTypes['status'] ? $roomTypes['data'] : []; + $roomTypesCollect = collect($roomTypes); + + //$ratePlans + $ratePlansParam = [ + 'filter[property_id]' => $channelManagerPropertyId + ]; + + $ratePlans = $this->request('GET', 'rate_plans', $ratePlansParam); + $ratePlans = $ratePlans['status'] ? $ratePlans['data'] : []; + $ratePlansCollect = collect($ratePlans); + + if (strpos($param['attributes']['unique_id'], 'EXP') !== false) { + $param['attributes']['ota_name'] = 'Expedia'; + } + + try { + + //Check Channel Manager Mapping + $channelManagerMappingCriteria = + [ + 'criteria' => + [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyChannelManager'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManagerMappingData = $this->channelManagerMappingService->select($channelManagerMappingCriteria); + $channelManagerMappingCollect = collect($channelManagerMappingData['data']); + $channelManagerMapping = $channelManagerMappingCollect->where('channel_manager_channel_id', $param['attributes']['ota_name'])->first(); + + //Eğer kanala ait eşleşen bir kanal yok ise devam et + if (empty($channelManagerMapping)) { + //TODO: hata dönecek + throw new ApiErrorException('Channel Mapping Not Found'); + } + //Check Channel Manager Mapping + + + //Check Channel Manager Property Mapping + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + //Eğer kanala ait bir otel yok ise devam et + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + ///TODO: hata dönecek + /// throw new ApiErrorException($request['message']); + } + + $channelManagerRoomRateCollect = collect($channelManagerPropertyMapping['data']['channel_manager_room_rate']); + + + $roomRequest = []; + foreach ($param['attributes']['rooms'] as $room) { + $roomRequest[] = [ + 'adults' => $room['occupancy']['adults'], + 'children' => $room['occupancy']['children'], + 'age' => !empty($room['occupancy']['ages']) ? $room['occupancy']['ages'] : [] + ]; + } + + $paymentTypeCode = 'HTL'; + $paymentSourceCode = null; + if (isset($param['attributes']['payment_type']) && $param['attributes']['payment_type'] == 'credit_card') { + $paymentTypeCode = 'CRD'; + if ($param['attributes']['payment_collect'] == 'property') { + $paymentSourceCode = 'GST'; + } + if ($param['attributes']['payment_collect'] == 'ota') { + $paymentSourceCode = 'OTA'; + } + } + + + //Booking + $bookingParam['booking'] = [ + 'id' => null, + 'booking_code' => null, + 'property_id' => $propertyId, + 'channel_manager_id' => $this->channelManagerId, + 'channel_id' => $channelManagerMapping['property_channel_id'], + 'channel_booking_code' => fillOnUndefined($param['attributes'], 'ota_reservation_code'), + 'search_key' => $param['attributes']['booking_id'], + 'checkin_date' => $param['attributes']['arrival_date'], + 'checkout_date' => $param['attributes']['departure_date'], + 'rooms' => json_encode($roomRequest), + 'payment_type_code' => $paymentTypeCode, + 'room_amount' => $param['attributes']['amount'], + 'addon_amount' => 0, + 'discount_amount' => 0, + 'total' => $param['attributes']['amount'], + 'currency_code' => $param['attributes']['currency'], + 'channel_token' => null, + 'booking_engine_token' => null, + 'reservation_time' => Carbon::parse($param['attributes']['inserted_at'],'UTC')->timestamp, + 'status' => $param['attributes']['status'] == 'cancelled' ? 0 : 1 + ]; + + $extraParamBookingContact = []; + if (isset($param['attributes']['customer']['meta']['is_genius'])) { + $extraParamBookingContact['is_genius'] = $param['attributes']['customer']['meta']['is_genius']; + } + + $phoneNumber['code'] = null; + $phoneNumber['number'] = null; + if (isset($param['attributes']['customer']['phone'])) { + + $phoneNumberExplode = explode(' ', $param['attributes']['customer']['phone']); + + if (!empty($phoneNumberExplode) && count($phoneNumberExplode) > 1) { + $phoneNumber['code'] = reset($phoneNumberExplode); + unset($phoneNumberExplode[0]); + $phoneNumber['number'] = implode(' ', $phoneNumberExplode); + } + + } + + + //Booking Contact + $bookingParam['contact'] = [ + 'name' => $param['attributes']['customer']['name'], + 'surname' => !empty($param['attributes']['customer']['surname']) ? $param['attributes']['customer']['surname'] : null, + 'phone_code' => fillOnUndefined($phoneNumber, 'code'), + 'phone_number' => fillOnUndefined($phoneNumber, 'number'), + 'email' => fillOnUndefined($param['attributes']['customer'], 'mail'), + 'country_code' => fillOnUndefined($param['attributes']['customer'], 'country'), + 'note' => !empty($param['attributes']['notes']) ? $param['attributes']['notes'] : null, + 'language_code' => fillOnUndefined($param['attributes']['customer'], 'language', 'en'), + 'extra_param' => !empty($extraParamBookingContact) ? json_encode($extraParamBookingContact) : null, + 'status' => 1 + ]; + + $bookingParam['contact']['language_code'] = mb_substr($bookingParam['contact']['language_code'], 0, 2); + $bookingParam['contact']['country_code'] = mb_strtolower($bookingParam['contact']['country_code']); + + //Booking Room + $param['attributes']['rooms'] = array_values($param['attributes']['rooms']); + foreach ($param['attributes']['rooms'] as $roomOrder => $room) { + + + //Check Room Rate Mapping + $channelManagerRoomRate = $channelManagerRoomRateCollect->where('channel_manager_room_id', $room['room_type_id'])->where('channel_manager_room_rate_id', $room['rate_plan_id'])->first(); + if (empty($channelManagerRoomRate)) { + $channelManagerRoomRate = $channelManagerRoomRateCollect->where('channel_manager_room_id', $room['room_type_id'])->first(); + } + + $roomTypesSelected = $roomTypesCollect->where('id', $room['room_type_id'])->first(); + $ratePlanSelected = $ratePlansCollect->where('id', $room['rate_plan_id'])->first(); + + + $dailyAmount = []; + foreach ($room['days'] as $roomDay => $roomAmount) { + $dailyAmount[] = [ + 'date' => Carbon::parse($roomDay)->toDateString(), + 'amount' => $roomAmount, + 'currency_code' => $param['attributes']['currency'] + ]; + } + + $occupancyCodeByRoom = str_repeat('A', $room['occupancy']['adults']); + if ($room['occupancy']['children'] > 0 && !empty($room['occupancy']['ages'])) { + foreach ($room['occupancy']['ages'] as $age) { + $occupancyCodeByRoom .= 'C' . $age; + } + } + + $extraParamRoom = []; + if (isset($room['guests']) && !empty($room['guests'])) { + $guests = []; + foreach ($room['guests'] as $guest) { + $guests[] = ucwords($guest['name']) . ' ' . ucwords($guest['surname']); + } + $extraParamRoom['guests'] = implode(', ', $guests); + } + if (isset($room['meta']['meal_plan'])) { + $extraParamRoom['meal_plan'] = $room['meta']['meal_plan']; + } + if (isset($room['meta']['policies']) && !empty(isset($room['meta']['policies']))) { + $extraParamRoom['policies'] = $room['meta']['policies']; + } + if (isset($room['meta']['cancel_penalties']) && !empty($room['meta']['cancel_penalties'])) { + $extraParamRoom['cancel_penalties'] = $room['meta']['cancel_penalties']; + } + if (isset($room['meta']['bed_preferences'])) { + $extraParamRoom['bed_preferences'] = $room['meta']['bed_preferences']; + } + if (isset($room['meta']['free_text'])) { + $extraParamRoom['free_text'] = $room['meta']['free_text']; + } + if (isset($room['meta']['payment_instruction'])) { + $extraParamRoom['payment_instruction'] = $room['meta']['payment_instruction']; + } + if (isset($room['meta']['promotion_code'])) { + $extraParamRoom['promotion_code'] = $room['meta']['promotion_code']; + } + if (isset($room['meta']['smoking_preferences'])) { + $extraParamRoom['smoking_preferences'] = $room['meta']['smoking_preferences']; + } + + $bookingParam['room'][] = [ + 'room_order_number' => ($roomOrder + 1), + 'occupancy_code' => $occupancyCodeByRoom, + 'checkin_date' => $room['checkin_date'], + 'checkout_date' => $room['checkout_date'], + 'rate_key' => null, + 'rate_key_code' => null, + 'availability_id' => 1, + 'availability_code' => 'ROM', + 'room_id' => $channelManagerRoomRate['property_room_rate_mapping']['room_id'], + 'room_name' => isset($roomTypesSelected['attributes']['title']) ? $roomTypesSelected['attributes']['title'] : null, + 'room_rate_mapping_id' => $channelManagerRoomRate['property_room_rate_mapping']['id'], + 'room_rate_name' => isset($ratePlanSelected['attributes']['title']) ? $ratePlanSelected['attributes']['title'] : null, + 'cancellation_policy' => null, + 'payment_type_code' => $paymentTypeCode, + 'daily_amount' => !empty($dailyAmount) ? json_encode($dailyAmount) : null, + 'extra_param' => !empty($extraParamRoom) ? json_encode($extraParamRoom) : null, + 'rate_detail' => json_encode($room), + 'total' => $room['amount'], + 'currency_code' => $param['attributes']['currency'], + 'status' => $room['is_cancelled'] ? 0 : 1 + ]; + + } + + //Expedia Net Commission + $paymentAmount = $param['attributes']['amount']; + if($paymentTypeCode == 'CRD' && $paymentSourceCode == 'OTA' && in_array($param['attributes']['ota_name'],['Expedia'])) { + if(isset($param['attributes']['guarantee']['meta']) && isset($param['attributes']['guarantee']['meta']['expedia_net_commission'])) { + $paymentAmountNet = moneyDoubleFormatDecimal($param['attributes']['guarantee']['meta']['virtual_card_current_balance'] / 100); + if($paymentAmount != $paymentAmountNet) { + $paymentAmount = $paymentAmountNet; + } + } + } + + //Booking Payment Data + $bookingParam['payment'] = [ + 'payment_code' => null, + 'payment_type_code' => $paymentTypeCode, + 'payment_source_code' => $paymentSourceCode, + 'extra_param' => json_encode($param), + 'total' => $paymentAmount, + 'currency_code' => $param['attributes']['currency'], + 'status' => 2 + ]; + + //Booking Channel Payment Data + $bookingParam['payment_channel'] = null; + if (isset($param['attributes']['guarantee']) && !empty($param['attributes']['guarantee']) && in_array($param['attributes']['status'], ['new', 'modified'])) { + + $urlParam = [ + 'api_key' => $this->pciApiKey, + 'method' => 'get', + 'url' => $this->pciSecureUrl . 'api/v1/bookings/' . $param['attributes']['booking_id'], + 'profile' => 'channex_entity' + ]; + + $creditCardCapture = $this->requestPCI('POST', 'capture?' . http_build_query($urlParam), null); + + if ($creditCardCapture['status'] && isset($creditCardCapture['data']['attributes']['guarantee']['token'])) { + + $bookingParam['payment_channel'] = [ + 'type' => 'ch', + 'data' => $creditCardCapture['data']['attributes']['guarantee']['token'], + 'status' => 2 + ]; + + } + + + } + //Booking Channel Payment Data + + + //Channel Manager ETC Params + $bookingParam['channel_manager'] = [ + 'property_id' => $channelManagerPropertyId, + 'booking_id' => $param['attributes']['booking_id'], + 'sub_channel' => $param['attributes']['ota_name'], + 'sub_channel_booking_id' => $param['attributes']['ota_reservation_code'], + 'unique_id' => $param['attributes']['unique_id'], + 'revision_id' => $param['attributes']['id'], + ]; + + $statusType = null; + if ($param['attributes']['status'] == 'new') { + $statusType = 'createBooking'; + } elseif ($param['attributes']['status'] == 'cancelled') { + $statusType = 'cancelBooking'; + } elseif ($param['attributes']['status'] == 'modified') { + $statusType = 'modifiedBooking'; + } + + //$statusType = 'modifiedBooking'; + + $response['status'] = true; + $response['type'] = $statusType; + $response['data'] = $bookingParam; + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + + public function reservationListParam($propertyId) + { + + $requestParam = []; + + $requestParam = [ + 'pagination[page]' => 1, + 'pagination[limit]' => 10, + 'order[inserted_at]' => 'ASC', + 'filter[property_id]' => $propertyId + ]; + + + return $requestParam; + } + + public function reservationList($param) + { + $response = ['status' => false, 'message' => '']; + + try { + + $request = $this->request('GET', 'booking_revisions/feed', $param); + + //TODO: Delete + //$request = $this->request('GET', 'booking_revisions/4cb223d2-0259-41f6-b717-fdd4705c57e6', []); + + //a99e36ec-c83a-4b95-b124-2a0c2ba95c78 Expedia + //4cb223d2-0259-41f6-b717-fdd4705c57e6 Booking + //0b7912ef-aeaa-479c-9906-d71b7bc65b64 Booking + //c3426e46-6d6c-440d-a529-fd860b7b3283 Expedia + //d50355b7-c538-49f6-a0e4-898bf92812e0 + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $response = [ + 'status' => true, + 'data' => $request['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + public function reservationConfirmParam($propertyId, $channelManagerBookingId, $bookingCode, $param = []) + { + + $requestParam = []; + + $requestParam = [ + 'revision_id' => $param['channel_manager']['revision_id'] + ]; + + + return $requestParam; + } + + public function reservationConfirm($param) + { + $response = ['status' => false, 'message' => '']; + + try { + + $request = $this->request('POST', 'booking_revisions/' . $param['revision_id'] . '/ack', []); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $response = [ + 'status' => true, + 'data' => $request['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + +} diff --git a/app/Core/Service/ChannelManager/ElektraWeb.php b/app/Core/Service/ChannelManager/ElektraWeb.php new file mode 100644 index 0000000..d6105c5 --- /dev/null +++ b/app/Core/Service/ChannelManager/ElektraWeb.php @@ -0,0 +1,226 @@ +restClient = $restClient; + $this->mailer = $mailer; + $this->reservationPushUrl = 'https://channelmanager.elektraweb.com/extranetwork?action=push'; + + + $this->typeMapping = [ + 'Booking' => 'Book', + 'Modify' => 'Modify', + 'Cancel' => 'Cancel' + ]; + + $this->channelManagerId = 4; + $this->channelManagerName = 'ElektraWeb'; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + + } + + public function requestPost($method, $xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + $this->restClient = new Client(['http_errors' => false]); + $res = $this->restClient->post($this->url . $method, + [ + 'headers' => [ + 'Content-Type' => 'text/xml; charset=UTF8', + 'Accept' => 'text/xml' + ], + 'body' => $xmlPayload, + ] + ); + + $getResponseBody = $res->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponseXml = new \SimpleXMLElement($getResponse, LIBXML_NOCDATA); + $getResponse = json_decode(json_encode($getResponseXml), 1); + + $response = ["status" => true, 'message' => '', "data" => $getResponse]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function requestPostReservationPushParam($propertyId, $bookingId, $type, $param = []) + { + + $type = $this->typeMapping[$type]; + $bookingDetail = $param['booking_detail']; + + + $jsonResponse = []; + $jsonResponse['hotel_id'] = $propertyId; + + + $jsonResponse['reservation']['code'] = $bookingDetail['booking_code']; + $jsonResponse['reservation']['status'] = $type; + + $jsonResponse['reservation']['time'] = Carbon::createFromTimestamp($param['booking_detail']['created_at'])->toDateTimeString(); + + $jsonResponse['reservation']['checkin_date'] = $bookingDetail['checkin_date']; + $jsonResponse['reservation']['checkout_date'] = $bookingDetail['checkout_date']; + $jsonResponse['reservation']['currency'] = $bookingDetail['currency_code']; + $jsonResponse['reservation']['total'] = ($bookingDetail['total'] - $bookingDetail['addon_amount']); + + $jsonResponse['reservation']['contact'] = [ + 'name' => $bookingDetail['booking_contact']['name'], + 'surname' => $bookingDetail['booking_contact']['surname'], + 'email' => $bookingDetail['booking_contact']['email'], + 'phone' => $bookingDetail['booking_contact']['phoneFormatted'], + 'note' => $bookingDetail['booking_contact']['note'], + ]; + + + $jsonResponse['reservation']['rooms'] = []; + + + $roomKey = 0; + foreach ($bookingDetail['booking_room'] as $roomKey => $roomDetail) { + + $occupancy = occupancyCodeParser($roomDetail['occupancy_code']); + + $diffInDays = Carbon::parse($roomDetail['checkin_date'])->diffInDays(Carbon::parse($roomDetail['checkout_date'])); + $baseRateDaily = moneyDoubleFormatDecimal($roomDetail['total'] / $diffInDays); + + $dailyPrices = []; + $currentDate = $roomDetail['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $dailyPrices[] = [ + 'date' => $currentDate, + 'amount' => $baseRateDaily + ]; + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + + $roomPax = []; + foreach ($roomDetail['room_pax'] as $pax) { + $roomPax[] = [ + 'type' => $pax['type'], + 'name' => $pax['name'], + 'surname' => $pax['surname'], + 'gender' => $pax['gender'], + 'citizen' => $pax['citizen'], + 'birth_date' => $pax['birth_date'], + ]; + } + + $jsonResponse['reservation']['rooms'][$roomKey] = [ + 'room_id' => $roomDetail['room_id'], + 'room_name' => $roomDetail['room_name'], + 'rate_id' => $roomDetail['room_rate_mapping_id'], + 'rate_name' => $roomDetail['room_rate_name'], + 'occupancy' => [ + 'adults' => $occupancy['ADT'], + 'children' => $occupancy['CHD'], + 'age' => $occupancy['AGE'], + ], + 'daily' => $dailyPrices, + 'pax' => $roomPax + + ]; + + + } + + $jsonResponse['reservation']['channel'] = [ + 'id' => $bookingDetail['booking_channel']['id'], + 'name' => $bookingDetail['booking_channel']['id'] == 1 ? 'Extranetwork' : $bookingDetail['booking_channel']['name'], + ]; + + $jsonResponse['reservation']['payment'] = [ + 'code' => $bookingDetail['booking_payment_type']['code'], + 'name' => $bookingDetail['booking_payment_type']['name'], + ]; + + return json_encode($jsonResponse); + + } + + public function requestPostReservationPush($jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + + try { + $this->restClient = new Client(['http_errors' => false]); + $res = $this->restClient->post($this->reservationPushUrl, + [ + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'body' => $jsonPayload, + ] + ); + + $getResponseBody = $res->getBody(); + $getResponseJsonBase = $getResponseBody->getContents(); + + $getResponse = json_decode($getResponseJsonBase, 1); + + if (!$getResponse['Success']) { + throw new ApiErrorException($getResponse['Message']); + } + + $response = ['status' => true, 'message' => '', 'response' => json_encode($getResponse)]; + + } catch (ApiErrorException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + +} diff --git a/app/Core/Service/ChannelManager/Fina.php b/app/Core/Service/ChannelManager/Fina.php new file mode 100644 index 0000000..5e48714 --- /dev/null +++ b/app/Core/Service/ChannelManager/Fina.php @@ -0,0 +1,255 @@ +restClient = $restClient; + $this->mailer = $mailer; + $this->reservationPushUrl = [ + 790 => 'http://92.51.115.18/api/hotel/saveReservation' + ]; + + $this->typeMapping = [ + 'Booking' => 'Book', + 'Modify' => 'Modify', + 'Cancel' => 'Cancel' + ]; + + $this->channelManagerId = 8; + $this->channelManagerName = 'Fina'; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + + } + + public function requestPostReservationPushParam($propertyId, $bookingId, $type, $param = []) + { + + $type = $this->typeMapping[$type]; + $bookingDetail = $param['booking_detail']; + + $jsonResponse = []; + $jsonResponse['hotel_id'] = $propertyId; + + + $jsonResponse['reservation']['code'] = $bookingDetail['booking_code']; + $jsonResponse['reservation']['status'] = $type; + + $jsonResponse['reservation']['time'] = Carbon::createFromTimestamp($param['booking_detail']['created_at'])->toDateTimeString(); + + $jsonResponse['reservation']['checkin_date'] = $bookingDetail['checkin_date']; + $jsonResponse['reservation']['checkout_date'] = $bookingDetail['checkout_date']; + $jsonResponse['reservation']['currency'] = $bookingDetail['currency_code']; + $jsonResponse['reservation']['total'] = $bookingDetail['total']; + + + $jsonResponse['reservation']['contact'] = [ + 'name' => $bookingDetail['booking_contact']['name'], + 'surname' => $bookingDetail['booking_contact']['surname'], + 'email' => $bookingDetail['booking_contact']['email'], + 'phone' => $bookingDetail['booking_contact']['phoneFormatted'], + 'note' => $bookingDetail['booking_contact']['note'], + ]; + + + $jsonResponse['reservation']['rooms'] = []; + + $roomKey = 0; + foreach ($bookingDetail['booking_room'] as $roomKey => $roomDetail) { + + if ($roomDetail['status'] != 1) { + continue; + } + + $occupancy = occupancyCodeParser($roomDetail['occupancy_code']); + + $diffInDays = Carbon::parse($roomDetail['checkin_date'])->diffInDays(Carbon::parse($roomDetail['checkout_date'])); + + //Additional Fee + $additionalFees = null; + if (!empty($roomDetail['rate_detail'])) { + $rateDetail = json_decode($roomDetail['rate_detail'], 1); + if (isset($rateDetail['taxes']) && $bookingDetail['channel_manager_id'] == 2) { + $additionalFees = $rateDetail['taxes']; + } + } + + $additionalFeeTotal = null; + if (!empty($additionalFees)) { + foreach ($additionalFees as $additionalFee) { + + if (!$additionalFee['is_inclusive']) { + $additionalFeeTotal += $additionalFee['total_price']; + } + } + } + + $additionalFeePerDay = 0; + if (!empty($additionalFeeTotal)) { + $additionalFeePerDay = moneyDoubleFormatDecimal($additionalFeeTotal / $diffInDays); + } + //Additional Fee + + + $roomDailyAmount = []; + if (!empty($roomDetail['daily_amount'])) { + $roomDetailDailyAmount = json_decode($roomDetail['daily_amount'], 1); + if (!empty($roomDetailDailyAmount) && isset($roomDetailDailyAmount)) { + foreach ($roomDetailDailyAmount as $roomRateAmount) { + $calculatedAmount = $roomRateAmount['amount'] + $additionalFeePerDay; + $roomDailyAmount[] = [ + 'date' => $roomRateAmount['date'], + 'amount' => moneyDoubleFormatDecimal($calculatedAmount), + 'currency_code' => $roomRateAmount['currency_code'], + ]; + } + } + + $totalFromRoomDaily = collect($roomDailyAmount)->sum('amount'); + if ($totalFromRoomDaily != $roomDetail['total']) { + $totalFromDifference = moneyDoubleFormatDecimal($roomDetail['total'] - $totalFromRoomDaily); + $roomDailyAmount[count($roomDailyAmount) - 1]['amount'] += $totalFromDifference; + } + + } + + $baseRateDaily = moneyDoubleFormatDecimal($roomDetail['total'] / $diffInDays); + if (empty($roomDailyAmount)) { + $currentDate = $roomDetail['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $roomDailyAmount[] = [ + 'date' => $currentDate, + 'amount' => $baseRateDaily, + 'currency_code' => $roomDetail['currency_code'], + ]; + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + } + + $roomPax = []; + foreach ($roomDetail['room_pax'] as $pax) { + $roomPax[] = [ + 'type' => $pax['type'], + 'name' => $pax['name'], + 'surname' => $pax['surname'], + 'gender' => $pax['gender'], + 'citizen' => $pax['citizen'], + 'birth_date' => $pax['birth_date'], + ]; + } + + $jsonResponse['reservation']['rooms'][$roomKey] = [ + 'id' => $roomDetail['id'], + 'room_id' => $roomDetail['room_id'], + 'room_name' => $roomDetail['room_name'], + 'rate_id' => $roomDetail['room_rate_mapping_id'], + 'rate_name' => $roomDetail['room_rate_name'], + 'occupancy' => [ + 'adults' => $occupancy['ADT'], + 'children' => $occupancy['CHD'], + 'age' => $occupancy['AGE'], + ], + 'total' => $roomDetail['total'], + //'totalFromDaily' => collect($roomDailyAmount)->sum('amount'), + 'daily' => $roomDailyAmount, + 'pax' => $roomPax + + ]; + + + } + + $jsonResponse['reservation']['channel'] = [ + 'id' => $bookingDetail['booking_channel']['id'], + 'name' => $bookingDetail['booking_channel']['id'] == 1 ? 'Extranetwork' : $bookingDetail['booking_channel']['name'], + 'booking_code' => !empty($bookingDetail['channel_booking_code']) ? $bookingDetail['channel_booking_code'] : $bookingDetail['booking_code'] + ]; + + $jsonResponse['reservation']['payment'] = [ + 'code' => $bookingDetail['booking_payment_type']['code'], + 'source' => $bookingDetail['booking_payment']['payment_source_code'], + 'name' => $bookingDetail['booking_payment_type']['name'], + ]; + + return json_encode($jsonResponse); + + } + + public function requestPostReservationPush($jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + + $jsonData = json_decode($jsonPayload, 1); + + $getResponse = []; + + try { + + /*if(!isset($this->reservationPushUrl[$jsonData['hotel_id']])) { + throw new ApiErrorException('Fina live reservation no push url'); + } + + $this->restClient = new Client(['http_errors' => false, 'timeout' => 30]); + $res = $this->restClient->post($this->reservationPushUrl[$jsonData['hotel_id']], + [ + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'body' => $jsonPayload, + ] + ); + + $getResponseBody = $res->getBody(); + $getResponseJsonBase = $getResponseBody->getContents(); + + $getResponse = json_decode($getResponseJsonBase, 1); + + if (!$getResponse['success']) { + throw new ApiErrorException($getResponse['errorReason']); + }*/ + + $response = ['status' => true, 'message' => '', 'response' => json_encode($getResponse)]; + + } catch (ApiErrorException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + +} diff --git a/app/Core/Service/ChannelManager/HotelRunner.php b/app/Core/Service/ChannelManager/HotelRunner.php new file mode 100644 index 0000000..e3c0835 --- /dev/null +++ b/app/Core/Service/ChannelManager/HotelRunner.php @@ -0,0 +1,284 @@ +restClient = $restClient; + $this->mailer = $mailer; + //$this->reservationPushUrl = 'https://cm-staging.hotelrunner.com/api/push/reservation/28fb9e7b18903126e9d71a10ced00db0edec8710'; + $this->reservationPushUrl = 'https://cm.hotelrunner.com/api/push/reservation/da120fb30eae007e41034f5ab7824608ab8250de'; + + + $this->typeMapping = [ + 'Booking' => 'Confirm', + 'Modify' => 'Modify', + 'Cancel' => 'Cancel' + ]; + + $this->roomStatusTypeMapping = [ + 1 => 'Confirm', + 0 => 'Cancel' + ]; + + $this->channelManagerId = 3; + $this->channelManagerName = 'HotelRunner'; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + + } + + public function requestPost($method, $xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + $this->restClient = new Client(['http_errors' => false]); + $res = $this->restClient->post($this->url . $method, + [ + 'headers' => [ + 'Content-Type' => 'text/xml; charset=UTF8', + 'Accept' => 'text/xml' + ], + 'body' => $xmlPayload, + ] + ); + + $getResponseBody = $res->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponseXml = new \SimpleXMLElement($getResponse, LIBXML_NOCDATA); + $getResponse = json_decode(json_encode($getResponseXml), 1); + + $response = ["status" => true, 'message' => '', "data" => $getResponse]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function requestPostReservationPushParam($propertyId, $bookingId, $type, $param = []) + { + + $type = $this->typeMapping[$type]; + $bookingDetail = $param['booking_detail']; + + $xmlResponse = new \SimpleXMLElement(''); + + $xmlResponse->addChild('status', $type); + $xmlResponse->addChild('code', $bookingDetail['booking_code']); + $xmlResponse->addChild('time_zone', 'UTC'); + + if (in_array($type, ['Modify', 'Cancel'])) { + $xmlResponse->addChild('date', Carbon::createFromTimestamp($param['booking_detail']['updated_at'], 'UTC')->toDateTimeString()); + } else { + $xmlResponse->addChild('date', Carbon::createFromTimestamp($param['booking_detail']['created_at'], 'UTC')->toDateTimeString()); + } + + //$xmlResponse->addChild('name', $bookingDetail['booking_contact']['nameSurname']); + $xmlResponse->addChild('name', ''); + $xmlResponse->addChild('mail', $bookingDetail['booking_contact']['email']); + $xmlResponse->addChild('address'); + $xmlResponse->addChild('city'); + + $firstRoom = reset($bookingDetail['booking_room']); + $firstPax = reset($firstRoom['room_pax']); + + $xmlResponse->addChild('country', $firstPax['pax_country']['name']); + $xmlResponse->addChild('telephone', $bookingDetail['booking_contact']['phoneFormatted']); + + $xmlResponse->addChild('property_id', $bookingDetail['property_id']); + + $totalAdultCount = 0; + $totalChildrenCount = 0; + $roomSearchOccupancy = json_decode($bookingDetail['rooms'], 1); + foreach ($roomSearchOccupancy as $roomOccupancy) { + $totalAdultCount += $roomOccupancy['adults']; + $totalChildrenCount += $roomOccupancy['children']; + } + + $xmlResponse->addChild('adult_count', $totalAdultCount); + $xmlResponse->addChild('child_count', $totalChildrenCount); + + + $xmlResponse->addChild('total_cost', $bookingDetail['total']); + $xmlResponse->addChild('currency', $bookingDetail['currency_code']); + + if (!empty($bookingDetail['booking_contact']['note'])) { + $xmlResponse->addChild('remark', ''); + } else { + $xmlResponse->addChild('remark', $bookingDetail['booking_contact']['note']); + } + + + $xmlResponse->addChild('arrival_date', $bookingDetail['checkin_date']); + $xmlResponse->addChild('departure_date', $bookingDetail['checkout_date']); + + $xmlResponse->addChild('card');//Credit Card + + + $rooms = $xmlResponse->addChild('rooms'); + + foreach ($bookingDetail['booking_room'] as $roomKey => $roomDetail) { + + $room = $rooms->addChild('room'); + + $roomStatusType = isset($this->roomStatusTypeMapping[$roomDetail['status']]) ? $this->roomStatusTypeMapping[$roomDetail['status']] : 1; + + if (in_array($type, ['Modify']) && $roomDetail['status'] == 1) { + $roomStatusType = $type; + } + + if (in_array($type, ['Cancel'])) { + $roomStatusType = $type; + } + + $room->addChild('room_id', $roomDetail['room_id']); + $room->addChild('rate_id', $roomDetail['room_rate_mapping_id']); + $room->addChild('code', $roomDetail['room_id'] . ':' . $roomDetail['room_rate_mapping_id']); + + + $isNonRefundable = false; + $cancellationPolicy = json_decode($roomDetail['cancellation_policy'], 1); + if (!empty($cancellationPolicy)) { + if (isset($cancellationPolicy['isNonRefundable']) && $cancellationPolicy['isNonRefundable'] == 1) { + $isNonRefundable = true; + } + } + + if ($isNonRefundable) { + $roomName = $roomDetail['room_name'] . ' - ' . $roomDetail['room_rate_name'].' - NonRefundable'; + } else { + $roomName = $roomDetail['room_name'] . ' - ' . $roomDetail['room_rate_name']; + } + + $room->addChild('name', ''); + + $roomPaxCollect = collect($roomDetail['room_pax']); + $room->addChild('adult_count', $roomPaxCollect->where('type', 'ADT')->count()); + $room->addChild('child_count', $roomPaxCollect->where('type', 'CHD')->count()); + + $childAges = isset($roomSearchOccupancy[$roomKey]) && $roomSearchOccupancy[$roomKey]['children'] > 0 ? '[' . implode(',', $roomSearchOccupancy[$roomKey]['age']) . ']' : null; + + $room->addChild('child_ages', $childAges); + + + $room->addChild('arrival_date', $roomDetail['checkin_date']); + $room->addChild('departure_date', $roomDetail['checkout_date']); + + $room->addChild('total_cost', $roomDetail['total']); + $room->addChild('status', $roomStatusType); + + + $room->addChild('non_refundable', $isNonRefundable); + + + $dailyPrices = $room->addChild('daily_prices'); + + $diffInDays = Carbon::parse($roomDetail['checkin_date'])->diffInDays(Carbon::parse($roomDetail['checkout_date'])); + $baseRateDaily = moneyDoubleFormatDecimal($roomDetail['total'] / $diffInDays); + + $currentDate = $roomDetail['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $dailyPrice = $dailyPrices->addChild('daily_price'); + $dailyPrice->addChild('date', $currentDate); + $dailyPrice->addChild('price', $baseRateDaily); + + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + + } + + //Log::debug($xmlResponse->asXML()); + + //dd(html_entity_decode($xmlResponse->asXML())); + + return html_entity_decode($xmlResponse->asXML()); + + } + + public function requestPostReservationPush($xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + $propertyId = null; + $xmlPayloadData = simplexml_load_string($xmlPayload); + $xmlPayloadData = json_decode(json_encode($xmlPayloadData), 1); + + try { + $this->restClient = new Client(['http_errors' => false]); + $res = $this->restClient->post($this->reservationPushUrl . '/' . $xmlPayloadData['property_id'], + [ + 'headers' => [ + 'Content-Type' => 'text/xml; charset=UTF8', + //'Authorization' => 'Basic dUV4dHJhbmV0V29yazpFeHRybnRXcmsyMSEqMjI=', + 'Accept' => 'text/xml' + ], + 'body' => $xmlPayload, + ] + ); + + $getResponseBody = $res->getBody(); + $getResponseXmlBase = $getResponseBody->getContents(); + + $getResponseXml = new \SimpleXMLElement($getResponseXmlBase, LIBXML_NOCDATA); + $getResponse = json_decode(json_encode($getResponseXml), 1); + + //Log::debug($xmlPayload); + //Log::debug($getResponse); + + if ($getResponse['status'] != 0) { + throw new ApiErrorException($getResponse['message']); + } + + $response = ['status' => true, 'message' => '', 'response' => $getResponseXmlBase]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + +} diff --git a/app/Core/Service/ChannelManager/HyperGuest.php b/app/Core/Service/ChannelManager/HyperGuest.php new file mode 100644 index 0000000..2ef47d9 --- /dev/null +++ b/app/Core/Service/ChannelManager/HyperGuest.php @@ -0,0 +1,416 @@ +restClient = $restClient; + $this->mailer = $mailer; + $this->reservationPushUrl = 'https://book-api.hyperguest.com'; + $this->bearerToken = '63d51938dc3749788ac3e88ea6d58950'; + + $this->channelManagerId = 10; + $this->channelManagerName = 'HyperGuest'; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + + $this->paymentTypeMapping = [ + 'CRD' => 'bank_transfer', + 'HTL' => 'external' + ]; + + } + + public function requestPost($method, $xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + + $this->restClient = new Client(['http_errors' => false]); + + $parameter = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->bearerToken + ] + ]; + + if (!empty($jsonPayload)) { + $parameter['body'] = $jsonPayload; + } + + $request = $this->restClient->request('POST', $this->url . $method, $parameter); + + $getResponseBody = $request->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponse = json_decode($getResponse, 1); + + if ($request->getStatusCode() != 200) { + $errors = singleElementArray($getResponse['errors']); + $firstError = reset($errors); + throw new ApiErrorException($firstError['code'] . ': ' . $firstError['title']); + } + + $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, 'data', [])]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function requestPostReservationPushParam($propertyId, $bookingId, $type, $param = []) + { + + $jsonResponse = null; + + try { + + $bookingDetail = $param['booking_detail']; + + $requestParam = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + + $jsonResponse = []; + $jsonResponse['dates']['from'] = $bookingDetail['checkin_date']; + $jsonResponse['dates']['to'] = $bookingDetail['checkout_date']; + + $jsonResponse['propertyId'] = $channelManagerPropertyMapping['channel_manager_property_id']; + + + //$jsonResponse['reservation']['code'] = $bookingDetail['booking_code']; + //$jsonResponse['reservation']['status'] = $type; + + //$jsonResponse['reservation']['time'] = Carbon::createFromTimestamp($param['booking_detail']['created_at'])->toDateTimeString(); + + $jsonResponse['leadGuest']['birthDate'] = '1980-01-01'; + + + $jsonResponse['leadGuest']['contact']['address'] = 'N/A'; + $jsonResponse['leadGuest']['contact']['city'] = 'N/A'; + $jsonResponse['leadGuest']['contact']['country'] = !empty($bookingDetail['booking_contact']['country_code']) ? $bookingDetail['booking_contact']['country_code'] : 'TR'; + $jsonResponse['leadGuest']['contact']['email'] = $bookingDetail['booking_contact']['email']; + $jsonResponse['leadGuest']['contact']['phone'] = $bookingDetail['booking_contact']['phoneFormatted']; + $jsonResponse['leadGuest']['contact']['state'] = 'N/A'; + $jsonResponse['leadGuest']['contact']['zip'] = 'N/A'; + + $jsonResponse['leadGuest']['name']['first'] = $bookingDetail['booking_contact']['name']; + $jsonResponse['leadGuest']['name']['last'] = $bookingDetail['booking_contact']['surname']; + + $jsonResponse['leadGuest']['title'] = 'Mr'; + + $jsonResponse['reference']['agency'] = $bookingDetail['booking_code'];//'Extranetwork'; + + + $jsonResponse['paymentDetails']['type'] = isset($this->paymentTypeMapping[$bookingDetail['payment_type_code']]) ? $this->paymentTypeMapping[$bookingDetail['payment_type_code']] : $this->paymentTypeMapping['HTL']; + //$jsonResponse['paymentDetails']['details']['number'] = null; + //$jsonResponse['paymentDetails']['details']['cvv'] = null; + //$jsonResponse['paymentDetails']['details']['expiry']['month'] = null; + //$jsonResponse['paymentDetails']['details']['name']['first'] = null; + //$jsonResponse['paymentDetails']['details']['name']['last'] = null; + + + $jsonResponse['rooms'] = []; + + + $roomKey = 0; + foreach ($bookingDetail['booking_room'] as $roomKey => $roomDetail) { + + /*$occupancy = occupancyCodeParser($roomDetail['occupancy_code']); + $diffInDays = Carbon::parse($roomDetail['checkin_date'])->diffInDays(Carbon::parse($roomDetail['checkout_date'])); + */ + + $roomPax = []; + foreach ($roomDetail['room_pax'] as $pax) { + $roomPax[] = [ + 'birthDate' => $pax['birth_date'], + /*'contact' => [ + 'address' => null, + 'city' => null, + 'country' => $pax['citizen'], + 'email' => null, + 'phone' => null, + 'state' => null, + 'zip' => null, + ],*/ + 'name' => [ + 'first' => $pax['name'], + 'last' => $pax['surname'], + ], + //'title' => null + ]; + } + + $channelRoom = collect($channelManagerPropertyMapping['channel_manager_room_rate'])->where('property_room_rate_mapping.room_id', $roomDetail['room_id'])->first(); + $channelRoomRate = collect($channelManagerPropertyMapping['channel_manager_room_rate'])->where('property_room_rate_mapping_id', $roomDetail['room_rate_mapping_id'])->first(); + + $jsonResponse['rooms'][$roomKey] = [ + 'roomCode' => $channelRoom['channel_manager_room_id'], + 'rateCode' => $channelRoomRate['channel_manager_room_rate_id'], + 'expectedPrice' => [ + 'amount' => $roomDetail['total'], + 'currency' => $roomDetail['currency_code'], + ], + 'guests' => $roomPax, + 'specialRequests' => [ + $roomDetail['room_name'], $roomDetail['room_rate_name'], + ] + + ]; + + } + + + $metaNotes = []; + + $metaNotes[] = [ + 'key' => 'Source', + 'value' => 'Extranetwork', + ]; + + $metaNotes[] = [ + 'key' => 'BookingId', + 'value' => $bookingDetail['id'], + ]; + + $metaNotes[] = [ + 'key' => 'BookingCode', + 'value' => $bookingDetail['booking_code'], + ]; + + $metaNotes[] = [ + 'key' => 'ChannelBookingCode', + 'value' => !empty($bookingDetail['channel_booking_code']) ? $bookingDetail['channel_booking_code'] : false, + ]; + + $metaNotes[] = [ + 'key' => 'Channel', + 'value' => $bookingDetail['booking_channel']['name'], + ]; + + $metaNotes[] = [ + 'key' => 'Payment', + 'value' => $bookingDetail['booking_payment_type']['code'] . ': ' . $bookingDetail['booking_payment_type']['name'], + ]; + + $metaNotes[] = [ + 'key' => 'Status', + 'value' => $bookingDetail['booking_status']['name'], + ]; + + if (!empty($bookingDetail['booking_contact']['note'])) { + $metaNotes[] = [ + 'key' => 'Note', + 'value' => $bookingDetail['booking_contact']['note'], + ]; + } + + $jsonResponse['meta'] = $metaNotes; + $jsonResponse['isTest'] = (App::environment() == 'production') ? false : true; + $jsonResponse['groupBooking'] = false; + + return json_encode($jsonResponse); + + + } catch (ApiErrorException | Exception $e) { + + $jsonResponse = null; + + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + } + + + return $jsonResponse; + + } + + public function requestPostReservationPush($jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + + $jsonPayloadArray = json_decode($jsonPayload, 1); + $status = collect($jsonPayloadArray['meta'])->where('key', 'Status')->pluck('value')->first(); + $bookingId = collect($jsonPayloadArray['meta'])->where('key', 'BookingId')->pluck('value')->first(); + $bookingCode = collect($jsonPayloadArray['meta'])->where('key', 'BookingCode')->pluck('value')->first(); + $channelBookingCode = collect($jsonPayloadArray['meta'])->where('key', 'ChannelBookingCode')->pluck('value')->first(); + + if ($status == 'Booking') { + $method = '/2.0/booking/create'; + } elseif ($status == 'Cancel/Refund') { + $method = '/2.0/booking/cancel'; + } + + if ($status == 'Cancel/Refund') { + $jsonResponse = [ + 'bookingId' => $channelBookingCode, + 'reason' => 'N/A', + 'simulation' => false, + ]; + $jsonPayload = json_encode($jsonResponse); + } + + try { + + $this->restClient = new Client(['http_errors' => false]); + + $parameter = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->bearerToken + ] + ]; + + + //$jsonPayload = '{"dates":{"from":"2024-08-30","to":"2024-08-31"},"propertyId":"90365","leadGuest":{"birthDate":"1980-01-01","contact":{"address":false,"city":false,"country":"TR","email":"cem@test.com","phone":"+4932121321","state":false,"zip":false},"name":{"first":"cem","last":"test"},"title":"Mr"},"reference":{"agency":"BKG240730-5VI02ISW"},"paymentDetails":{"type":"external"},"rooms":[{"roomCode":"DBL","rateCode":"BB","expectedPrice":{"amount":200.75,"currency":"EUR"},"guests":[{"birthDate":"1988-07-20","name":{"first":"dsada","last":"sadsa"}},{"birthDate":"1988-07-24","name":{"first":"sad","last":"sadfds"}}],"specialRequests":["Standart Room With Sea View","Bed and Breakfast"]}],"meta":[{"key":"Source","value":"Extranetwork"},{"key":"BookingId","value":46502},{"key":"BookingCode","value":"BKG240730-5VI02ISW"},{"key":"ChannelBookingCode","value":false},{"key":"Channel","value":"Web Booking Engine"},{"key":"Payment","value":"HTL: Pay at Hotel"},{"key":"Status","value":"Booking"},{"key":"Note","value":"asdasd"}],"isTest":true,"groupBooking":false}'; + + if (!empty($jsonPayload)) { + $parameter['body'] = $jsonPayload; + } + + + $request = $this->restClient->request('POST', $this->reservationPushUrl . $method, $parameter); + + $getResponseBody = $request->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponse = json_decode($getResponse, 1); + + //dd(json_encode($getResponse)); + + //Log::debug($method); + //Log::debug($parameter); + //Log::debug(json_encode($getResponse)); + //Log::debug($request->getStatusCode()); + + if ($request->getStatusCode() != 200) { + + if (isset($getResponse['error'])) { + if (is_array($getResponse['errorDetails']) && !empty($getResponse['errorDetails'])) { + $errors = $getResponse['errorDetails']; + $firstError = reset($errors); + throw new ApiErrorException($firstError['message']); + } elseif (!empty($getResponse['errorDetails'])) { + throw new ApiErrorException($getResponse['errorDetails']); + } else { + throw new ApiErrorException($getResponse['error']); + } + + } else { + $errors = singleElementArray($getResponse); + $firstError = reset($errors); + throw new ApiErrorException($firstError['errorCode'] . ': ' . $firstError['errorDetails']); + } + + } + + if ($status == 'Booking') { + $channelBookingCodeUpdate = $this->bookingService->update($bookingId, ['channel_booking_code' => $getResponse['content']['bookingId']]); + } + + + $response = ['status' => true, 'message' => '', 'response' => json_encode($getResponse)]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function reservationListParam($propertyId) + { + + $response = ['status' => false, 'message' => null]; + + return $response; + } + + public function reservationList($propertyId) + { + + $response = ['status' => false, 'message' => null]; + + return $response; + } + + public function roomAvailabilityPush($propertyId) + { + + $response = ['status' => false, 'message' => null]; + + return $response; + } + +} diff --git a/app/Core/Service/ChannelManager/Mirai.php b/app/Core/Service/ChannelManager/Mirai.php new file mode 100644 index 0000000..70f6fce --- /dev/null +++ b/app/Core/Service/ChannelManager/Mirai.php @@ -0,0 +1,685 @@ +restClient = $restClient; + $this->mailer = $mailer; + + /*if (App::environment() == 'production') { + $this->login = 'extranetwork-xml-100378788'; + $this->password = 'Burhan21'; + } else { + $this->login = 'ExtranetWorktest-xml-100378347'; + $this->password = 'ifXHs3EJc-$>'; + }*/ + + $this->login = null; + $this->password = null; + $this->url = 'https://api.mirai.com/XMLIntegrationx/'; + + $this->channelManagerId = 7; + $this->channelManagerName = 'Mirai'; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + + } + + public function setupUser($login, $password) + { + $this->login = $login; + $this->password = $password; + } + + public function request($type, $method, $xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + $this->restClient = new Client(['http_errors' => false]); + + $formAuthParam = [ + 'login' => $this->login, + 'password' => $this->password, + ]; + + if(empty($this->login)) { + throw new ApiErrorException('Login data not set!'); + } + + $parameter = [ + 'headers' => [], + 'query' => $formAuthParam, + ]; + + if (!empty($xmlPayload)) { + $parameter['body'] = $xmlPayload; + } + + //Log::debug($xmlPayload); + + $request = $this->restClient->request($type, $this->url . $method, $parameter); + + $getResponseBody = $request->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponseXml = new \SimpleXMLElement($getResponse); + $getResponse = json_decode(json_encode($getResponseXml), 1); + + if (isset($getResponse['message']['error'])) { + $errorMessage = is_array($getResponse['message']['error']['comment']) ? implode(',', $getResponse['message']['error']['comment']) : $getResponse['message']['error']['comment']; + throw new ApiErrorException($errorMessage); + } + + if ($request->getStatusCode() != 200) { + + if($request->getReasonPhrase()) { + $firstError = $request->getReasonPhrase(); + } else { + $firstError = is_array($getResponse['message']['error']['comment']) ? implode(',', $getResponse['message']['error']['comment']) : $getResponse['message']['error']['comment']; + } + + /*Log::debug('__REQ__'); + Log::debug($this->url); + Log::debug($type); + Log::debug($method); + Log::debug($xmlPayload); + Log::debug('__RES__'); + Log::debug($getResponse); + Log::debug(PHP_EOL); */ + + throw new ApiErrorException($firstError); + } + + $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, '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 $response; + + } + + + public function inventoryRoomRateUpdate($method, $xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + + + $request = $this->request('POST', $method, $xmlPayload); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $response = [ + 'status' => true, + 'data' => null + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + /** Pattern Functions */ + /* + public function availabilityUpdateRequestMultiParam($propertyId, $propertyRoomRateMapping, $params) + { + + $idList = []; + + $requestParam['values'] = []; + + ksort($params); + + $dataByRoomDate = []; + $propertyRoomRateMappingCollect = collect($propertyRoomRateMapping); + foreach ($params as $currentDate => $rooms) { + foreach ($rooms as $roomId => $value) { + $uniqueKey = $value['availability']; + + $roomIdManipulate = $roomId; + $roomIdManipulateCheck = $propertyRoomRateMappingCollect->where('channel_manager_room_id', $roomId)->where('property_room_rate_mapping.status', 1)->first(); + if (!empty($roomIdManipulateCheck)) { + $roomIdManipulate = $roomIdManipulateCheck['channel_manager_room_rate_id']; + } + + $dataByRoomDate[$roomIdManipulate][$uniqueKey][$currentDate] = $value; + } + } + + $requestParam = []; + + $valueKey = 0; + foreach ($dataByRoomDate as $roomId => $rooms) { + + foreach ($rooms as $uniqueKey => $uniqueValues) { + + $uniqueValueDate = array_keys($uniqueValues); + + $dateKey = 0; + $dateGroup = []; + for ($i = 0; $i < count($uniqueValueDate); $i++) { + $dateGroup[$dateKey][] = $uniqueValueDate[$i]; + if ($i + 1 < count($uniqueValueDate)) { + if (Carbon::parse($uniqueValueDate[$i])->diffInDays(Carbon::parse($uniqueValueDate[$i + 1])) > 1) { + $dateKey++; + } + } + } + + foreach ($uniqueValues as $uniqueValue) { + $idList = array_merge($idList, $uniqueValue['idList']); + } + + $currentValue = reset($uniqueValues); + + + foreach ($dateGroup as $dates) { + + if (count($dates) > 1) { + + $requestParam[$roomId]['data'][$valueKey] = [ + 'property_id' => $propertyId, + 'room_type_id' => $roomId, + 'date_from' => reset($dates), + 'date_to' => last($dates), + 'availability' => $currentValue['availability'] + ]; + + } else { + + $requestParam[$roomId]['data'][$valueKey] = [ + 'property_id' => $propertyId, + 'room_type_id' => $roomId, + 'date_from' => reset($dates), + 'date_to' => reset($dates), + 'availability' => $currentValue['availability'] + ]; + + } + + $valueKey++; + + } + + + } + } + + + $xmlResponse = new \SimpleXMLElement(''); + + $xmlResponse->addAttribute('hotelId', $propertyId); + + foreach ($requestParam as $roomId => $roomData) { + + $room = $xmlResponse->addChild('room'); + $room->addAttribute('id', $roomId); + + $roomInventory = $room->addChild('inventory'); + + foreach ($roomData['data'] as $roomRate) { + $roomInventoryAvailability = $roomInventory->addChild('availability'); + + $roomInventoryAvailability->addAttribute('from', $roomRate['date_from']); + $roomInventoryAvailability->addAttribute('to', $roomRate['date_to']); + $roomInventoryAvailability->addAttribute('quantity', $roomRate['availability']); + + } + + } + + return [ + 'idList' => $idList, + 'payload' => $xmlResponse->asXML() + ]; + } + + public function roomAvailabilityPush($propertyId, $bulkQueueData) + { + $response = ['status' => false, 'message' => '']; + + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true, + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + $channelManagerPushedIds = []; + $channelManagerNoneMappingIds = []; + $channelManagerPushConfirmCode = []; + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']); + + + $roomAvailabilityQueueForUpdate = []; + $roomAvailabilityQueueForUpdateIdList = []; + $roomAvailabilityQueue = $bulkQueueData; + + + try { + + foreach ($roomAvailabilityQueue as $roomAvailability) { + + + //Dates less than today cannot be updated. + if (Carbon::parse($roomAvailability['date'])->lessThan(Carbon::now()->toDateString())) { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem', + 'logMessage' => '
' . print_r($roomAvailability, true) . '
' + ]; + + //$this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $channelManagerPushedIds[] = $roomAvailability['id']; + + continue; + + } + + $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping.room_id', $roomAvailability['property_room_id'])->first(); + + if (empty($channelManagerRoomRate)) { + //None Mapping! + //$channelManagerPushedIds[] = $roomAvailability['id']; + $channelManagerNoneMappingIds[] = $roomAvailability['id']; + + continue; + } + + + $roomAvailabilityQueueForUpdateIdList[$roomAvailability['date']][] = $roomAvailability['id']; + + + $roomAvailabilityQueueForUpdate[$roomAvailability['date']] + [$channelManagerRoomRate['channel_manager_room_id']] = [ + 'id' => $roomAvailability['id'], + 'idList' => $roomAvailabilityQueueForUpdateIdList[$roomAvailability['date']], + 'date' => $roomAvailability['date'], + 'availability' => $roomAvailability['stop_sell'] == 1 ? 0 : $roomAvailability['availability'] + ]; + + } + + $roomAvailabilityUpdateRequestMultiParam = $this->availabilityUpdateRequestMultiParam($channelManagerPropertyMapping['channel_manager_property_id'], $channelManagerPropertyMapping['channel_manager_room_rate'], $roomAvailabilityQueueForUpdate); + $request = $this->inventoryRoomRateUpdate('webservice_updater.apro', $roomAvailabilityUpdateRequestMultiParam['payload']); + + if ($request['status']) { + + $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomAvailabilityUpdateRequestMultiParam['idList']); + $channelManagerPushConfirmCode[] = $request['data']['confirmId']; + + } else { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateAvailabilityUpdate Error', + 'logMessage' => '
' . print_r(array_merge($request, $roomAvailabilityUpdateRequestMultiParam), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + throw new ApiErrorException($request['message']); + } + + + } catch (ApiErrorException $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . implode(', ', $e->getMessageArr()); + Log::error($message); + //$response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + //$response['message'] = $e->getMessage(); + } + + + } else { + $channelManagerNoneMappingIds = collect($bulkQueueData)->pluck('id')->toArray(); + } + + + $channelManagerPushedIds = array_unique($channelManagerPushedIds); + asort($channelManagerPushedIds); + $channelManagerPushedIds = array_values($channelManagerPushedIds); + + $response = [ + 'status' => true, + 'data' => [ + 'confirmCode' => $channelManagerPushConfirmCode, + 'channelManagerPushedIds' => $channelManagerPushedIds, + 'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds + ] + ]; + + return $response; + } + + + public function roomRateUpdateRequestMultiParam($propertyId, $params) + { + + $idList = []; + ksort($params); + + $dataByRoomRate = []; + foreach ($params as $currentDate => $rooms) { + foreach ($rooms as $roomId => $rates) { + foreach ($rates as $rateId => $value) { + $uniqueKey = $value['amount'] . '|' . $value['stop_sell'] . '|' . $value['min_stay']; + $dataByRoomRate[$rateId][$uniqueKey][$currentDate] = $value; + } + } + } + + //Log::debug(json_encode($dataByRoomRate)); + + $requestParam = []; + + $valueKey = 0; + foreach ($dataByRoomRate as $rateId => $rates) { + + $valueKey = 0; + foreach ($rates as $uniqueKey => $uniqueValues) { + + + $uniqueValueDate = array_keys($uniqueValues); + + $dateKey = 0; + $dateGroup = []; + for ($i = 0; $i < count($uniqueValueDate); $i++) { + $dateGroup[$dateKey][] = $uniqueValueDate[$i]; + if ($i + 1 < count($uniqueValueDate)) { + if (Carbon::parse($uniqueValueDate[$i])->diffInDays(Carbon::parse($uniqueValueDate[$i + 1])) > 1) { + $dateKey++; + } + } + } + + foreach ($uniqueValues as $uniqueValue) { + $idList = array_merge($idList, $uniqueValue['idList']); + } + + $currentValue = reset($uniqueValues); + + foreach ($dateGroup as $dates) { + + $requestParam[$rateId][$valueKey] = [ + 'property_id' => $propertyId, + 'rate_plan_id' => $rateId, + 'date_from' => reset($dates), + 'date_to' => last($dates), + 'amount' => moneyDoubleFormatDecimal($currentValue['amount']), + 'stop_sell' => fillOnUndefined($currentValue, 'stop_sell', 0), + 'min_stay' => fillOnUndefined($currentValue, 'min_stay', 1) != 0 ? $currentValue['min_stay'] : 1, + 'currency' => $currentValue['currency'], + ]; + + if ($requestParam[$rateId][$valueKey]['amount'] == 0) { + $requestParam[$rateId][$valueKey]['stop_sell'] = 1; + } + + $valueKey++; + + } + + } + } + + + $xmlResponse = new \SimpleXMLElement(''); + + $xmlResponse->addAttribute('hotelId', $propertyId); + + foreach ($requestParam as $roomId => $roomData) { + + $room = $xmlResponse->addChild('room'); + $room->addAttribute('id', $roomId); + + $currencyCode = reset($roomData)['currency']; + + $roomRate = $room->addChild('rate'); + $roomRate->addAttribute('currency', $currencyCode); + + foreach ($roomData as $roomRateData) { + + $roomRatePlaning = $roomRate->addChild('planning'); + + $roomRatePlaning->addAttribute('from', $roomRateData['date_from']); + $roomRatePlaning->addAttribute('to', $roomRateData['date_to']); + $roomRatePlaning->addAttribute('unitPrice', $roomRateData['stop_sell'] == 1 ? 0 : $roomRateData['amount']); + $roomRatePlaning->addAttribute('minimumStay', $roomRateData['min_stay']); + + } + + } + + return [ + 'idList' => $idList, + 'payload' => $xmlResponse->asXML() + ]; + + } + + + public function roomRatePricePush($propertyId, $bulkQueueData) + { + $response = ['status' => false, 'message' => '']; + + try { + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true, + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + $channelManagerPushedIds = []; + $channelManagerNoneMappingIds = []; + $channelManagerPushConfirmCode = null; + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']); + + + $roomRatePriceQueueForUpdate = []; + $roomRatePriceQueueForUpdateIdList = []; + $roomRatePriceQueue = $bulkQueueData; + + + foreach ($roomRatePriceQueue as $roomRatePrice) { + + + //Dates less than today cannot be updated. + if (Carbon::parse($roomRatePrice['date'])->lessThan(Carbon::now()->toDateString())) { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem', + 'logMessage' => '
' . print_r($roomRatePrice, true) . '
' + ]; + + //$this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $channelManagerPushedIds[] = $roomRatePrice['id']; + + continue; + + } + + $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping_id', $roomRatePrice['room_rate_mapping_id'])->first(); + + if (empty($channelManagerRoomRate)) { + //None Mapping! + //$channelManagerPushedIds[] = $roomAvailability['id']; + $channelManagerNoneMappingIds[] = $roomRatePrice['id']; + + //TODO: Burada oda eşleşmiyorsa süreç devam ediyor ama uyarı maili atılabilir. + + continue; + } + + + $roomRatePriceQueueForUpdateIdList + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']] + [$channelManagerRoomRate['channel_manager_room_rate_id']][] = $roomRatePrice['id']; + + + $roomRatePriceQueueForUpdate + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomRatePrice['date']] + [$channelManagerRoomRate['channel_manager_room_id']] + [$channelManagerRoomRate['channel_manager_room_rate_id']] = [ + 'id' => $roomRatePrice['id'], + 'idList' => $roomRatePriceQueueForUpdateIdList[$channelManagerPropertyMapping['channel_manager_property_id']][$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']][$channelManagerRoomRate['channel_manager_room_rate_id']], + 'date' => $roomRatePrice['date'], + 'amount' => $roomRatePrice['amount'], + 'currency' => $roomRatePrice['currency'], + 'stop_sell' => $roomRatePrice['stop_sell'], + 'min_stay' => $roomRatePrice['min_stay'], + ]; + + } + + + foreach ($roomRatePriceQueueForUpdate as $channelManagerPropertyId => $channelManagerProperty) { + + + if (empty($channelManagerProperty)) { + throw new ApiErrorException('$channelManagerProperty is Empty!'); + } + + + $roomRateUpdateRequestMultiParam = $this->roomRateUpdateRequestMultiParam($channelManagerPropertyId, $channelManagerProperty); + $request = $this->inventoryRoomRateUpdate('webservice_updater.apro', $roomRateUpdateRequestMultiParam['payload']); + + if ($request['status']) { + + $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomRateUpdateRequestMultiParam['idList']); + $channelManagerPushConfirmCode = $request['data']['confirmId']; + + } else { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateUpdate Error', + 'logMessage' => '
' . print_r($request, true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + throw new ApiErrorException($request['message']); + } + + } + + } + + $channelManagerPushedIds = array_unique($channelManagerPushedIds); + asort($channelManagerPushedIds); + $channelManagerPushedIds = array_values($channelManagerPushedIds); + + $response = [ + 'status' => true, + 'data' => [ + 'confirmCode' => $channelManagerPushConfirmCode, + 'channelManagerPushedIds' => $channelManagerPushedIds, + 'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds + ] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + */ + +} diff --git a/app/Core/Service/ChannelManager/Reseliva.php b/app/Core/Service/ChannelManager/Reseliva.php new file mode 100644 index 0000000..30aeeea --- /dev/null +++ b/app/Core/Service/ChannelManager/Reseliva.php @@ -0,0 +1,1236 @@ +restClient = $restClient; + $this->mailer = $mailer; + $this->url = 'https://www.reseliva.net/siteBase/REST/cms/service/'; + //$this->url = 'https://api.reseliva.com/pms/service/'; + $this->reservationPushUrl = 'https://www.reseliva.net/siteBase/utility/CM/ExtranetWork/notify.php'; + + $this->statusMapping = [ + 'A' => 'Active', + 'C' => 'Canceled', + 'M' => 'Modified', + ]; + + $this->typeMapping = [ + 'Booking' => 'Book', + 'Modify' => 'Modify', + 'Cancel' => 'Cancel' + ]; + + $this->paymentTypeMapping = [ + 'MO' => 'Money Order', + 'CC' => 'Credit Card given', + 'POS' => 'Payed with payment system', + 'PayPal' => 'Payed with PayPal' + ]; + + $this->channelManagerId = 1; + $this->channelManagerName = 'Reseliva'; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + + } + + public function requestPost($method, $xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + $this->restClient = new Client(['http_errors' => false]); + $res = $this->restClient->post($this->url . $method, + [ + 'headers' => [ + 'Content-Type' => 'text/xml; charset=UTF8', + 'Accept' => 'text/xml' + ], + 'body' => $xmlPayload, + ] + ); + + $getResponseBody = $res->getBody(); + $getResponse = $getResponseBody->getContents(); + + if (empty($getResponse)) { + throw new ApiErrorException('Empty XML response'); + } + + $getResponseXml = new \SimpleXMLElement($getResponse, LIBXML_NOCDATA); + $getResponse = json_decode(json_encode($getResponseXml), 1); + + $response = ["status" => true, 'message' => '', "data" => $getResponse]; + + } catch (ApiErrorException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage() . ' ' . $method; + Log::error($message); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage() . ' ' . $method; + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function productList($reselivaPropertyId) + { + $response = ['status' => false, 'message' => '']; + + try { + + $userId = Config::get('app.channelManager.reseliva.userId'); + $userPSW = Config::get('app.channelManager.reseliva.userPassword'); + + $requestParam = new \SimpleXMLElement(''); + + $Authentication = $requestParam->addChild('Authentication'); + $Authentication->addChild('UserID', $userId); + $Authentication->addChild('UserPSW', $userPSW); + $Authentication->addChild('PropertyID', $reselivaPropertyId); + + $xmlPayload = $requestParam->asXML(); + + $request = $this->requestPost('product_list', $xmlPayload); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $responseData = []; + $responseData['hotelId'] = $request['data']['@attributes']['hotel_id']; + + $roomType = singleElementXMLArray($request['data']['RoomType']); + foreach ($roomType as $room) { + + $responseData['rooms'][$room['@attributes']['id']] = [ + 'id' => $room['@attributes']['id'], + 'name' => $room['@attributes']['name'], + ]; + + $room['RatePlan'] = singleElementXMLArray($room['RatePlan']); + foreach ($room['RatePlan'] as $rate) { + + $responseData['rooms'][$room['@attributes']['id']]['rate'][$rate['@attributes']['id']] = [ + 'id' => $rate['@attributes']['id'], + 'name' => htmlspecialchars_decode($rate['@attributes']['name']), + ]; + + } + + } + + $response = [ + 'status' => true, + 'data' => $responseData + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + public function inventoryRoomRateUpdate($xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + + $request = $this->requestPost('inventory', $xmlPayload); + + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + if (!isset($request['data']['success'])) { + $request['data']['error']['erroritem'] = singleElementXMLArray($request['data']['error']['erroritem']); + $errorsCollect = collect($request['data']['error']['erroritem']); + throw new ApiErrorException($request['data']['error']['@attributes']['msg'] . '. Errors: ' . implode(', ', $errorsCollect->pluck('@attributes.desc')->toArray())); + } + + $response = [ + 'status' => true, + 'data' => [ + 'confirmId' => $request['data']['rqid'] + ] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + + /** Pattern Functions */ + + public function availabilityUpdateRequestMultiParam($propertyId, $params) + { + + $idList = []; + $userId = Config::get('app.channelManager.reseliva.userId'); + $userPSW = Config::get('app.channelManager.reseliva.userPassword'); + + $requestParam = new \SimpleXMLElement(''); + + $Authentication = $requestParam->addChild('Authentication'); + $Authentication->addChild('UserID', $userId); + $Authentication->addChild('UserPSW', $userPSW); + $Authentication->addChild('PropertyID', $propertyId); + + $inventory = $requestParam->addChild('inventory'); + + foreach ($params as $updateDate => $rooms) { + + //foreach + $day = $inventory->addChild('day'); + $date = $day->addChild('date'); + $date->addAttribute('from', $updateDate); + $date->addAttribute('to', $updateDate); + + $roomTypes = $day->addChild('roomtypes'); + + foreach ($rooms as $roomId => $value) { + //foreach + $roomType = $roomTypes->addChild('roomtype'); + $roomType->addAttribute('reseliva_id', $roomId); + $roomType->addAttribute('availability', fillOnUndefined($value, 'availability', 0)); + $roomType->addAttribute('stopsale', fillOnUndefined($value, 'stop_sell', 0)); + + $idList = array_merge($idList, $value['idList']); + + } + + } + + return [ + 'idList' => $idList, + 'xmlPayload' => $requestParam->asXML() + ]; + } + + public function roomAvailabilityPush($propertyId, $bulkQueueData) + { + $response = ['status' => false, 'message' => '']; + + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true, + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + $channelManagerPushedIds = []; + $channelManagerNoneMappingIds = []; + $channelManagerPushConfirmCode = []; + + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']); + + + $roomAvailabilityQueueForUpdate = []; + $roomAvailabilityQueueForUpdateIdList = []; + $roomAvailabilityQueue = $bulkQueueData; + + + foreach ($roomAvailabilityQueue as $roomAvailability) { + + + //Dates less than today cannot be updated. + if (Carbon::parse($roomAvailability['date'])->lessThan(Carbon::now()->toDateString())) { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem', + 'logMessage' => '
' . print_r($roomAvailability, true) . '
' + ]; + + //$this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $channelManagerPushedIds[] = $roomAvailability['id']; + + continue; + + } + + $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping.room_id', $roomAvailability['property_room_id'])->first(); + + if (empty($channelManagerRoomRate)) { + //None Mapping! + //$channelManagerPushedIds[] = $roomAvailability['id']; + $channelManagerNoneMappingIds[] = $roomAvailability['id']; + + continue; + } + + $roomAvailabilityQueueForUpdateIdList + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomAvailability['date']][] = $roomAvailability['id']; + + + $roomAvailabilityQueueForUpdate + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomAvailability['date']] + [$channelManagerRoomRate['channel_manager_room_id']] = [ + 'id' => $roomAvailability['id'], + 'idList' => $roomAvailabilityQueueForUpdateIdList[$channelManagerPropertyMapping['channel_manager_property_id']][$roomAvailability['date']], + 'date' => $roomAvailability['date'], + 'availability' => $roomAvailability['availability'], + 'stop_sell' => $roomAvailability['stop_sell'], + ]; + + } + + foreach ($roomAvailabilityQueueForUpdate as $channelManagerPropertyId => $channelManagerProperty) { + + try { + + ksort($channelManagerProperty); + $roomAvailabilityUpdateRequestMultiParam = $this->availabilityUpdateRequestMultiParam($channelManagerPropertyId, $channelManagerProperty); + + $request = $this->inventoryRoomRateUpdate($roomAvailabilityUpdateRequestMultiParam['xmlPayload']); + + if ($request['status']) { + + $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomAvailabilityUpdateRequestMultiParam['idList']); + $channelManagerPushConfirmCode[] = $request['data']['confirmId']; + + } else { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateAvailabilityUpdate Error', + 'logMessage' => '
' . print_r(array_merge($request, $channelManagerProperty), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + throw new ApiErrorException($request['message']); + } + + } catch (ApiErrorException $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . implode(', ', $e->getMessageArr()); + Log::error($message); + //$response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + //$response['message'] = $e->getMessage(); + } + + } + + } else { + $channelManagerNoneMappingIds = collect($bulkQueueData)->pluck('id')->toArray(); + } + + $channelManagerPushedIds = array_unique($channelManagerPushedIds); + asort($channelManagerPushedIds); + $channelManagerPushedIds = array_values($channelManagerPushedIds); + + $response = [ + 'status' => true, + 'data' => [ + 'confirmCode' => $channelManagerPushConfirmCode, + 'channelManagerPushedIds' => $channelManagerPushedIds, + 'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds + ] + ]; + + return $response; + } + + + public function roomRateUpdateRequestMultiParam($propertyId, $params) + { + $idList = []; + $userId = Config::get('app.channelManager.reseliva.userId'); + $userPSW = Config::get('app.channelManager.reseliva.userPassword'); + + $requestParam = new \SimpleXMLElement(''); + + $Authentication = $requestParam->addChild('Authentication'); + $Authentication->addChild('UserID', $userId); + $Authentication->addChild('UserPSW', $userPSW); + $Authentication->addChild('PropertyID', $propertyId); + + $inventory = $requestParam->addChild('inventory'); + + foreach ($params as $updateDate => $rooms) { + + //foreach + $day = $inventory->addChild('day'); + $date = $day->addChild('date'); + $date->addAttribute('from', $updateDate); + $date->addAttribute('to', $updateDate); + + $roomTypes = $day->addChild('roomtypes'); + + foreach ($rooms as $roomId => $rates) { + + //foreach + $roomType = $roomTypes->addChild('roomtype'); + $roomType->addAttribute('reseliva_id', $roomId); + //$roomType->addAttribute('stopsale', fillOnUndefined($value, 'stop_sell')); + + foreach ($rates as $rateId => $value) { + //foreach + $ratePlan = $roomType->addChild('RatePlan'); + $ratePlan->addAttribute('reseliva_id', $rateId); + $rate = $ratePlan->addChild('Rate'); + $rate->addAttribute('price1', floatval($value['amount'])); + + $restrictions = $ratePlan->addChild('Restrictions'); + $restrictions->addAttribute('closed', fillOnUndefined($value, 'stop_sell', 0)); + + if (isset($value['min_stay']) && !in_array($value['min_stay'], [0])) { + $restrictions->addAttribute('minLOS', fillOnUndefined($value, 'min_stay', 1)); + } + + $idList = array_merge($idList, $value['idList']); + } + + } + + } + + return [ + 'idList' => $idList, + 'xmlPayload' => $requestParam->asXML() + ]; + + } + + public function roomRatePricePush($propertyId, $bulkQueueData) + { + $response = ['status' => false, 'message' => '']; + + try { + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true, + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + $channelManagerPushedIds = []; + $channelManagerNoneMappingIds = []; + $channelManagerPushConfirmCode = null; + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']); + + + $roomRatePriceQueueForUpdate = []; + $roomRatePriceQueueForUpdateIdList = []; + $roomRatePriceQueue = $bulkQueueData; + + + foreach ($roomRatePriceQueue as $roomRatePrice) { + + + //Dates less than today cannot be updated. + if (Carbon::parse($roomRatePrice['date'])->lessThan(Carbon::now()->toDateString())) { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem', + 'logMessage' => '
' . print_r($roomRatePrice, true) . '
' + ]; + + //$this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $channelManagerPushedIds[] = $roomRatePrice['id']; + + continue; + + } + + $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping_id', $roomRatePrice['room_rate_mapping_id'])->first(); + + if (empty($channelManagerRoomRate)) { + //None Mapping! + //$channelManagerPushedIds[] = $roomAvailability['id']; + $channelManagerNoneMappingIds[] = $roomRatePrice['id']; + + continue; + } + + + $roomRatePriceQueueForUpdateIdList + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']] + [$channelManagerRoomRate['channel_manager_room_rate_id']][] = $roomRatePrice['id']; + + + $roomRatePriceQueueForUpdate + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomRatePrice['date']] + [$channelManagerRoomRate['channel_manager_room_id']] + [$channelManagerRoomRate['channel_manager_room_rate_id']] = [ + 'id' => $roomRatePrice['id'], + 'idList' => $roomRatePriceQueueForUpdateIdList[$channelManagerPropertyMapping['channel_manager_property_id']][$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']][$channelManagerRoomRate['channel_manager_room_rate_id']], + 'date' => $roomRatePrice['date'], + 'amount' => $roomRatePrice['amount'], + 'currency' => $roomRatePrice['currency'], + 'stop_sell' => $roomRatePrice['stop_sell'], + 'min_stay' => $roomRatePrice['min_stay'], + ]; + + } + + + foreach ($roomRatePriceQueueForUpdate as $channelManagerPropertyId => $channelManagerProperty) { + + + if (empty($channelManagerProperty)) { + throw new ApiErrorException('$channelManagerProperty is Empty!'); + } + + ksort($channelManagerProperty); + $roomRateUpdateRequestMultiParam = $this->roomRateUpdateRequestMultiParam($channelManagerPropertyId, $channelManagerProperty); + + $request = $this->inventoryRoomRateUpdate($roomRateUpdateRequestMultiParam['xmlPayload']); + + if ($request['status']) { + + $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomRateUpdateRequestMultiParam['idList']); + $channelManagerPushConfirmCode = $request['data']['confirmId']; + + } else { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateUpdate Error', + 'logMessage' => '
' . print_r($request, true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + throw new ApiErrorException($request['message']); + } + + } + + } + + $channelManagerPushedIds = array_unique($channelManagerPushedIds); + asort($channelManagerPushedIds); + $channelManagerPushedIds = array_values($channelManagerPushedIds); + + $response = [ + 'status' => true, + 'data' => [ + 'confirmCode' => $channelManagerPushConfirmCode, + 'channelManagerPushedIds' => $channelManagerPushedIds, + 'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds + ] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + + public function reservationPullFormattedData($propertyId, $channelManagerPropertyId, $param) + { + + $bookingParam = []; + + $response = ['status' => false, 'message' => '']; + + try { + + $isB2CBooking = false; + + //Check Channel Manager Mapping + $channelManagerMappingCriteria = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyChannelManager'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManagerMappingData = $this->channelManagerMappingService->select($channelManagerMappingCriteria); + $channelManagerMappingCollect = collect($channelManagerMappingData['data']); + $channelManagerMapping = $channelManagerMappingCollect->where('channel_manager_channel_id', $param['otaname'])->first(); + + + //Eğer kanala ait bir otel yok ise devam et + if (empty($channelManagerMapping)) { + //TODO: hata dönecek + //throw new ApiErrorException($request['message']); + } + //Check Channel Manager Mapping + + + //Check Channel Manager Property Mapping + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + //Eğer kanala ait bir otel yok ise devam et + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + ///TODO: hata dönecek + /// throw new ApiErrorException($request['message']); + } + + $channelManagerRoomRateCollect = collect($channelManagerPropertyMapping['data']['channel_manager_room_rate']); + + + if (empty(fillOnUndefined($param, 'reservno_ota'))) { + $param['reservno_ota'] = null; + } + + $roomRequest = []; + foreach ($param['room'] as $room) { + $roomRequest[] = [ + 'adults' => $room['totalpax'], + 'children' => $room['totalchd'], + 'age' => [] + ]; + } + + $paymentTypeCode = 'HTL'; + $paymentSourceCode = null; + if (isset($param['cc_token']) && !empty($param['cc_token'])) { + $paymentTypeCode = 'CRD'; + if (empty($param['prepaid'])) { + $paymentSourceCode = 'GST'; + } + if (!empty($param['prepaid'])) { + $paymentSourceCode = 'OTA'; + } + } + + //Booking + $bookingParam['booking'] = [ + 'id' => null, + 'booking_code' => null, + 'property_id' => $propertyId, + 'channel_manager_id' => $this->channelManagerId, + 'channel_id' => $channelManagerMapping['property_channel_id'], + 'channel_booking_code' => fillOnUndefined($param, 'reservno_ota'), + 'search_key' => $param['reservno'], + 'checkin_date' => $param['checkinFormatted'], + 'checkout_date' => $param['checkoutFormatted'], + 'rooms' => json_encode($roomRequest), + 'payment_type_code' => $paymentTypeCode, + 'room_amount' => $param['paymenttotal'], + 'addon_amount' => 0, + 'discount_amount' => 0, + 'total' => $param['paymenttotal'], + 'currency_code' => $param['paymentcurr'], + 'channel_token' => null, + 'booking_engine_token' => null, + 'reservation_time' => $param['restimeUnixFormatted'], + 'status' => $param['status'] == 'A' ? 1 : 0 + ]; + + $extraParamBookingContact = []; + + //Booking Contact + $bookingParam['contact'] = [ + 'name' => $param['firstname'], + 'surname' => !empty($param['lastname']) ? $param['lastname'] : null, + 'phone_code' => null, + 'phone_number' => fillOnUndefined($param, 'tel'), + 'email' => fillOnUndefined($param, 'email'), + 'country_code' => fillOnUndefined($param, 'country'), + 'note' => !empty($param['note']) ? $param['note'] : null, + 'language_code' => fillOnUndefined($param, 'language', 'en'), + 'extra_param' => !empty($extraParamBookingContact) ? json_encode($extraParamBookingContact) : null, + 'status' => 1 + ]; + + $bookingParam['contact']['language_code'] = mb_substr($bookingParam['contact']['language_code'], 0, 2); + $bookingParam['contact']['country_code'] = mb_strtolower($bookingParam['contact']['country_code']); + + //Booking Room + $param['room'] = array_values($param['room']); + foreach ($param['room'] as $roomOrder => $room) { + + + //Check Room Rate Mapping + $channelManagerRoomRate = $channelManagerRoomRateCollect->where('channel_manager_room_id', $room['roomid'])->where('channel_manager_room_rate_id', $room['rateid'])->first(); + if (empty($channelManagerRoomRate)) { + $channelManagerRoomRate = $channelManagerRoomRateCollect->where('channel_manager_room_id', $room['roomid'])->first(); + } + + $dailyAmount = []; + if (isset($room['price_breakdown']) && !empty($room['price_breakdown'])) { + foreach ($room['price_breakdown'] as $roomAmount) { + if (isset($roomAmount['@attributes'])) { + $dailyAmount[] = [ + 'date' => Carbon::parse($roomAmount['@attributes']['date'])->toDateString(), + 'amount' => floatval($roomAmount['@attributes']['price']), + 'currency_code' => $param['paymentcurr'] + ]; + } + } + } + + + $extraParamRoom = []; + + $bookingParam['room'][] = [ + 'room_order_number' => ($roomOrder + 1), + 'occupancy_code' => str_repeat('A', $room['totalpax']) . str_repeat('C', $room['totalchd']), + 'checkin_date' => $param['checkinFormatted'], //$room['checkin'], + 'checkout_date' => $param['checkoutFormatted'],//$room['checkout'], + 'rate_key' => null, + 'rate_key_code' => null, + 'availability_id' => 1, + 'availability_code' => 'ROM', + 'room_id' => $channelManagerRoomRate['property_room_rate_mapping']['room_id'], + 'room_name' => !empty($room['roomtype']) ? html_entity_decode($room['roomtype']) : null, + 'room_rate_mapping_id' => $channelManagerRoomRate['property_room_rate_mapping']['id'], + 'room_rate_name' => !empty($room['ratename']) ? html_entity_decode($room['ratename']) : null, + 'cancellation_policy' => null, + 'payment_type_code' => $paymentTypeCode, + 'daily_amount' => !empty($dailyAmount) ? json_encode($dailyAmount) : null, + 'extra_param' => !empty($extraParamRoom) ? json_encode($extraParamRoom) : null, + 'rate_detail' => json_encode($room), + 'total' => $room['total_amount'], + 'currency_code' => $param['paymentcurr'], + 'status' => $param['status'] == 'A' ? 1 : 0 + ]; + + + if (!$isB2CBooking && (mb_strpos(html_entity_decode($room['ratename']), 'B2C') || mb_strpos(html_entity_decode($room['ratename']), 'OPAQUE')) ) { + $isB2CBooking = true; + } + + } + + + //Booking Payment Data + $bookingParam['payment'] = [ + 'payment_code' => (isset($param['cc_token']) && !empty($param['cc_token'])) ? fillOnUndefined($param, 'cc_token') : null, + 'payment_type_code' => $paymentTypeCode, + 'payment_source_code' => $paymentSourceCode, + 'extra_param' => json_encode($param), + 'total' => $param['paymenttotal'], + 'currency_code' => $param['paymentcurr'], + 'status' => 2 + ]; + + + //Booking Channel Payment Data + $bookingParam['payment_channel'] = null; + if (isset($param['cc_token']) && !empty($param['cc_token'])) { + + $bookingParam['payment_channel'] = [ + 'type' => 'ch', + 'data' => md5($param['reservno'] . $channelManagerPropertyMapping['data']['channel_manager_property_id'] . $param['cc_token']) + ]; + + } + + //Channel Manager ETC Params + $bookingParam['channel_manager'] = [ + 'property_id' => $channelManagerPropertyId, + 'booking_id' => $param['reservno'], + 'sub_channel' => $param['otaname'], + 'sub_channel_booking_id' => $param['reservno_ota'], + 'sub_channel_source' => $param['source'], + ]; + + $statusType = null; + if ($param['status'] == 'A') { + $statusType = 'createBooking'; + } elseif ($param['status'] == 'C') { + $statusType = 'cancelBooking'; + } elseif ($param['status'] == 'M') { + $statusType = 'modifiedBooking'; + } + + //362: Barın Hotel, 34: Hotelbeds + if (in_array($propertyId, [362]) && in_array($bookingParam['booking']['channel_id'], [34])) { + if($isB2CBooking) { + //312: Hotelbeds B2C + $bookingParam['booking']['channel_id'] = 312; + } + } + + + //$statusType = 'modifiedBooking'; + + $response['status'] = true; + $response['type'] = $statusType; + $response['data'] = $bookingParam; + + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + public function reservationListParam($channelManagerPropertyId) + { + + $userId = Config::get('app.channelManager.reseliva.userId'); + $userPSW = Config::get('app.channelManager.reseliva.userPassword'); + + $requestParam = new \SimpleXMLElement(''); + + $Authentication = $requestParam->addChild('Authentication'); + $Authentication->addChild('UserID', $userId); + $Authentication->addChild('UserPSW', $userPSW); + $Authentication->addChild('PropertyID', $channelManagerPropertyId); + $requestParam->addChild('include_price_breakdown', 1); + + return $requestParam->asXML(); + } + + public function reservationList($xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + + $request = $this->requestPost('reservation_list', $xmlPayload); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $responseData = []; + $reservationList = isset($request['data']['reservation']) ? singleElementXMLArray($request['data']['reservation']) : []; + foreach ($reservationList as $reservation) { + + if(Carbon::parse($reservation['checkin'])->toDateString() < Carbon::now()->toDateString() && $reservation['status'] == 'A') { + continue; + } + + $reservation['paymenttype'] = empty($reservation['paymenttype']) ? null : $reservation['paymenttype']; + + $responseData[$reservation['reservno']] = [ + 'reservno' => $reservation['reservno'], + 'firstname' => $reservation['firstname'], + 'lastname' => $reservation['lastname'], + 'email' => !empty($reservation['email']) ? $reservation['email'] : null, + 'tel' => !empty($reservation['tel']) ? $reservation['tel'] : null, + 'status' => $reservation['status'], + 'statusText' => fillOnUndefined($this->statusMapping, $reservation['status']), + 'resdate' => $reservation['resdate'], + 'restime' => $reservation['restime'], + 'restimeUnixFormatted' => Carbon::parse($reservation['restime'],'UTC')->timestamp, + 'resdateFormatted' => Carbon::parse($reservation['resdate'])->toDateString(), + 'checkin' => $reservation['checkin'], + 'checkinFormatted' => Carbon::parse($reservation['checkin'])->toDateString(), + 'checkout' => $reservation['checkout'], + 'checkoutFormatted' => Carbon::parse($reservation['checkout'])->toDateString(), + 'nation' => !empty($reservation['nation']) ? $reservation['nation'] : null, + 'paymentcurr' => $reservation['paymentcurr'], + 'paymenttotal' => $reservation['paymenttotal'], + 'source' => $reservation['source'], + 'otaname' => $reservation['otaname'], + 'reservno_ota' => $reservation['reservno_ota'], + 'prepaid' => $reservation['prepaid'], + 'paymenttype' => $reservation['paymenttype'], + 'paymentTypeText' => fillOnUndefined($this->paymentTypeMapping, $reservation['paymenttype']), + 'restotalpax' => $reservation['restotalpax'], + 'restotalchd' => $reservation['restotalchd'], + 'note' => $reservation['note'], + 'cc_token' => fillOnUndefined($reservation, 'cc_token'), + ]; + + $roomList = singleElementXMLArray($reservation['rooms']['room']); + foreach ($roomList as $room) { + + $room['rateid'] = !empty($room['rateid']) ? $room['rateid'] : null; + $room['ratename'] = !empty($room['ratename']) ? $room['ratename'] : null; + $room['price_breakdown'] = !empty($room['price_breakdown']) && isset($room['price_breakdown']['day']) ? singleElementXMLArray($room['price_breakdown']['day']) : null; + + $responseData[$reservation['reservno']]['room'][] = $room; + } + + } + + $response = [ + 'status' => true, + 'data' => $responseData + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + + public function reservationConfirmParam($propertyId, $channelManagerBookingId, $bookingCode, $param = []) + { + + $userId = Config::get('app.channelManager.reseliva.userId'); + $userPSW = Config::get('app.channelManager.reseliva.userPassword'); + + $requestParam = new \SimpleXMLElement(''); + + $Authentication = $requestParam->addChild('Authentication'); + $Authentication->addChild('UserID', $userId); + $Authentication->addChild('UserPSW', $userPSW); + $Authentication->addChild('PropertyID', $propertyId); + + $reservations = $requestParam->addChild('reservations'); + $reservation = $reservations->addChild('reservation'); + $reservation->addAttribute('reseliva_id', $channelManagerBookingId); + $reservation->addAttribute('pms_id', $bookingCode); + + return $requestParam->asXML(); + } + + public function reservationConfirm($xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + + $request = $this->requestPost('reservation_confirm', $xmlPayload); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + if (!isset($request['data']['success'])) { + $request['data']['error']['erroritem'] = singleElementXMLArray($request['data']['error']['erroritem']); + $errorsCollect = collect($request['data']['error']['erroritem']); + throw new ApiErrorException($request['data']['error']['@attributes']['msg'] . '. Errors: ' . implode(', ', $errorsCollect->pluck('@attributes.desc')->toArray())); + } + + $response = [ + 'status' => true, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + + public function requestPostReservationPushParam($propertyId, $bookingId, $type, $param = []) + { + + $bookingDetail = $param['booking_detail']; + + $type = $this->typeMapping[$type]; + + $serviceRequestName = 'BookingRetrieval'; + $xmlResponse = new \SimpleXMLElement('<' . $serviceRequestName . 'RS>'); + + if (in_array($bookingDetail['status'], [0, 3])) { + + $Bookings = $xmlResponse->addChild('Bookings'); + $Booking = $Bookings->addChild('Booking'); + + if (Carbon::createFromTimestamp($param['booking_detail']['created_at'])->lessThan(Carbon::parse('2022-04-01'))) { + $Booking->addAttribute('id', $bookingDetail['id']); + } else { + $Booking->addAttribute('id', $bookingDetail['booking_code']); + } + + $Booking->addAttribute('type', 'Cancel'); + $Booking->addAttribute('cancelDateTime', Carbon::createFromTimestamp($bookingDetail['updated_at'])->toISOString()); + + $Hotel = $Booking->addChild('Hotel'); + $Hotel->addAttribute('id', $bookingDetail['property_id']); + + foreach ($bookingDetail['booking_room'] as $room) { + + $RoomStay = $Booking->addChild('RoomStay'); + $RoomStay->addAttribute('roomTypeID', $room['room_id']); + $RoomStay->addAttribute('ratePlanID', $room['room_rate_mapping_id']); + + $StayDate = $RoomStay->addChild('StayDate'); + $StayDate->addAttribute('arrival', $room['checkin_date']); + $StayDate->addAttribute('departure', $room['checkout_date']); + + } + + } else { + + + $Bookings = $xmlResponse->addChild('Bookings'); + $Booking = $Bookings->addChild('Booking'); + + if (Carbon::createFromTimestamp($param['booking_detail']['created_at'])->lessThan(Carbon::parse('2022-04-01'))) { + $Booking->addAttribute('id', $bookingDetail['id']); + } else { + $Booking->addAttribute('id', $bookingDetail['booking_code']); + } + + //Book ve Modify Aynı olacak, Cancel + if ($type == 'Book') { + $Booking->addAttribute('type', 'Book'); + $Booking->addAttribute('createDateTime', Carbon::createFromTimestamp($bookingDetail['created_at'])->toISOString()); + } + + if ($type == 'Modify') { + $Booking->addAttribute('type', 'Modify'); + $Booking->addAttribute('modifyDateTime', Carbon::createFromTimestamp($bookingDetail['updated_at'])->toISOString()); + } + + $Hotel = $Booking->addChild('Hotel'); + $Hotel->addAttribute('id', $bookingDetail['property_id']); + + + foreach ($bookingDetail['booking_room'] as $room) { + + $RoomStay = $Booking->addChild('RoomStay'); + $RoomStay->addAttribute('roomTypeID', $room['room_id']); + $RoomStay->addAttribute('ratePlanID', $room['room_rate_mapping_id']); + + $StayDate = $RoomStay->addChild('StayDate'); + $StayDate->addAttribute('arrival', $room['checkin_date']); + $StayDate->addAttribute('departure', $room['checkout_date']); + + + $roomPaxCollect = collect($room['room_pax']); + + $GuestCount = $RoomStay->addChild('GuestCount'); + $GuestCount->addAttribute('adult', $roomPaxCollect->where('type', 'ADT')->count()); + $GuestCount->addAttribute('child', $roomPaxCollect->where('type', 'CHD')->count()); + + if($roomPaxCollect->where('type', 'CHD')->count() > 0) { + $roomPaxChild = $roomPaxCollect->where('type', 'CHD')->toArray(); + foreach ($roomPaxChild as $child) { + $Child = $GuestCount->addChild('Child'); + $Child->addAttribute('age',Carbon::now()->diffInYears(Carbon::parse($child['birth_date'])->toDateString())); + } + } + + $diffInDays = Carbon::parse($room['checkin_date'])->diffInDays(Carbon::parse($room['checkout_date'])); + + //dd($room['total'], $diffInDays, $room); + + $PerDayRates = $RoomStay->addChild('PerDayRates'); + $PerDayRates->addAttribute('currency', $room['currency_code']); + + $baseRateDaily = moneyDoubleFormatDecimal($room['total'] / $diffInDays); + + $currentDate = $room['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $PerDayRate = $PerDayRates->addChild('PerDayRate'); + $PerDayRate->addAttribute('stayDate', $currentDate); + $PerDayRate->addAttribute('baseRate', $baseRateDaily); + $PerDayRate->addAttribute('hotelServiceFees', 0); + + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + + $Total = $RoomStay->addChild('Total'); + $Total->addAttribute('amountAfterTaxes', $room['total']); + $Total->addAttribute('amountOfTaxes', 0); + $Total->addAttribute('currency', $room['currency_code']); + + $roomNotes['cancellationPolicy'] = 'Cancellation Policy: Refundable'; + $cancellationPolicy = json_decode($room['cancellation_policy'], 1); + if (!empty($cancellationPolicy) && is_array($cancellationPolicy)) { + if (isset($cancellationPolicy['isNonRefundable']) && $cancellationPolicy['isNonRefundable']) { + $roomNotes['cancellationPolicy'] = 'Cancellation Policy: NonRefundable'; + } + } + + $roomNotes['payment'] = 'Payment: ' . $bookingDetail['booking_payment_type']['name']; + + $RoomStay->addChild('RoomNotes', implode('. ', $roomNotes)); + + } + + + $PrimaryGuest = $Booking->addChild('PrimaryGuest'); + + $Name = $PrimaryGuest->addChild('Name'); + $Name->addAttribute('givenName', $bookingDetail['booking_contact']['name']); + $Name->addAttribute('surname', $bookingDetail['booking_contact']['surname']); + + $Phone = $PrimaryGuest->addChild('Phone'); + $Phone->addAttribute('countryCode', $bookingDetail['booking_contact']['phone_code']); + //$Phone->addAttribute('cityAreaCode'); + $Phone->addAttribute('number', $bookingDetail['booking_contact']['phone_number']); + + $PrimaryGuest->addChild('Email', $bookingDetail['booking_contact']['email']); + + if(fillOnUndefined($bookingDetail['booking_contact'],'language_code')) { + $Booking->addChild('CountryCode', $bookingDetail['booking_contact']['language_code']); + } + + $Total = $Booking->addChild('Total'); + $Total->addAttribute('amountAfterTaxes', $bookingDetail['total']); + $Total->addAttribute('amountOfTaxes', 0); + $Total->addAttribute('currency', $bookingDetail['currency_code']); + + + $Booking->addChild('SpecialRequest'); + $Booking->addChild('BookingNotes', htmlspecialchars($bookingDetail['booking_contact']['note'])); + + } + + return $xmlResponse->asXML(); + + } + + public function requestPostReservationPush($xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + //U : uExtranetWork + //P : ExtrntWrk21!*22 + + try { + $this->restClient = new Client(['http_errors' => false]); + $res = $this->restClient->post($this->reservationPushUrl, + [ + 'headers' => [ + 'Content-Type' => 'text/xml; charset=UTF8', + 'Authorization' => 'Basic dUV4dHJhbmV0V29yazpFeHRybnRXcmsyMSEqMjI=', + 'Accept' => 'text/xml' + ], + 'body' => $xmlPayload, + ] + ); + + $getResponseBody = $res->getBody(); + $getResponseXmlBase = $getResponseBody->getContents(); + + $getResponseXml = new \SimpleXMLElement($getResponseXmlBase, LIBXML_NOCDATA); + $getResponse = json_decode(json_encode($getResponseXml), 1); + + if ($getResponse['Status'] != 'success') { + throw new ApiErrorException('requestPostReservationPush Error channelManagerName: ' . $this->channelManagerName); + } + + $response = ['status' => true, 'message' => '', 'response' => $getResponseXmlBase]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + +} diff --git a/app/Core/Service/ChannelManager/SistemOtel.php b/app/Core/Service/ChannelManager/SistemOtel.php new file mode 100644 index 0000000..7fa6a3f --- /dev/null +++ b/app/Core/Service/ChannelManager/SistemOtel.php @@ -0,0 +1,252 @@ +restClient = $restClient; + $this->mailer = $mailer; + $this->reservationPushUrl = 'https://cm.istbooking.com/push/extranetwork'; + + + $this->typeMapping = [ + 'Booking' => 'Book', + 'Modify' => 'Modify', + 'Cancel' => 'Cancel' + ]; + + $this->channelManagerId = 6; + $this->channelManagerName = 'SistemOtel'; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + $this->channelManagerBookingService = $channelManagerBookingService; + + } + + public function requestPostReservationPushParam($propertyId, $bookingId, $type, $param = []) + { + + $type = $this->typeMapping[$type]; + $bookingDetail = $param['booking_detail']; + + + $jsonResponse = []; + $jsonResponse['hotel_id'] = $propertyId; + + + $jsonResponse['reservation']['code'] = $bookingDetail['booking_code']; + $jsonResponse['reservation']['status'] = $type; + + $jsonResponse['reservation']['time'] = Carbon::createFromTimestamp($param['booking_detail']['created_at'])->toDateTimeString(); + + $jsonResponse['reservation']['checkin_date'] = $bookingDetail['checkin_date']; + $jsonResponse['reservation']['checkout_date'] = $bookingDetail['checkout_date']; + $jsonResponse['reservation']['currency'] = $bookingDetail['currency_code']; + $jsonResponse['reservation']['total'] = $bookingDetail['total']; + + + $jsonResponse['reservation']['contact'] = [ + 'name' => $bookingDetail['booking_contact']['name'], + 'surname' => $bookingDetail['booking_contact']['surname'], + 'email' => $bookingDetail['booking_contact']['email'], + 'phone' => $bookingDetail['booking_contact']['phoneFormatted'], + 'note' => $bookingDetail['booking_contact']['note'], + ]; + + + $jsonResponse['reservation']['rooms'] = []; + + + $roomKey = 0; + $additionalNoteText = []; + foreach ($bookingDetail['booking_room'] as $roomKey => $roomDetail) { + + if ($roomDetail['status'] != 1) { + continue; + } + + $occupancy = occupancyCodeParser($roomDetail['occupancy_code']); + + $diffInDays = Carbon::parse($roomDetail['checkin_date'])->diffInDays(Carbon::parse($roomDetail['checkout_date'])); + $baseRateDaily = moneyDoubleFormatDecimal($roomDetail['total'] / $diffInDays); + + $dailyPrices = []; + $currentDate = $roomDetail['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $dailyPrices[] = [ + 'date' => $currentDate, + 'amount' => $baseRateDaily + ]; + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + + $roomPax = []; + foreach ($roomDetail['room_pax'] as $pax) { + $roomPax[] = [ + 'type' => $pax['type'], + 'name' => $pax['name'], + 'surname' => $pax['surname'], + 'gender' => $pax['gender'], + 'citizen' => $pax['citizen'], + 'birth_date' => $pax['birth_date'], + ]; + } + + $cancellationPolicy = []; + $cancellationPolicyRoom = json_decode($roomDetail['cancellation_policy'], 1); + if ($cancellationPolicyRoom) { + $cancellationPolicy = [ + 'is_nonrefundable' => fillOnUndefined($cancellationPolicyRoom, 'isNonRefundable') == 1 ? true : false, + 'is_freecancellation' => fillOnUndefined($cancellationPolicyRoom, 'isFreeCancellation') == 1 ? true : false, + 'before_arrival_day' => fillOnUndefined($cancellationPolicyRoom, 'isFreeCancellation') == 1 ? fillOnUndefined($cancellationPolicyRoom, 'beforeArrivalDay') : null, + 'type' => fillOnUndefined($cancellationPolicyRoom, 'type') == 1 ? fillOnUndefined($cancellationPolicyRoom, 'type') : null, + 'value' => fillOnUndefined($cancellationPolicyRoom, 'value') == 1 ? fillOnUndefined($cancellationPolicyRoom, 'value') : null, + 'text' => null, + ]; + + $cancellationPolicyTextFormatted = cancellationPolicyTextFormatted( + fillOnUndefined($cancellationPolicyRoom, 'isNonRefundable'), + fillOnUndefined($cancellationPolicyRoom, 'isFreeCancellation'), + fillOnUndefined($cancellationPolicyRoom, 'beforeArrivalDay'), + fillOnUndefined($cancellationPolicyRoom, 'type'), + fillOnUndefined($cancellationPolicyRoom, 'value'), + $bookingDetail['currency_code'], + 'en' + ); + + if($cancellationPolicyTextFormatted) { + $cancellationPolicy['text'] = $cancellationPolicyTextFormatted; + } + } + + $jsonResponse['reservation']['rooms'][$roomKey] = [ + 'id' => $roomDetail['id'], + 'room_id' => $roomDetail['room_id'], + 'room_name' => $roomDetail['room_name'], + 'rate_id' => $roomDetail['room_rate_mapping_id'], + 'rate_name' => $roomDetail['room_rate_name'], + 'cancellation_policy' => $cancellationPolicy, + 'occupancy' => [ + 'adults' => $occupancy['ADT'], + 'children' => $occupancy['CHD'], + 'age' => $occupancy['AGE'], + ], + 'daily' => $dailyPrices, + 'pax' => $roomPax, + + ]; + + if(isset($jsonResponse['reservation']['rooms'][$roomKey]['cancellation_policy']['text'])) { + $additionalNoteText[] = ($roomKey + 1).'. '.$jsonResponse['reservation']['rooms'][$roomKey]['room_name'].' - '.$jsonResponse['reservation']['rooms'][$roomKey]['rate_name']. ' ('.$jsonResponse['reservation']['rooms'][$roomKey]['cancellation_policy']['text'].')'; + } + + } + + $additionalNoteText[] = 'Payment: '.$bookingDetail['booking_payment_type']['code'].' - '.$bookingDetail['booking_payment_type']['name']; + + $jsonResponse['reservation']['contact']['note'].=implode(', ',$additionalNoteText); + + $jsonResponse['reservation']['channel'] = [ + 'id' => $bookingDetail['booking_channel']['id'], + 'name' => $bookingDetail['booking_channel']['id'] == 1 ? 'Extranetwork' : $bookingDetail['booking_channel']['name'], + ]; + + $jsonResponse['reservation']['payment'] = [ + 'code' => $bookingDetail['booking_payment_type']['code'], + 'name' => $bookingDetail['booking_payment_type']['name'], + ]; + + return json_encode($jsonResponse); + + } + + public function requestPostReservationPush($jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + + try { + $this->restClient = new Client(['http_errors' => false]); + $res = $this->restClient->post($this->reservationPushUrl, + [ + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'body' => $jsonPayload, + ] + ); + + $getResponseBody = $res->getBody(); + $getResponseJsonBase = $getResponseBody->getContents(); + + $getResponse = json_decode($getResponseJsonBase, 1); + + if (!$getResponse['status']) { + if($getResponse['message'] == 'HOTEL_TANIMLI_DEGIL') { + + $jsonPayloadArray = json_decode($jsonPayload,1); + $bookingCriteria = [ + 'criteria' => + [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'type', 'condition' => '=', 'value' => array_search($jsonPayloadArray['reservation']['status'], $this->typeMapping)], + ['field' => 'request', 'condition' => 'LIKE', 'value' => '%'.$jsonPayloadArray['reservation']['code'].'%'], + ], + 'firstRow' => true + ]; + + $channelManagerBookingDetail = $this->channelManagerBookingService->select($bookingCriteria); + if($channelManagerBookingDetail['status'] == 'success' && !empty($channelManagerBookingDetail['status'])) { + $channelManagerBookingDetail = $channelManagerBookingDetail['data']; + $this->channelManagerBookingService->update($channelManagerBookingDetail['id'], ['status' => 2]); + } + + } + throw new ApiErrorException($getResponse['message']); + } + + $response = ['status' => true, 'message' => '', 'response' => json_encode($getResponse)]; + + } catch (ApiErrorException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + +} diff --git a/app/Core/Service/ChannelManager/Trivago.php b/app/Core/Service/ChannelManager/Trivago.php new file mode 100644 index 0000000..d8c8c3f --- /dev/null +++ b/app/Core/Service/ChannelManager/Trivago.php @@ -0,0 +1,208 @@ +restClient = $restClient; + $this->mailer = $mailer; + $this->reservationPushUrl = 'https://secde.trivago.com/tracking/booking'; + + + $this->typeMapping = [ + 'Booking' => 'Booking', + 'Modify' => 'Modify', + 'Cancel' => 'Cancel' + ]; + + $this->channelManagerId = 11; + $this->channelManagerName = 'Trivago'; + $this->xTrvAnaKey = '1fe8a527-cbcb-4e01-9a3c-31444d47c422'; + $this->advertiserId = 3698; + + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + $this->bookingService = $bookingService; + + } + + public function requestPostReservationPushParam($propertyId, $bookingId, $type, $param = []) + { + + $type = $this->typeMapping[$type]; + $bookingDetail = $param['booking_detail']; + + $jsonResponse = []; + + $extraParam = json_decode($bookingDetail['extra_param'], 1); + if (!isset($extraParam['trv_reference'])) { + return $jsonResponse; + } + + $locale = 'TR'; + if(isset($extraParam['locale'])) { + $locale = $extraParam['locale']; + } + + if($bookingDetail['total'] > 0) { + $bookingDetail['total'] = $bookingDetail['total']; + } else { + $bookingDetail['total'] = $bookingDetail['booking_payment']['total']; + } + + $jsonResponse['trv_reference'] = $extraParam['trv_reference']; + $jsonResponse['advertiser_id'] = $this->advertiserId; + $jsonResponse['hotel'] = $bookingDetail['property_id']; + $jsonResponse['arrival'] = Carbon::parse($bookingDetail['checkin_date'])->format('Ymd'); + $jsonResponse['departure'] = Carbon::parse($bookingDetail['checkout_date'])->format('Ymd'); + $jsonResponse['date_format'] = 'Ymd'; + $jsonResponse['volume'] = $bookingDetail['total']; + $jsonResponse['currency'] = $bookingDetail['currency_code']; + $jsonResponse['locale'] = mb_strtoupper($locale); + $jsonResponse['booking_id'] = $bookingDetail['booking_code']; + //$jsonResponse['margin'] = $bookingDetail['booking_code']; + $jsonResponse['booking_date'] = Carbon::createFromTimestamp($bookingDetail['created_at'])->subHours(3)->format('YmdHisP'); + $jsonResponse['booking_date_format'] = 'YmdHisP'; + $jsonResponse['number_of_rooms'] = count($bookingDetail['booking_room']); + + if ($type == 'Modify') { + $jsonResponse['update_date'] = Carbon::createFromTimestamp($param['created_at'])->subHours(3)->format('YmdHisP'); + $jsonResponse['update_date_format'] = 'YmdHisP'; + $jsonResponse['update_origin'] = 'user'; + } + + if ($type == 'Cancel') { + $jsonResponse['refund_amount'] = $bookingDetail['total']; + $jsonResponse['cancellation_date'] = Carbon::createFromTimestamp($param['created_at'])->subHours(3)->format('YmdHisP'); + $jsonResponse['cancellation_date_format'] = 'YmdHisP'; + } + + $jsonResponse['params']['type'] = $type; + + return json_encode($jsonResponse); + + } + + public function requestPostReservationPush($jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + + $getResponse = []; + $this->restClient = new Client(['http_errors' => false]); + + try { + + + + $jsonData = json_decode($jsonPayload, 1); + $jsonDataParams = $jsonData['params']; + unset($jsonData['params']); + $jsonPayload = json_encode($jsonData); + + + switch ($jsonDataParams['type']) { + case 'Booking': + + $request = $this->restClient->post($this->reservationPushUrl, + [ + 'headers' => [ + 'X-Trv-Ana-Key' => $this->xTrvAnaKey, + 'Content-Type' => 'application/json' + ], + 'body' => $jsonPayload, + ] + ); + + break; + case 'Modify': + + $request = $this->restClient->put($this->reservationPushUrl, + [ + 'headers' => [ + 'X-Trv-Ana-Key' => $this->xTrvAnaKey, + 'Content-Type' => 'application/json' + ], + 'body' => $jsonPayload, + ] + ); + + break; + case 'Cancel': + + $request = $this->restClient->delete($this->reservationPushUrl, + [ + 'headers' => [ + 'X-Trv-Ana-Key' => $this->xTrvAnaKey, + 'Content-Type' => 'application/json' + ], + 'body' => $jsonPayload, + ] + ); + + break; + case 'default': + throw new ApiErrorException('Reservation push type not found.'); + break; + + } + + $getResponseBody = $request->getBody(); + $getResponseJsonBase = $getResponseBody->getContents(); + + $getResponse = json_decode($getResponseJsonBase, 1); + + Log::debug($jsonPayload); + Log::debug(json_encode($getResponse)); + + //dd($getResponse); + + if ($getResponse['state'] != 'OK') { + throw new ApiErrorException($getResponse['errorMessage']); + } + + $response = ['status' => true, 'message' => '', 'response' => json_encode($getResponse)]; + + } catch (ApiErrorException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + +} diff --git a/app/Core/Service/ChannelManager/_Mirai.php b/app/Core/Service/ChannelManager/_Mirai.php new file mode 100644 index 0000000..69a2252 --- /dev/null +++ b/app/Core/Service/ChannelManager/_Mirai.php @@ -0,0 +1,667 @@ +restClient = $restClient; + $this->mailer = $mailer; + + if (App::environment() == 'production') { + $this->login = 'ExtranetWorktest-xml-100378347'; + $this->password = 'ifXHs3EJc-$>'; + $this->url = 'https://api.mirai.com/XMLIntegrationx/'; + } else { + $this->login = 'ExtranetWorktest-xml-100378347'; + $this->password = 'ifXHs3EJc-$>'; + $this->url = 'https://api.mirai.com/XMLIntegrationx/'; + } + + $this->channelManagerId = 7; + $this->channelManagerName = 'Mirai'; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerMappingService = $channelManagerMappingService; + + } + + public function request($type, $method, $xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + $this->restClient = new Client(['http_errors' => false]); + + $formAuthParam = [ + 'login' => $this->login, + 'password' => $this->password, + ]; + + $parameter = [ + 'headers' => [], + 'query' => $formAuthParam, + ]; + + if (!empty($xmlPayload)) { + $parameter['body'] = $xmlPayload; + } + + $request = $this->restClient->request($type, $this->url . $method, $parameter); + + $getResponseBody = $request->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponseXml = new \SimpleXMLElement($getResponse); + $getResponse = json_decode(json_encode($getResponseXml), 1); + + if (isset($getResponse['error'])) { + $errorMessage = is_array($getResponse['error']['comment']) ? implode(',', $getResponse['error']['comment']) : $getResponse['error']['comment']; + throw new ApiErrorException($errorMessage); + } + + + if ($request->getStatusCode() != 200) { + $firstError = is_array($getResponse['error']['comment']) ? implode(',', $getResponse['error']['comment']) : $getResponse['error']['comment']; + + Log::debug('__REQ__'); + Log::debug($this->url); + Log::debug($type); + Log::debug($method); + Log::debug($xmlPayload); + Log::debug('__RES__'); + Log::debug($getResponse); + Log::debug(PHP_EOL); + + throw new ApiErrorException($firstError); + } + + $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, '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 $response; + + } + + + public function inventoryRoomRateUpdate($method, $xmlPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + + + $request = $this->request('POST', $method, $xmlPayload); + + if (!$request['status']) { + throw new ApiErrorException($request['message']); + } + + $response = [ + 'status' => true, + 'data' => null + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + /** Pattern Functions */ + + public function availabilityUpdateRequestMultiParam($propertyId, $propertyRoomRateMapping, $params) + { + + $idList = []; + + $requestParam['values'] = []; + + ksort($params); + + $dataByRoomDate = []; + $propertyRoomRateMappingCollect = collect($propertyRoomRateMapping); + foreach ($params as $currentDate => $rooms) { + foreach ($rooms as $roomId => $value) { + $uniqueKey = $value['availability']; + + $roomIdManipulate = $roomId; + $roomIdManipulateCheck = $propertyRoomRateMappingCollect->where('channel_manager_room_id', $roomId)->where('property_room_rate_mapping.status', 1)->first(); + if (!empty($roomIdManipulateCheck)) { + $roomIdManipulate = $roomIdManipulateCheck['channel_manager_room_rate_id']; + } + + $dataByRoomDate[$roomIdManipulate][$uniqueKey][$currentDate] = $value; + } + } + + $requestParam = []; + + $valueKey = 0; + foreach ($dataByRoomDate as $roomId => $rooms) { + + foreach ($rooms as $uniqueKey => $uniqueValues) { + + $uniqueValueDate = array_keys($uniqueValues); + + $dateKey = 0; + $dateGroup = []; + for ($i = 0; $i < count($uniqueValueDate); $i++) { + $dateGroup[$dateKey][] = $uniqueValueDate[$i]; + if ($i + 1 < count($uniqueValueDate)) { + if (Carbon::parse($uniqueValueDate[$i])->diffInDays(Carbon::parse($uniqueValueDate[$i + 1])) > 1) { + $dateKey++; + } + } + } + + foreach ($uniqueValues as $uniqueValue) { + $idList = array_merge($idList, $uniqueValue['idList']); + } + + $currentValue = reset($uniqueValues); + + + foreach ($dateGroup as $dates) { + + if (count($dates) > 1) { + + $requestParam[$roomId]['data'][$valueKey] = [ + 'property_id' => $propertyId, + 'room_type_id' => $roomId, + 'date_from' => reset($dates), + 'date_to' => last($dates), + 'availability' => $currentValue['availability'] + ]; + + } else { + + $requestParam[$roomId]['data'][$valueKey] = [ + 'property_id' => $propertyId, + 'room_type_id' => $roomId, + 'date_from' => reset($dates), + 'date_to' => reset($dates), + 'availability' => $currentValue['availability'] + ]; + + } + + $valueKey++; + + } + + + } + } + + + $xmlResponse = new \SimpleXMLElement(''); + + $xmlResponse->addAttribute('hotelId', $propertyId); + + foreach ($requestParam as $roomId => $roomData) { + + $room = $xmlResponse->addChild('room'); + $room->addAttribute('id', $roomId); + + $roomInventory = $room->addChild('inventory'); + + foreach ($roomData['data'] as $roomRate) { + $roomInventoryAvailability = $roomInventory->addChild('availability'); + + $roomInventoryAvailability->addAttribute('from', $roomRate['date_from']); + $roomInventoryAvailability->addAttribute('to', $roomRate['date_to']); + $roomInventoryAvailability->addAttribute('quantity', $roomRate['availability']); + + } + + } + + return [ + 'idList' => $idList, + 'payload' => $xmlResponse->asXML() + ]; + } + + public function roomAvailabilityPush($propertyId, $bulkQueueData) + { + $response = ['status' => false, 'message' => '']; + + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true, + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + $channelManagerPushedIds = []; + $channelManagerNoneMappingIds = []; + $channelManagerPushConfirmCode = []; + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']); + + + $roomAvailabilityQueueForUpdate = []; + $roomAvailabilityQueueForUpdateIdList = []; + $roomAvailabilityQueue = $bulkQueueData; + + + try { + + foreach ($roomAvailabilityQueue as $roomAvailability) { + + + //Dates less than today cannot be updated. + if (Carbon::parse($roomAvailability['date'])->lessThan(Carbon::now()->toDateString())) { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem', + 'logMessage' => '
' . print_r($roomAvailability, true) . '
' + ]; + + //$this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $channelManagerPushedIds[] = $roomAvailability['id']; + + continue; + + } + + $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping.room_id', $roomAvailability['property_room_id'])->first(); + + if (empty($channelManagerRoomRate)) { + //None Mapping! + //$channelManagerPushedIds[] = $roomAvailability['id']; + $channelManagerNoneMappingIds[] = $roomAvailability['id']; + + continue; + } + + + $roomAvailabilityQueueForUpdateIdList[$roomAvailability['date']][] = $roomAvailability['id']; + + + $roomAvailabilityQueueForUpdate[$roomAvailability['date']] + [$channelManagerRoomRate['channel_manager_room_id']] = [ + 'id' => $roomAvailability['id'], + 'idList' => $roomAvailabilityQueueForUpdateIdList[$roomAvailability['date']], + 'date' => $roomAvailability['date'], + 'availability' => $roomAvailability['stop_sell'] == 1 ? 0 : $roomAvailability['availability'] + ]; + + } + + $roomAvailabilityUpdateRequestMultiParam = $this->availabilityUpdateRequestMultiParam($channelManagerPropertyMapping['channel_manager_property_id'], $channelManagerPropertyMapping['channel_manager_room_rate'], $roomAvailabilityQueueForUpdate); + $request = $this->inventoryRoomRateUpdate('webservice_updater.apro', $roomAvailabilityUpdateRequestMultiParam['payload']); + + if ($request['status']) { + + $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomAvailabilityUpdateRequestMultiParam['idList']); + $channelManagerPushConfirmCode[] = $request['data']['confirmId']; + + } else { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateAvailabilityUpdate Error', + 'logMessage' => '
' . print_r(array_merge($request, $roomAvailabilityUpdateRequestMultiParam), true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + throw new ApiErrorException($request['message']); + } + + + } catch (ApiErrorException $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . implode(', ', $e->getMessageArr()); + Log::error($message); + //$response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + //$response['message'] = $e->getMessage(); + } + + + } else { + $channelManagerNoneMappingIds = collect($bulkQueueData)->pluck('id')->toArray(); + } + + + $channelManagerPushedIds = array_unique($channelManagerPushedIds); + asort($channelManagerPushedIds); + $channelManagerPushedIds = array_values($channelManagerPushedIds); + + $response = [ + 'status' => true, + 'data' => [ + 'confirmCode' => $channelManagerPushConfirmCode, + 'channelManagerPushedIds' => $channelManagerPushedIds, + 'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds + ] + ]; + + return $response; + } + + + public function roomRateUpdateRequestMultiParam($propertyId, $params) + { + + $idList = []; + ksort($params); + + $dataByRoomRate = []; + foreach ($params as $currentDate => $rooms) { + foreach ($rooms as $roomId => $rates) { + foreach ($rates as $rateId => $value) { + $uniqueKey = $value['amount'] . '|' . $value['stop_sell'] . '|' . $value['min_stay']; + $dataByRoomRate[$rateId][$uniqueKey][$currentDate] = $value; + } + } + } + + //Log::debug(json_encode($dataByRoomRate)); + + $requestParam = []; + + $valueKey = 0; + foreach ($dataByRoomRate as $rateId => $rates) { + + $valueKey = 0; + foreach ($rates as $uniqueKey => $uniqueValues) { + + + $uniqueValueDate = array_keys($uniqueValues); + + $dateKey = 0; + $dateGroup = []; + for ($i = 0; $i < count($uniqueValueDate); $i++) { + $dateGroup[$dateKey][] = $uniqueValueDate[$i]; + if ($i + 1 < count($uniqueValueDate)) { + if (Carbon::parse($uniqueValueDate[$i])->diffInDays(Carbon::parse($uniqueValueDate[$i + 1])) > 1) { + $dateKey++; + } + } + } + + foreach ($uniqueValues as $uniqueValue) { + $idList = array_merge($idList, $uniqueValue['idList']); + } + + $currentValue = reset($uniqueValues); + + foreach ($dateGroup as $dates) { + + $requestParam[$rateId][$valueKey] = [ + 'property_id' => $propertyId, + 'rate_plan_id' => $rateId, + 'date_from' => reset($dates), + 'date_to' => last($dates), + 'amount' => moneyDoubleFormatDecimal($currentValue['amount']), + 'stop_sell' => fillOnUndefined($currentValue, 'stop_sell', 0), + 'min_stay' => fillOnUndefined($currentValue, 'min_stay', 1) != 0 ? $currentValue['min_stay'] : 1, + 'currency' => $currentValue['currency'], + ]; + + if ($requestParam[$rateId][$valueKey]['amount'] == 0) { + $requestParam[$rateId][$valueKey]['stop_sell'] = 1; + } + + $valueKey++; + + } + + } + } + + + $xmlResponse = new \SimpleXMLElement(''); + + $xmlResponse->addAttribute('hotelId', $propertyId); + + foreach ($requestParam as $roomId => $roomData) { + + $room = $xmlResponse->addChild('room'); + $room->addAttribute('id', $roomId); + + $currencyCode = reset($roomData)['currency']; + + $roomRate = $room->addChild('rate'); + $roomRate->addAttribute('currency', $currencyCode); + + foreach ($roomData as $roomRateData) { + + $roomRatePlaning = $roomRate->addChild('planning'); + + $roomRatePlaning->addAttribute('from', $roomRateData['date_from']); + $roomRatePlaning->addAttribute('to', $roomRateData['date_to']); + $roomRatePlaning->addAttribute('unitPrice', $roomRateData['stop_sell'] == 1 ? 0 : $roomRateData['amount']); + $roomRatePlaning->addAttribute('minimumStay', $roomRateData['min_stay']); + + } + + } + + return [ + 'idList' => $idList, + 'payload' => $xmlResponse->asXML() + ]; + + } + + + public function roomRatePricePush($propertyId, $bulkQueueData) + { + $response = ['status' => false, 'message' => '']; + + try { + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true, + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + $channelManagerPushedIds = []; + $channelManagerNoneMappingIds = []; + $channelManagerPushConfirmCode = null; + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']); + + + $roomRatePriceQueueForUpdate = []; + $roomRatePriceQueueForUpdateIdList = []; + $roomRatePriceQueue = $bulkQueueData; + + + foreach ($roomRatePriceQueue as $roomRatePrice) { + + + //Dates less than today cannot be updated. + if (Carbon::parse($roomRatePrice['date'])->lessThan(Carbon::now()->toDateString())) { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem', + 'logMessage' => '
' . print_r($roomRatePrice, true) . '
' + ]; + + //$this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + $channelManagerPushedIds[] = $roomRatePrice['id']; + + continue; + + } + + $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping_id', $roomRatePrice['room_rate_mapping_id'])->first(); + + if (empty($channelManagerRoomRate)) { + //None Mapping! + //$channelManagerPushedIds[] = $roomAvailability['id']; + $channelManagerNoneMappingIds[] = $roomRatePrice['id']; + + //TODO: Burada oda eşleşmiyorsa süreç devam ediyor ama uyarı maili atılabilir. + + continue; + } + + + $roomRatePriceQueueForUpdateIdList + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']] + [$channelManagerRoomRate['channel_manager_room_rate_id']][] = $roomRatePrice['id']; + + + $roomRatePriceQueueForUpdate + [$channelManagerPropertyMapping['channel_manager_property_id']] + [$roomRatePrice['date']] + [$channelManagerRoomRate['channel_manager_room_id']] + [$channelManagerRoomRate['channel_manager_room_rate_id']] = [ + 'id' => $roomRatePrice['id'], + 'idList' => $roomRatePriceQueueForUpdateIdList[$channelManagerPropertyMapping['channel_manager_property_id']][$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']][$channelManagerRoomRate['channel_manager_room_rate_id']], + 'date' => $roomRatePrice['date'], + 'amount' => $roomRatePrice['amount'], + 'currency' => $roomRatePrice['currency'], + 'stop_sell' => $roomRatePrice['stop_sell'], + 'min_stay' => $roomRatePrice['min_stay'], + ]; + + } + + + foreach ($roomRatePriceQueueForUpdate as $channelManagerPropertyId => $channelManagerProperty) { + + + if (empty($channelManagerProperty)) { + throw new ApiErrorException('$channelManagerProperty is Empty!'); + } + + + $roomRateUpdateRequestMultiParam = $this->roomRateUpdateRequestMultiParam($channelManagerPropertyId, $channelManagerProperty); + $request = $this->inventoryRoomRateUpdate('webservice_updater.apro', $roomRateUpdateRequestMultiParam['payload']); + + if ($request['status']) { + + $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomRateUpdateRequestMultiParam['idList']); + $channelManagerPushConfirmCode = $request['data']['confirmId']; + + } else { + + //$logMessage + $mailParams = [ + 'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateUpdate Error', + 'logMessage' => '
' . print_r($request, true) . '
' + ]; + + $this->mailer->onQueue('logMail', new LogMail($mailParams)); + //$logMessage + + throw new ApiErrorException($request['message']); + } + + } + + } + + $channelManagerPushedIds = array_unique($channelManagerPushedIds); + asort($channelManagerPushedIds); + $channelManagerPushedIds = array_values($channelManagerPushedIds); + + $response = [ + 'status' => true, + 'data' => [ + 'confirmCode' => $channelManagerPushConfirmCode, + 'channelManagerPushedIds' => $channelManagerPushedIds, + 'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds + ] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = $this->channelManagerName . ' - ' . implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $this->channelManagerName . ' - ' . $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + +} diff --git a/app/Core/Service/ChannelManagerBookingService.php b/app/Core/Service/ChannelManagerBookingService.php new file mode 100644 index 0000000..600ad54 --- /dev/null +++ b/app/Core/Service/ChannelManagerBookingService.php @@ -0,0 +1,145 @@ +channelManagerBookingRepository = $channelManagerBookingRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->channelManagerBookingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'booking_id' => fillOnUndefined($params, 'booking_id'), + 'channel_manager_id' => fillOnUndefined($params, 'channel_manager_id'), + 'type' => fillOnUndefined($params, 'type', 'Booking'), + 'status' => fillOnUndefined($params, 'status', 1) + ]; + + $createResult = $this->channelManagerBookingRepository->create($insertData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $createResult["data"], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->channelManagerBookingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function delete($ids = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteResult = $this->channelManagerBookingRepository->destroy($ids); + + if ($deleteResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult['data'] // affected row count + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + +} diff --git a/app/Core/Service/ChannelManagerLogService.php b/app/Core/Service/ChannelManagerLogService.php new file mode 100644 index 0000000..27a00e2 --- /dev/null +++ b/app/Core/Service/ChannelManagerLogService.php @@ -0,0 +1,119 @@ +channelManagerLogRepository = $channelManagerLogRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->channelManagerLogRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_manager_id' => fillOnUndefined($params, 'channel_manager_id',1), + 'type' => fillOnUndefined($params, 'type','C2E'), + 'service' => fillOnUndefined($params, 'service'), + 'request' => fillOnUndefined($params, 'request'), + 'response' => fillOnUndefined($params, 'response'), + 'ip_address' => fillOnUndefined($params, 'ip_address'), + 'status' => fillOnUndefined($params, 'status') + ]; + + $createResult = $this->channelManagerLogRepository->create($insertData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $createResult["data"], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->channelManagerLogRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/ChannelManagerMappingService.php b/app/Core/Service/ChannelManagerMappingService.php new file mode 100644 index 0000000..f963f87 --- /dev/null +++ b/app/Core/Service/ChannelManagerMappingService.php @@ -0,0 +1,134 @@ +channelManagerMappingRepository = $channelManagerMappingRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->channelManagerMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $data, + ]; + + }catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $createResult = $this->channelManagerMappingRepository->create($params); + + if ($createResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + $createData = $createResult["data"]; + $response = [ + 'status' => true, + 'data' => $createData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->channelManagerMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function delete($ids = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteResult = $this->channelManagerMappingRepository->destroy($ids); + + if ($deleteResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult['data'] // affected row count + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + +} diff --git a/app/Core/Service/ChannelManagerPropertyMappingService.php b/app/Core/Service/ChannelManagerPropertyMappingService.php new file mode 100644 index 0000000..2aa4371 --- /dev/null +++ b/app/Core/Service/ChannelManagerPropertyMappingService.php @@ -0,0 +1,134 @@ +channelManagerPropertyMappingRepository = $channelManagerPropertyMappingRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->channelManagerPropertyMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $data, + ]; + + }catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $createResult = $this->channelManagerPropertyMappingRepository->create($params); + + if ($createResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + $createData = $createResult["data"]; + $response = [ + 'status' => true, + 'data' => $createData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->channelManagerPropertyMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function delete($ids = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteResult = $this->channelManagerPropertyMappingRepository->destroy($ids); + + if ($deleteResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult['data'] // affected row count + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + +} diff --git a/app/Core/Service/ChannelManagerPropertyRateMappingService.php b/app/Core/Service/ChannelManagerPropertyRateMappingService.php new file mode 100644 index 0000000..964095a --- /dev/null +++ b/app/Core/Service/ChannelManagerPropertyRateMappingService.php @@ -0,0 +1,134 @@ +channelManagerPropertyRateMappingRepository = $channelManagerPropertyRateMappingRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->channelManagerPropertyRateMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $data, + ]; + + }catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $createResult = $this->channelManagerPropertyRateMappingRepository->create($params); + + if ($createResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + $createData = $createResult["data"]; + $response = [ + 'status' => true, + 'data' => $createData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->channelManagerPropertyRateMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function delete($ids = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteResult = $this->channelManagerPropertyRateMappingRepository->destroy($ids); + + if ($deleteResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult['data'] // affected row count + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + +} diff --git a/app/Core/Service/ChannelManagerService.php b/app/Core/Service/ChannelManagerService.php new file mode 100644 index 0000000..e7f31d2 --- /dev/null +++ b/app/Core/Service/ChannelManagerService.php @@ -0,0 +1,134 @@ +channelManagerRepository = $channelManagerRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->channelManagerRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $data, + ]; + + }catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $createResult = $this->channelManagerRepository->create($params); + + if ($createResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + $createData = $createResult["data"]; + $response = [ + 'status' => true, + 'data' => $createData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->channelManagerRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function delete($ids = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteResult = $this->channelManagerRepository->destroy($ids); + + if ($deleteResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult['data'] // affected row count + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + +} diff --git a/app/Core/Service/CompetitorPriceAnalysisService.php b/app/Core/Service/CompetitorPriceAnalysisService.php new file mode 100644 index 0000000..76cd26f --- /dev/null +++ b/app/Core/Service/CompetitorPriceAnalysisService.php @@ -0,0 +1,130 @@ +propertyCompetitorMappingRepository = $propertyCompetitorMappingRepository; + } + + public function createPropertyCompetitorMapping($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $insertData = [ + "property_id" => fillOnUndefined($param, "property_id"), + "competitor_property_name" => fillOnUndefined($param, "competitor_property_name"), + "competitor_property_source" => fillOnUndefined($param, "competitor_property_source", 'tripadvisor'), + "competitor_property_key" => fillOnUndefined($param, "competitor_property_key"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $insertDataResult = $this->propertyCompetitorMappingRepository->create($insertData); + if ($insertDataResult['status'] != 'success') { + throw new Exception($insertDataResult['message']); + } + + $insertDataResult = $insertDataResult["data"]; + + $response = [ + 'status' => true, + 'data' => $insertDataResult, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + + public function selectPropertyCompetitorMapping($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyCompetitorMappingRepository->findByCriteria($param, $column); + + $propertyList = []; + foreach ($data as $property) { + $propertyList[] = [ + 'id' => $property['id'], + 'property_id' => $property['property_id'], + 'name' => $property['competitor_property_name'], + 'key' => $property['competitor_property_key'], + 'competitor_property_source' => $property['competitor_property_source'], + + ]; + } + + $response = [ + 'status' => true, + 'data' => $propertyList, + ]; + + } 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); + } + + public function deletePropertyCompetitorMapping($deleteThisId) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $deleteThisId = is_array($deleteThisId) ? $deleteThisId : [$deleteThisId]; + $deleteThisArray = $this->propertyCompetitorMappingRepository->destroy($deleteThisId); + + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + +} diff --git a/app/Core/Service/CountryService.php b/app/Core/Service/CountryService.php new file mode 100644 index 0000000..ebf7e53 --- /dev/null +++ b/app/Core/Service/CountryService.php @@ -0,0 +1,43 @@ +countryRepository = $countryRepository; + } + + + public function getCountryList ( array $criteria = [],$columns = ["*"] ) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $data = $this->countryRepository->findByCriteria($criteria, $columns); + $response = [ + 'status' => true, + 'data' => $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); + } + +} diff --git a/app/Core/Service/CurrencyService.php b/app/Core/Service/CurrencyService.php new file mode 100644 index 0000000..84adeb1 --- /dev/null +++ b/app/Core/Service/CurrencyService.php @@ -0,0 +1,145 @@ +currencyRepository = $currencyRepository; + $this->currencyRatesRepository = $currencyRatesRepository; + } + + + public function getCurrencyList(array $params = [], $columns = ["*"]) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => CurrencyService::STATUS], + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + + if(isset($params['justBasicCurrencies']) && $params['justBasicCurrencies']) { + $criteria['criteria'][] = ['field' => 'is_basic', 'condition' => '=', 'value' => 1]; + } + + $data = $this->currencyRepository->findByCriteria($criteria, $columns); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function lastExchangeRate($currency, $exchangeCurrency) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'currency_code', 'condition' => '=', 'value' => $currency], + ['field' => 'exc_currency_code', 'condition' => '=', 'value' => $exchangeCurrency], + ], + "orderBy" => [ + ["field" => "date", "value" => "DESC"] + ] + , + 'firstRow' => true, + ]; + + $data = $this->currencyRatesRepository->findByCriteria($criteria); + + $rate = fillOnUndefined($data, 'rate', 1); + + $response = [ + 'status' => true, + 'data' => $rate, + ]; + + } 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); + } + + public function exchangeCurrencyRates($currency) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'currency_code', 'condition' => '=', 'value' => $currency], + ], + "orderBy" => [ + ["field" => "date", "value" => "DESC"] + ], + 'firstRow' => true, + ]; + + $lastDate = $this->currencyRatesRepository->findByCriteria($criteria); + + + $criteria = [ + 'criteria' => [ + ['field' => 'currency_code', 'condition' => '=', 'value' => $currency], + ['field' => 'date', 'condition' => '=', 'value' => $lastDate['date']], + ], + "orderBy" => [ + ["field" => "currency_code", "value" => "ASC"], + ["field" => "exc_currency_code", "value" => "ASC"] + ] + ]; + + $currencyRates = $this->currencyRatesRepository->findByCriteria($criteria); + + $response = [ + 'status' => true, + 'data' => $currencyRates, + ]; + + } 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); + } +} diff --git a/app/Core/Service/DashboardPlusService.php b/app/Core/Service/DashboardPlusService.php new file mode 100644 index 0000000..1cb3318 --- /dev/null +++ b/app/Core/Service/DashboardPlusService.php @@ -0,0 +1,878 @@ +currencyService = $currencyService; + $this->propertyWebRepository = $propertyWebRepository; + } + + + public function webVisitor(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => true + ]; + + $propertyWeb = $this->propertyWebRepository->findByCriteria($criteria, $columns); + if (empty($propertyWeb)) { + throw new ApiErrorException('No record found.'); + } + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->timestamp; + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->timestamp; + $propertyWebLogRaw = PropertyWebLog::whereBetween('created_at', [$startDate, $finishDate]) + ->where('web_id', $propertyWeb) + ->groupBy('country_code') + ->select('country_code', DB::raw('count(id) count')) + ->get()->toArray(); + + $propertyWebLogCountryIds = pickItemFromArray('country_code', $propertyWebLogRaw); + $propertyWebLogCountry = Country::whereIn('country_code', $propertyWebLogCountryIds)->get()->toArray(); + $propertyWebLogCountry = collect($propertyWebLogCountry); + + $propertyWebLog = []; + $propertyWebLogTotal = 0; + foreach ($propertyWebLogRaw as $propertyWebLogKey => $propertyWebLogValue) { + + $countryCodeDetail = $propertyWebLogCountry->where('country_code', mb_strtoupper($propertyWebLogValue['country_code']))->first(); + if (empty($countryCodeDetail)) { + continue; + } + + $propertyWebLog[] = [ + 'country' => $countryCodeDetail['name'], + 'language_key' => $countryCodeDetail['language_key'], + 'country_code' => $propertyWebLogValue['country_code'], + 'count' => $propertyWebLogValue['count'], + 'formatted' => [ + 'value' => $propertyWebLogValue['count'], + 'country' => $propertyWebLogValue['country_code'], + 'country_name' => $countryCodeDetail['name'], + ] + ]; + + } + + $propertyWebLog = collect($propertyWebLog)->sortByDesc('count')->toArray(); + $propertyWebLog = array_values($propertyWebLog); + + $propertyWebLogTotal = collect($propertyWebLog)->sum('count'); + + $response = [ + 'status' => true, + 'data' => [ + 'total' => $propertyWebLogTotal, + 'country' => $propertyWebLog + ], + ]; + + } 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 $response; + } + + public function guestDemographic(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $propertyChannel = PropertyChannel::whereIn('channel_category_id', [2, 3])->get(['id'])->sortBy('id')->toArray(); + $propertyChannelIds = pickItemFromArray('id', $propertyChannel); + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->timestamp; + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->timestamp; + $propertyBooking = Booking::whereBetween('created_at', [$startDate, $finishDate]) + ->where('status', 1) + ->whereIn('channel_id', $propertyChannelIds) + ->where('property_id', $params['property_id']) + ->get(['id'])->sortBy('id')->toArray(); + $propertyBookingIds = pickItemFromArray('id', $propertyBooking); + + + $bookingRoomPax = BookingRoomPax:: + whereIn('booking_id', $propertyBookingIds) + ->get()->toArray(); + $bookingRoomPax = collect($bookingRoomPax); + + $guestDemographic = []; + + + $totalPax = $bookingRoomPax->count(); + + $guestDemographic['all'] = $totalPax; + + //Gender + $guestDemographic['gender'] = []; + if (!empty($totalPax)) { + $guestDemographic['gender'] = [ + 'male' => [ + 'title' => 'Male', + 'count' => $bookingRoomPax->where('gender', 'M')->count(), + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPax->where('gender', 'M')->count() / $totalPax * 100), + ], + 'female' => [ + 'title' => 'Female', + 'count' => $bookingRoomPax->where('gender', 'F')->count(), + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPax->where('gender', 'F')->count() / $totalPax * 100), + ] + ]; + } + + + //Type + $guestDemographic['type'] = []; + if (!empty($totalPax)) { + $guestDemographic['type'] = [ + 'adult' => [ + 'title' => 'Adult', + 'count' => $bookingRoomPax->where('type', 'ADT')->count(), + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPax->where('type', 'ADT')->count() / $totalPax * 100), + ], + 'child' => [ + 'title' => 'Child', + 'count' => $bookingRoomPax->where('type', 'CHD')->count(), + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPax->where('type', 'CHD')->count() / $totalPax * 100), + ] + ]; + } + + + //Citizen + $guestDemographic['citizen'] = []; + if (!empty($totalPax)) { + $bookingRoomPaxGroupedCitizen = $bookingRoomPax->groupBy('citizen')->map->count()->toArray(); + arsort($bookingRoomPaxGroupedCitizen); + + $paxGroupedCitizenCountry = Country::whereIn('country_code', array_keys($bookingRoomPaxGroupedCitizen))->get()->toArray(); + $paxGroupedCitizenCountry = collect($paxGroupedCitizenCountry); + + foreach ($bookingRoomPaxGroupedCitizen as $bookingRoomPaxGroupedCitizenCode => $bookingRoomPaxGroupedCitizenCount) { + + $countryCodeDetail = $paxGroupedCitizenCountry->where('country_code', mb_strtoupper($bookingRoomPaxGroupedCitizenCode))->first(); + + $guestDemographic['citizen'][mb_strtolower($bookingRoomPaxGroupedCitizenCode)] = [ + 'country' => fillOnUndefined($countryCodeDetail, 'name', $bookingRoomPaxGroupedCitizenCode), + 'language_key' => fillOnUndefined($countryCodeDetail, 'language_key', $bookingRoomPaxGroupedCitizenCode), + 'country_code' => mb_strtolower($bookingRoomPaxGroupedCitizenCode), + 'count' => $bookingRoomPaxGroupedCitizenCount, + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPaxGroupedCitizenCount / $totalPax * 100), + ]; + } + } + + //Age + $guestDemographic['age'] = []; + if (!empty($totalPax)) { + $bookingRoomPaxAges = $bookingRoomPax->map(function ($value, $key) { + $value['age'] = Carbon::parse($value['birth_date'])->diffInYears(Carbon::createFromTimestamp($value['created_at'])); + return $value; + }); + + + $guestDemographic['age'] = [ + '0-12' => [ + 'title' => '0-12 Age', + 'count' => $bookingRoomPaxAges->where('age', '>=', 0)->where('age', '<', 12)->count(), + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPaxAges->where('age', '>=', 0)->where('age', '<', 12)->count() / $totalPax * 100), + ], + '12-18' => [ + 'title' => '12-18 Age', + 'count' => $bookingRoomPaxAges->where('age', '>=', 12)->where('age', '<', 18)->count(), + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPaxAges->where('age', '>=', 12)->where('age', '<', 18)->count() / $totalPax * 100), + ], + '18-36' => [ + 'title' => '18-36 Age', + 'count' => $bookingRoomPaxAges->where('age', '>=', 18)->where('age', '<', 36)->count(), + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPaxAges->where('age', '>=', 18)->where('age', '<', 36)->count() / $totalPax * 100), + ], + '36-60' => [ + 'title' => '36-60 Age', + 'count' => $bookingRoomPaxAges->where('age', '>=', 36)->where('age', '<', 60)->count(), + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPaxAges->where('age', '>=', 36)->where('age', '<', 60)->count() / $totalPax * 100), + ], + '60+' => [ + 'title' => '60+ Age', + 'count' => $bookingRoomPaxAges->where('age', '>=', 60)->count(), + 'percentage' => moneyDoubleFormatDecimal($bookingRoomPaxAges->where('age', '>=', 60)->count() / $totalPax * 100), + ], + ]; + } + + $response = [ + 'status' => true, + 'data' => $guestDemographic + ]; + + } 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 $response; + } + + public function topChannel(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $defaultCurrency = 'TRY'; + $bookingEngineBaseCurrency = PropertyChannelMapping::where('status', 1) + ->where('channel_id', 1) + ->where('property_id', $params['property_id']) + ->first()->toArray(); + + if (!empty($bookingEngineBaseCurrency)) { + $defaultCurrency = $bookingEngineBaseCurrency['currency_code']; + } + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->timestamp; + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->timestamp; + /*$propertyBooking = Booking::whereBetween('created_at', [$startDate, $finishDate]) + ->where('property_id', $params['property_id']) + ->get(['id', 'property_id', 'channel_id', 'total', 'currency_code', 'status']) + ->sortByDesc('id')->toArray();*/ + + $propertyBooking = Booking::whereBetween('created_at', [$startDate, $finishDate]) + ->where('property_id', $params['property_id']) + ->where('status', 1) + ->groupBy('channel_id') + ->groupBy('currency_code') + ->select('channel_id', 'currency_code', DB::raw('count(id) count, sum(total) total')) + ->get()->toArray(); + + //Channel Id List + $channelIds = pickItemFromArray('channel_id', $propertyBooking); + $channelIds = !empty($channelIds) ? array_values(array_unique($channelIds)) : []; + $propertyChannel = PropertyChannel::whereIn('id', $channelIds)->get(['id', 'name'])->toArray(); + $propertyChannelCollect = collect($propertyChannel); + + //Currency Code List + $currencyCodes = pickItemFromArray('currency_code', $propertyBooking); + $currencyCodes = !empty($currencyCodes) ? array_values(array_unique($currencyCodes)) : []; + + + $topChannel = []; + foreach ($propertyBooking as $booking) { + + if ($booking['total'] <= 0) { + continue; + } + + $commonCurrencyCodeRate = $this->currencyService->lastExchangeRate($booking['currency_code'], $defaultCurrency); + + $channelName = $propertyChannelCollect->where('id', $booking['channel_id'])->first(); + if (!empty($channelName)) { + $channelName = $channelName['name']; + } + + $topChannel[$booking['channel_id']] = [ + 'name' => $channelName, + 'count' => $booking['count'], + 'total' => $booking['total'], + 'currency_code' => $booking['currency_code'], + 'commonCurrencyTotal' => ($booking['total'] * $commonCurrencyCodeRate['data']), + 'commonCurrencyCode' => $defaultCurrency + ]; + + } + + $totalChannelOverall = collect($topChannel)->sum('commonCurrencyTotal'); + foreach ($topChannel as $channelId => $channel) { + $topChannel[$channelId]['total'] = moneyDoubleFormatDecimal($channel['total']); + $topChannel[$channelId]['commonCurrencyTotal'] = moneyDoubleFormatDecimal($channel['commonCurrencyTotal']); + $topChannel[$channelId]['percentage'] = moneyDoubleFormatDecimal($channel['commonCurrencyTotal'] / $totalChannelOverall * 100); + } + + + $topChannel = collect($topChannel)->sortByDesc('commonCurrencyTotal')->toArray(); + $topChannel = array_values($topChannel); + + $response = [ + 'status' => true, + 'data' => $topChannel + ]; + + } 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 $response; + } + + public function todayCheckin(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $todayCheckin = 0; + $today = Carbon::now()->startOfDay()->toDateString(); + $todayCheckin = Booking::where('checkin_date', $today)->where('property_id', $params['property_id'])->count(); + + $response = [ + 'status' => true, + 'data' => $todayCheckin + ]; + + } 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 $response; + } + + public function todayCheckout(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $todayCheckOut = 0; + $today = Carbon::now()->startOfDay()->toDateString(); + $todayCheckOut = Booking::where('checkout_date', $today)->where('property_id', $params['property_id'])->count(); + + $response = [ + 'status' => true, + 'data' => $todayCheckOut + ]; + + } 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 $response; + } + + public function lengthOfStay(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->toDateTimeString(); + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->toDateTimeString(); + + $lengthOfStay = 0; + $lengthOfStay = vwBookingSummaryAll::whereBetween('time', [$startDate, $finishDate]) + ->where('property_id', $params['property_id']) + ->pluck('length_of_stay')->avg(); + + $lengthOfStay = moneyDoubleFormatDecimal($lengthOfStay); + + $response = [ + 'status' => true, + 'data' => $lengthOfStay + ]; + + } 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 $response; + } + + public function lengthOfBooking(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->toDateTimeString(); + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->toDateTimeString(); + + $lengthOfBooking = 0; + $lengthOfBooking = vwBookingSummaryAll::whereBetween('time', [$startDate, $finishDate]) + ->where('property_id', $params['property_id']) + ->pluck('length_of_booking')->avg(); + + $lengthOfBooking = moneyDoubleFormatDecimal($lengthOfBooking); + + $response = [ + 'status' => true, + 'data' => $lengthOfBooking + ]; + + } 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 $response; + } + + public function totalBooking(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $defaultCurrency = 'TRY'; + $bookingEngineBaseCurrency = PropertyChannelMapping::where('status', 1) + ->where('channel_id', 1) + ->where('property_id', $params['property_id']) + ->first()->toArray(); + + if (!empty($bookingEngineBaseCurrency)) { + $defaultCurrency = $bookingEngineBaseCurrency['currency_code']; + } + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->toDateTimeString(); + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->toDateTimeString(); + + $propertyBooking = 0; + $propertyBooking = vwBookingSummaryAll::whereBetween('time', [$startDate, $finishDate]) + ->where('property_id', $params['property_id']) + ->groupBy('currency_code') + ->select('currency_code', DB::raw('count(id) count, sum(total) total')) + ->get()->toArray(); + + + $totalBooking = [ + 'totalCount' => 0, + 'totalRevenue' => 0, + 'currencyCode' => $defaultCurrency + ]; + + foreach ($propertyBooking as $booking) { + + if ($booking['total'] <= 0) { + continue; + } + + $commonCurrencyCodeRate = $this->currencyService->lastExchangeRate($booking['currency_code'], $defaultCurrency); + $totalBooking['totalRevenue'] += ($booking['total'] * $commonCurrencyCodeRate['data']); + $totalBooking['totalCount'] += $booking['count']; + + } + + $totalBooking['totalRevenue'] = moneyDoubleFormatDecimal($totalBooking['totalRevenue']); + + + $response = [ + 'status' => true, + 'data' => $totalBooking + ]; + + } 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 $response; + } + + public function averageDailyRate(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $defaultCurrency = 'TRY'; + $bookingEngineBaseCurrency = PropertyChannelMapping::where('status', 1) + ->where('channel_id', 1) + ->where('property_id', $params['property_id']) + ->first()->toArray(); + + if (!empty($bookingEngineBaseCurrency)) { + $defaultCurrency = $bookingEngineBaseCurrency['currency_code']; + } + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->toDateTimeString(); + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->toDateTimeString(); + + $averageDailyRate = [ + 'averageDailyRate' => 0, + 'currencyCode' => $defaultCurrency + ]; + + $propertyBooking = vwBookingSummaryAll::whereBetween('time', [$startDate, $finishDate]) + ->where('property_id', $params['property_id']) + ->select('id') + ->get()->toArray(); + $propertyBookingIds = pickItemFromArray('id', $propertyBooking); + + $bookingRoom = BookingRoom::whereIn('booking_id', $propertyBookingIds) + ->select('id', 'booking_id', 'checkin_date', 'checkout_date', 'total', 'currency_code') + ->get()->toArray(); + + $exchangeCurrencyList = collect($bookingRoom)->unique('currency_code')->pluck('currency_code')->toArray(); + + foreach ($exchangeCurrencyList as $currencyCode) { + if ($currencyCode == $defaultCurrency) { + $commonCurrencyCodeRate[$currencyCode] = 1; + } else { + $lastExchangeRate = $this->currencyService->lastExchangeRate($currencyCode, $defaultCurrency); + $commonCurrencyCodeRate[$currencyCode] = $lastExchangeRate['data']; + } + } + + $averageDailyRateList = []; + foreach ($bookingRoom as $perRoom) { + $diffInDays = Carbon::parse($perRoom['checkout_date'])->diffInDays(Carbon::parse($perRoom['checkin_date'])); + $totalExchangeAmount = ($perRoom['total'] * $commonCurrencyCodeRate[$perRoom['currency_code']]); + $averageDailyRateList[] = $totalExchangeAmount / $diffInDays; + } + + + $averageDailyRate['averageDailyRate'] = moneyDoubleFormatDecimal(collect($averageDailyRateList)->average()); + + $response = [ + 'status' => true, + 'data' => $averageDailyRate + ]; + + } 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 $response; + } + + public function totalPax(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $defaultCurrency = 'TRY'; + $bookingEngineBaseCurrency = PropertyChannelMapping::where('status', 1) + ->where('channel_id', 1) + ->where('property_id', $params['property_id']) + ->first()->toArray(); + + if (!empty($bookingEngineBaseCurrency)) { + $defaultCurrency = $bookingEngineBaseCurrency['currency_code']; + } + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->toDateTimeString(); + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->toDateTimeString(); + + $propertyBooking = 0; + $propertyBooking = vwBookingSummaryAll::whereBetween('time', [$startDate, $finishDate]) + ->where('property_id', $params['property_id']) + ->groupBy('currency_code') + ->select('currency_code', DB::raw('count(id) count, sum(total) total')) + ->get()->toArray(); + + + $totalPax = [ + 'totalPaxCount' => 0, + 'totalPaxRevenue' => 0, + 'averageRevenuePerPax' => 0, + 'currencyCode' => $defaultCurrency + ]; + + foreach ($propertyBooking as $booking) { + + if ($booking['total'] <= 0) { + continue; + } + + $commonCurrencyCodeRate = $this->currencyService->lastExchangeRate($booking['currency_code'], $defaultCurrency); + $totalPax['totalPaxRevenue'] += ($booking['total'] * $commonCurrencyCodeRate['data']); + + } + + $totalPax['totalPaxRevenue'] = moneyDoubleFormatDecimal($totalPax['totalPaxRevenue']); + + $bookingIds = vwBookingSummaryAll::whereBetween('time', [$startDate, $finishDate]) + ->where('property_id', $params['property_id']) + ->pluck('id')->toArray(); + + $bookingRoom = BookingRoom::whereIn('booking_id', $bookingIds) + ->groupBy('occupancy_code') + ->select('occupancy_code', DB::raw('count(id) count')) + ->get()->toArray(); + + foreach ($bookingRoom as $bookingRoomPax) { + + $adultCount = substr_count($bookingRoomPax['occupancy_code'], 'A'); + $childCount = substr_count($bookingRoomPax['occupancy_code'], 'C'); + + $totalPax['totalPaxCount'] += (($adultCount + $childCount) * $bookingRoomPax['count']); + + } + + if($totalPax['totalPaxCount'] > 0) { + $totalPax['averageRevenuePerPax'] = moneyDoubleFormatDecimal($totalPax['totalPaxRevenue'] / $totalPax['totalPaxCount']); + } + + + $response = [ + 'status' => true, + 'data' => $totalPax + ]; + + } 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 $response; + } + + public function channelForecast(array $params = [], $columns = ["*"]) + { + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->toDateString(); + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->toDateString(); + + //Burada mutlaka 2 kontrol eklenecke, + // 1. bitiş, başlangıçtan büyük olamazi + // 2. max 6 aylık yani 180 günlük veri çekilebilir + + + $relatingDates = []; + $dateDiff = Carbon::parse($startDate)->diffInDays(Carbon::parse($finishDate)); + for ($i = 0; $i < ($dateDiff + 1); $i++) { + $currentDate = Carbon::parse($startDate)->addDays($i)->toDateString(); + $relatingDates[] = $currentDate; + } + + $period = []; + $periodModel = 'daily'; + if (Carbon::parse($startDate)->month != Carbon::parse($finishDate)->month) { + $periodModel = 'monthly'; + } elseif (Carbon::parse($startDate)->diffInDays(Carbon::parse($finishDate)) > 30) { + $periodModel = 'monthly'; + } + + $propertyBooking = Booking::where('status', 1) + ->where('checkin_date', '<=', $finishDate) + ->where('checkout_date', '>=', $startDate) + ->where('property_id', $params['property_id']) + //->where('id',11825) + ->with('bookingRoomSummary') + ->select('id', 'channel_id', 'checkin_date', 'checkout_date', 'total', 'status') + ->get()->toArray(); + + //dd($propertyBooking); + + //Channel Id List + $channelIds = pickItemFromArray('channel_id', $propertyBooking); + $channelIds = !empty($channelIds) ? array_values(array_unique($channelIds)) : []; + $propertyChannel = PropertyChannel::whereIn('id', $channelIds)->get(['id', 'name'])->toArray(); + $propertyChannelCollect = collect($propertyChannel); + + $bookingChannelForecast = []; + $bookingChannelForecastTotal = []; + foreach ($propertyBooking as $booking) { + + if ($booking['total'] <= 0) { + continue; + } + + $channelName = $propertyChannelCollect->where('id', $booking['channel_id'])->first(); + if (!empty($channelName)) { + $channelName = $channelName['name']; + } + + foreach ($booking['booking_room_summary'] as $bookingRoom) { + + $checkInDate = $bookingRoom['checkin_date']; + $checkOutDate = $bookingRoom['checkout_date']; + $dateDiff = Carbon::parse($checkOutDate)->diffInDays(Carbon::parse($checkInDate)); + + for ($i = 0; $i < $dateDiff; $i++) { + $date = Carbon::parse($checkInDate)->addDays($i)->toDateString(); + + if(!in_array($date,$relatingDates)) { + continue; + } + + + if (!isset($bookingChannelForecast[$booking['channel_id']]['total'])) { + $bookingChannelForecast[$booking['channel_id']]['id'] = $booking['channel_id']; + $bookingChannelForecast[$booking['channel_id']]['title'] = $channelName; + $bookingChannelForecast[$booking['channel_id']]['total'] = 0; + $bookingChannelForecast[$booking['channel_id']]['percentage'] = 0; + } + + if (!isset($bookingChannelForecast[$booking['channel_id']]['data'][$date])) { + $bookingChannelForecast[$booking['channel_id']]['data'][$date] = 0; + } + + if (!isset($bookingChannelForecastTotal[$date])) { + $bookingChannelForecastTotal[$date] = 0; + } + + + $bookingChannelForecast[$booking['channel_id']]['data'][$date]++; + $bookingChannelForecast[$booking['channel_id']]['total']++; + $bookingChannelForecastTotal[$date]++; + + } + } + + } + + + $totalRoom = collect($bookingChannelForecast)->sum('total'); + + $dateDiff = Carbon::parse($startDate)->diffInDays(Carbon::parse($finishDate)); + for ($i = 0; $i < ($dateDiff + 1); $i++) { + $currentDate = Carbon::parse($startDate)->addDays($i)->toDateString(); + foreach ($bookingChannelForecast as $channelId => $channel) { + foreach ($channel['data'] as $date => $count) { + + if (!isset($bookingChannelForecast[$channelId]['data'][$currentDate])) { + $bookingChannelForecast[$channelId]['data'][$currentDate] = 0; + } + + if (!isset($bookingChannelForecastTotal[$currentDate])) { + $bookingChannelForecastTotal[$currentDate] = 0; + } + + + $period[$currentDate] = $currentDate; + $bookingChannelForecast[$channelId]['percentage'] = moneyDoubleFormatDecimal($bookingChannelForecast[$channelId]['total'] / $totalRoom * 100); + + ksort($bookingChannelForecast[$channelId]['data']); + ksort($period); + + } + } + } + + + if ($periodModel == 'monthly') { + + $period = []; + foreach ($bookingChannelForecast as $channelId => $channel) { + foreach ($channel['data'] as $date => $count) { + + $currentMonth = Carbon::parse($date)->format('Y-m'); + $period[$currentMonth] = $currentMonth; + + if (!isset($bookingChannelForecast[$channelId]['data'][$currentMonth])) { + $bookingChannelForecast[$channelId]['data'][$currentMonth] = 0; + } + + + if (!isset($bookingChannelForecastTotal[$currentMonth])) { + $bookingChannelForecastTotal[$currentMonth] = 0; + } + + + $bookingChannelForecast[$channelId]['data'][$currentMonth] += $bookingChannelForecast[$channelId]['data'][$date]; + $bookingChannelForecastTotal[$currentMonth] += $bookingChannelForecast[$channelId]['data'][$date]; + + unset($bookingChannelForecast[$channelId]['data'][$date]); + unset($bookingChannelForecastTotal[$date]); + ksort($bookingChannelForecast[$channelId]['data']); + ksort($bookingChannelForecast); + ksort($period); + } + } + + } + + $bookingChannelForecast = collect($bookingChannelForecast)->sortBy('title')->toArray(); + $bookingChannelForecast = array_values($bookingChannelForecast); + + $propertyChannel = collect($propertyChannel)->sortBy('name')->pluck('name')->toArray(); + + $period = is_array($period) ? array_values($period) : []; + + ksort($bookingChannelForecastTotal); + + $response = [ + 'status' => true, + 'data' => [ + 'title' => 'Forecast - '.$startDate . ' / ' . $finishDate . ' (' . ucfirst($periodModel) . ')', + 'period' => $period, + 'channel' => $propertyChannel, + 'forecast' => $bookingChannelForecast, + 'forecastTotal' => $bookingChannelForecastTotal + ] + ]; + + } 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 $response; + } + + +} diff --git a/app/Core/Service/DestinationService.php b/app/Core/Service/DestinationService.php new file mode 100644 index 0000000..72d1b10 --- /dev/null +++ b/app/Core/Service/DestinationService.php @@ -0,0 +1,107 @@ +VWDestinationRepository = $VWDestinationRepository; + $this->destinationSearchValidator = $destinationSearchValidator ; + + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->VWDestinationRepository->findByCriteria($param, $column); + + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function searchDestination($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $validationResult = $this->destinationSearchValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $searchCriteria = [ + 'criteria' => [ + ['field' => 'long_name', 'condition' => 'like', 'value' => '%'.$params['search_term'].'%'], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ["field" => "priority", "value" => "DESC"], + ["field" => "level", "value" => "ASC"], + ["field" => "name", "value" => "ASC"] + ], + 'take' => 5 + ]; + + $searchResult = $this->select($searchCriteria, ['id', 'long_name']); + + if(!$searchResult['data']){ + throw new ApiErrorException(lang('Search data not found')); + } + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['destination' =>$searchResult['data'] ] ]; + } catch(ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + + + +} \ No newline at end of file diff --git a/app/Core/Service/EnwContactFormService.php b/app/Core/Service/EnwContactFormService.php new file mode 100644 index 0000000..9c5d61c --- /dev/null +++ b/app/Core/Service/EnwContactFormService.php @@ -0,0 +1,85 @@ +mailer = $mailer; + $this->enwContactFormCreateValidator = $enwContactFormCreateValidator; + } + + public function sendEmail($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $validationResult = $this->enwContactFormCreateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $bcc[] = "bd@extranetwork.com"; + $mailData = [ + 'to' => [ + 'name' => Config::get('app.name'), + 'email' => Config::get('app.enwContactFormMailTo') + ], + 'bcc' => $bcc + ]; + + $mailViewParams = $params ; + $mailViewParams['logo'] = 'https://www.extranetwork.com/assets/img/logo/logo.png'; + + $mailParams = [ + 'mailData' => $mailData, + 'mailViewParams' => $mailViewParams + ]; + + + $this->mailer->onQueue( + 'enwContactFormMail', + new EnwContactFormMail($mailParams) + ); + + $response = [ + 'status' => true, + 'data' => null, + 'message' => 'myweb-contact_form-success_message', + ]; + + } 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); + } + + +} diff --git a/app/Core/Service/FindCountryCodeService.php b/app/Core/Service/FindCountryCodeService.php new file mode 100644 index 0000000..59ec097 --- /dev/null +++ b/app/Core/Service/FindCountryCodeService.php @@ -0,0 +1,122 @@ +restClient = $restClient; + $this->ipNationRepository = $ipNationRepository; + } + + public function findCountryWithIpAddress($ip) + { + + $response = ['status' => false, 'message' => '']; + + $ipPoolCache = []; + $ipPoolCacheKey = 'ipPoolCacheKey'; + + if(Cache::has($ipPoolCacheKey)) { + $ipPoolCache = Cache::get($ipPoolCacheKey); + } + + if(isset($ipPoolCache[$ip])) { + $response = [ + 'status' => true, + 'data' => [ + 'code' => mb_strtolower($ipPoolCache[$ip]), + 'source' => 'cache' + ] + ]; + + return output($response); + } + + try { + + $request = $this->restClient->get('http://ip-api.com/json/' . $ip, ['timeout' => 2]); + + $getResponseBody = $request->getBody(); + $getResponse = $getResponseBody->getContents(); + + if (empty($getResponse)) { + throw new ApiErrorException('Empty JSON response'); + } + + $getResponse = json_decode($getResponse, 1); + + if ($getResponse['status'] != 'success') { + throw new ApiErrorException('Error'); + } + + if (isset($getResponse['message'])) { + throw new ApiErrorException($getResponse['message']); + } + + + if(in_array($getResponse['countryCode'],['GB'])) { + $getResponse['countryCode'] = 'UK'; + } + + $response = [ + 'status' => true, + 'data' => [ + 'code' => mb_strtolower($getResponse['countryCode']), + 'source' => 'ip-api.com' + ] + ]; + + } catch (ApiErrorException $e) { + + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + + $ip = ip2long($ip); + + $requestData = [ + 'criteria' => [ + ['field' => 'ip', 'condition' => '<', 'value' => $ip] + ], + 'orderBy' => [["field" => "ip", "value" => "DESC"]], + 'with' => ['country'], + 'firstRow' => true + ]; + + $ipResult = $this->ipNationRepository->findByCriteria($requestData); + + $response = [ + 'status' => true, + 'data' => !empty($ipResult['country']) ? $ipResult['country'] : [] + ]; + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if($response['status'] && isset($response['data']['code']) && !empty($response['data']['code'])) { + $ipPoolCache[$ip] = $response['data']['code']; + Cache::put($ipPoolCacheKey, $ipPoolCache, 24 * 60 * 60);//10 Min, 60 * 10 = 600 TTL + } + + return output($response); + } + +} diff --git a/app/Core/Service/GeneralTimezoneService.php b/app/Core/Service/GeneralTimezoneService.php new file mode 100644 index 0000000..f6d4c10 --- /dev/null +++ b/app/Core/Service/GeneralTimezoneService.php @@ -0,0 +1,109 @@ +generalTimezoneRepository = $generalTimezoneRepository; + + } + + + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->generalTimezoneRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getAllaGeneralTimezone($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + $generalTimezoneRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + + ]; + + $generalTimezoneData = $this->select($generalTimezoneRequest); + if($generalTimezoneData['status'] != "success"){ + throw new Exception('api-unknown_error'); + } + + $generalTimezones = collect($generalTimezoneData['data'])->map(function ($timezone){ + return [ + "id" => $timezone["id"], + "location" => $timezone["location"], + "description" => $timezone["description"], + "action_type" => $timezone["action_type"], + "hour" => $timezone["hour"], + "minute" => $timezone["minute"], + + ]; + + }); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $generalTimezones]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + + + +} diff --git a/app/Core/Service/Google/GoogleVisionLabelService.php b/app/Core/Service/Google/GoogleVisionLabelService.php new file mode 100644 index 0000000..dd0100a --- /dev/null +++ b/app/Core/Service/Google/GoogleVisionLabelService.php @@ -0,0 +1,72 @@ +googleVisionAuthentication = base_path().'/resources/data/google-vision-authentication.json'; + putenv('GOOGLE_APPLICATION_CREDENTIALS='.$this->googleVisionAuthentication ); + } + + public function getImageGoogleVisionLabels($params) + { + $photoLabelList = []; + try + { + $imagePath = $params['image_paths']; + $image = file_get_contents($imagePath); + $imageAnnotator = new ImageAnnotatorClient(); + $responseImageAnnotator = $imageAnnotator->labelDetection($image); + $getLabelAnnotations = $responseImageAnnotator->getLabelAnnotations(); + $getLabelAnnotations = $getLabelAnnotations ? $getLabelAnnotations : null; + + if( empty($getLabelAnnotations) ) + { + throw new ApiErrorException(lang('Not tags found!')); + } + + if ($getLabelAnnotations) { + $i = 1; + foreach ($getLabelAnnotations as $labelAnnotation) + { + $photoLabelList[] = + [ + 'description' => $labelAnnotation->getDescription(), + 'score' => $labelAnnotation->getScore(), + 'topicality' => $labelAnnotation->getTopicality(), + ]; + if($i == 3){ + break; + } + $i++; + } + + Log:info($photoLabelList); + } + + + + $imageAnnotator->close(); + + }catch (Exception $e) + { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + } + + return $photoLabelList; + } + + +} diff --git a/app/Core/Service/JobsService.php b/app/Core/Service/JobsService.php new file mode 100644 index 0000000..acbfdef --- /dev/null +++ b/app/Core/Service/JobsService.php @@ -0,0 +1,20 @@ +jobsRepository = $jobsRepository; + } + + public function getJobsList ( array $params,$columns = ["*"] ) + { + return $this->jobsRepository->findByCriteria($params,$columns); + } + +} \ No newline at end of file diff --git a/app/Core/Service/JwtService.php b/app/Core/Service/JwtService.php new file mode 100644 index 0000000..f67aafa --- /dev/null +++ b/app/Core/Service/JwtService.php @@ -0,0 +1,79 @@ +jwt = $jwt ; + } + + + protected function jwt($params) + { + $payload = [ + 'iss' => $params['iss'], // Issuer of the token + 'sub' => $params['sub'], // Subject of the token + 'user_id' => $params['user_id'], + 'remember_me' => $params['remember_me'], + 'iat' => $params['iat'], // Time when JWT was issued. + 'exp' => $params['exp'] // Expiration time 5 Hours + ]; + + // As you can see we are passing `JWT_SECRET` as the second parameter that will + // be used to decode the token in the future. + return JWT::encode($payload, Config::get('app.jwt.secret')); + } + + public function jwtCreate($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $dayCounter = isset( $params['day_counter'] ) ? $params['day_counter'] : 5 ; + $rememberMe = isset( $params['remember_me'] ) && $params['remember_me'] === true ? true : false ; + $jwtParams = [ + 'iss' => $this->jwtISS, // Issuer of the token + 'sub' => 'UserLoginToken', // Subject of the token + 'user_id' => $params['user_id'], + 'iat' => time(), // Time when JWT was issued. + 'exp' => $rememberMe ? time() + (60 * 60 * 24 * 30) : time() + (60 * 60 * 24 * $dayCounter), + 'remember_me' => $rememberMe ? true : false , + ]; + $response = [ + 'status ' => 1, + 'data' => [ + 'token' => $this->jwt($jwtParams), + 'iat' => $jwtParams['iat'], + 'remember_me' => $rememberMe , + 'exp' => $jwtParams['exp'] + ], + + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + + } + +} diff --git a/app/Core/Service/LanguageBaseService.php b/app/Core/Service/LanguageBaseService.php new file mode 100644 index 0000000..935869a --- /dev/null +++ b/app/Core/Service/LanguageBaseService.php @@ -0,0 +1,581 @@ +request = $request; + $this->languageBaseRepository = $languageBaseRepository; + $this->languageTranslateRepository = $languageTranslateRepository; + $this->languageRepository = $languageRepository; + + $this->tableVariables = [ + +/* 'currency' => [ + 'repository' => 'App\Core\Repository\Currency\CurrencyRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'language' => [ + 'repository' => 'App\Core\Repository\Language\LanguageRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_channel_category' => [ + 'repository' => 'App\Core\Repository\PropertyChannelCategory\PropertyChannelCategoryRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_content_category' => [ + 'repository' => 'App\Core\Repository\PropertyContentCategory\PropertyContentCategoryRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_executive_type' => [ + 'repository' => 'App\Core\Repository\PropertyExecutiveType\PropertyExecutiveTypeRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_fact' => [ + 'repository' => 'App\Core\Repository\PropertyFact\PropertyFactRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => true, + 'parent_column_name' => 'parent_id' + ], + + 'property_photo_category' => [ + 'repository' => 'App\Core\Repository\PropertyPhotoCategory\PropertyPhotoCategoryRepository', + 'source_column' => 'category_name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => 'null' + ], + + 'property_room_bed_type' => [ + 'repository' => 'App\Core\Repository\PropertyRoomBedType\PropertyRoomBedTypeRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => 'null' + ], + + 'property_room_type' => [ + 'repository' => 'App\Core\Repository\PropertyRoomType\PropertyRoomTypeRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => 'null' + ], + + 'property_type' => [ + 'repository' => 'App\Core\Repository\PropertyType\PropertyTypeRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => 'null' + ], + + 'country' => [ + 'repository' => 'App\Core\Repository\Country\CountryRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_room_view_type' => [ + 'repository' => 'App\Core\Repository\PropertyRoomViewType\PropertyRoomViewTypeRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_booking_type' => [ + 'repository' => 'App\Core\Repository\PropertyBookingType\PropertyBookingTypeRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_booking_payment_type' => [ + 'repository' => 'App\Core\Repository\PropertyBookingPaymentType\PropertyBookingPaymentTypeRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_availability_type' => [ + 'repository' => 'App\Core\Repository\PropertyAvailabilityType\PropertyAvailabilityTypeRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_place_category' => [ + 'repository' => 'App\Core\Repository\PropertyPlaceCategory\PropertyPlaceCategoryRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => true, + 'parent_column_name' => 'parent_id' + ], + 'property_place_working_hour' => [ + 'repository' => 'App\Core\Repository\PropertyPlaceWorkingHour\PropertyPlaceWorkingHourRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => 'parent_id' + ], + + + 'property_place_fact_title' => [ + 'repository' => 'App\Core\Repository\PropertyPlaceFactTitle\PropertyPlaceFactTitleRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'property_place_fact' => [ + 'repository' => 'App\Core\Repository\PropertyPlaceFact\PropertyPlaceFactRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'place_category_field' => [ + 'repository' => 'App\Core\Repository\PlaceCategoryField\PlaceCategoryFieldRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'place_category_field_option' => [ + 'repository' => 'App\Core\Repository\PlaceCategoryFieldOption\PlaceCategoryFieldOptionRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => false, + 'parent_column_name' => null + ], + + 'promotion_type' => [ + 'repository' => 'App\Core\Repository\PromotionType\PromotionTypeRepository', + 'source_column' => 'name', + 'key_column' => 'language_key', + 'parent_structure' => true, + 'parent_column_name' => 'parent_id' + ] +*/ + + ]; + + } + + + public function createApplicationLanguageData() + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $languageCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + $languages = $this->languageRepository->findByCriteria($languageCriteria, ['id', 'code', 'name', 'status']); + if (!$languages) { + throw new ApiErrorException('Language data not found'); + } + + $languagesBase = $this->languageBaseRepository->findByCriteria([], ['id', 'code', 'key', 'text']); + $languagesBase = $languagesBase ? $languagesBase : []; + + $languagesBaseArray = []; + foreach ($languagesBase as $base) { + $languagesBaseArray[$base['key']] = $base['text']; + } + + $languagesTranslate = $this->languageTranslateRepository->findByCriteria([], ['id', 'code', 'key', 'text']); + $languagesTranslate = $languagesTranslate ? $languagesTranslate : []; + + $languagesTranslateArray = []; + foreach ($languagesTranslate as $translate) { + $languagesTranslateArray[$translate['code']][$translate['key']] = $translate['text']; + + } + + $languageExportData = []; + + foreach ($languages as $language) { + $languageContent = null; + if ($language['code'] == 'en') { + $languageContent = $languagesBaseArray; + } elseif (isset($languagesTranslateArray[$language['code']])) { + $languageContent = $languagesTranslateArray[$language['code']]; + } + + $languageExportData[$language['code']] = $languageContent; + } + + $response = [ + 'status' => 1, + 'data' => $languageExportData + ]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + } + + public function createApplicationLanguageFiles() + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $languageCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + $languages = $this->languageRepository->findByCriteria($languageCriteria, ['id', 'code', 'name', 'status']); + if (!$languages) { + throw new ApiErrorException('Language data not found'); + } + + $languagesBase = $this->languageBaseRepository->findByCriteria([], ['id', 'code', 'key', 'text']); + $languagesBase = $languagesBase ? $languagesBase : []; + + $languagesBaseArray = []; + foreach ($languagesBase as $base) { + $languagesBaseArray[$base['key']] = trim($base['text']); + } + + $languagesTranslate = $this->languageTranslateRepository->findByCriteria([], ['id', 'code', 'key', 'text']); + $languagesTranslate = $languagesTranslate ? $languagesTranslate : []; + + $languagesTranslateArray = []; + foreach ($languagesTranslate as $translate) { + $clearSpaceData = trim($translate['text']); + if($clearSpaceData){ + $languagesTranslateArray[$translate['code']][$translate['key']] = $clearSpaceData; + } + } + + $applicationLanguageFolder = Config::get('app.appLanguageJsonStoredFolder'); + $myWebLanguageFolder = Config::get('app.myWebLanguageJsonStoredFolder'); + //$wwwLanguageFolder = Config::get('app.wwwLanguageJsonStoredFolder'); + $bookingEngineLanguageFolder = Config::get('app.bookingEngineLanguageJsonStoredFolder'); + $apiLanguageFolder = resource_path() . '/lang/' ; + + foreach ($languages as $language) { + $languageContent = null; + if ($language['code'] == 'en') { + $languageContent = $languagesBaseArray; + } elseif (isset($languagesTranslateArray[$language['code']])) { + $languageContent = $languagesTranslateArray[$language['code']]; + } + $filePath = $applicationLanguageFolder . '/language/' . $language['code']; + if ($languageContent) { + // App Language Create + if (!File::exists($filePath)) { + File::makeDirectory($filePath, 0777, true); + } + File::put($filePath . '/common.json', json_encode($languageContent, JSON_UNESCAPED_UNICODE)); + + // Api Language Create + if (!File::exists($apiLanguageFolder)) { + File::makeDirectory($apiLanguageFolder, 0777, true); + } + File::put($apiLanguageFolder . $language['code'] . '.json', json_encode($languageContent, JSON_UNESCAPED_UNICODE)); + + // MyWeb Language Create + if (!File::exists($myWebLanguageFolder)) { + File::makeDirectory($myWebLanguageFolder, 0777, true); + } + File::put($myWebLanguageFolder . $language['code'] . '.json', json_encode($languageContent, JSON_UNESCAPED_UNICODE)); + + // www Language Create + /*if (!File::exists($wwwLanguageFolder)) { + File::makeDirectory($wwwLanguageFolder, 0777, true); + } + File::put($wwwLanguageFolder . $language['code'] . '.json', json_encode($languageContent, JSON_UNESCAPED_UNICODE));*/ + + // bookingEngine Language Create + if (!File::exists($bookingEngineLanguageFolder)) { + File::makeDirectory($bookingEngineLanguageFolder, 0777, true); + } + File::put($bookingEngineLanguageFolder . $language['code'] . '.json', json_encode($languageContent, JSON_UNESCAPED_UNICODE)); + + } + } + + try { + Artisan::call('queue:restart'); + } catch (Exception $error) { + Log::error($error->getMessage()); + } + + $response['status'] = 1; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + } + + public function createApplicationLanguageBaseData() + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $languagesBase = $this->languageBaseRepository->findByCriteria([], ['id', 'code', 'key', 'text']); + $languagesBase = $languagesBase ? $languagesBase : []; + + $languagesBaseArray = []; + foreach ($languagesBase as $base) { + $languagesBaseArray[$base['key']] = $base['text']; + } + + $insertBaseData = []; + foreach ($this->tableVariables as $key => $value) { + $repository = App::make($value['repository']); + $tableVariables = $repository->findByCriteria([]); + $tableVariables = $tableVariables ? $tableVariables : []; + foreach ($tableVariables as $tableVariable) { + if (!isset($languagesBaseArray[$tableVariable[$value['key_column']]])) { + + $insertBaseData[] = [ + 'key' => $tableVariable[$value['key_column']], + 'code' => 'en', + 'text' => $tableVariable[$value['source_column']], + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ]; + } + } + } + $insertBaseDataStatus = $this->languageBaseRepository->createAll($insertBaseData); + if ($insertBaseDataStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + + } + + $response['status'] = 1; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + } + + public function fillApplicationLanguageKeys() + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + foreach ($this->tableVariables as $key => $value) { + print("for table => $key \n"); + $repository = App::make($value['repository']); + $oldVariables = $repository->findByCriteria([]); + $oldVariables = $oldVariables ? $oldVariables : []; + $oldVariablesCollect = collect($oldVariables); + $oldVariables = $oldVariablesCollect->keyBy('id'); + if ($value['parent_structure'] == true) { + + $mainFacts = $oldVariablesCollect->where('parent_id', '=', null); + + foreach ($mainFacts as $mainFact) { + + // main fact keys, + $mainFactSlug = language_key($mainFact[$value['source_column']]); + $mainFactLanguageKey = $key . '-' . $mainFactSlug; + $mainFactLanguageKey = $this->keyGenerator($mainFactLanguageKey, $value['key_column'], $oldVariables, 0); + + if ($mainFact[$value['key_column']] == null) { + $mainFact[$value['key_column']] = $mainFactLanguageKey; + $oldVariables[$mainFact['id']] = $mainFact; + $repository->update($mainFact['id'], [$value['key_column'] => $mainFactLanguageKey]); + } + + + $subFacts = $oldVariablesCollect->where('parent_id', '=', $mainFact['id']); + foreach ($subFacts as $subFact) { + + // sub fact keys + $subFactSlug = language_key($subFact[$value['source_column']]); + $subFactLanguageKey = $mainFactLanguageKey . '-' . $subFactSlug; + $subFactLanguageKey = $this->keyGenerator($subFactLanguageKey, $value['key_column'], $oldVariables, 0); + + if ($subFact[$value['key_column']] == null) { + $subFact[$value['key_column']] = $subFactLanguageKey; + $oldVariables[$subFact['id']] = $subFact; + $repository->update($subFact['id'], [$value['key_column'] => $subFactLanguageKey]); + } + + $facts = $oldVariablesCollect->where('parent_id', '=', $subFact['id']); + foreach ($facts as $fact) { + + if ($fact[$value['key_column']] == null) { + // sub fact keys + $factSlug = language_key($fact[$value['source_column']]); + $factLanguageKey = $subFactLanguageKey . '-' . $factSlug; + $factLanguageKey = $this->keyGenerator($factLanguageKey, $value['key_column'], $oldVariables, 0); + + $fact[$value['key_column']] = $factLanguageKey; + $oldVariables[$fact['id']] = $fact; + $repository->update($fact['id'], [$value['key_column'] => $factLanguageKey]); + } + + } + + } + + } + } else { + foreach ($oldVariables as $oldVariable) { + if ($oldVariable[$value['key_column']] == null) { + $slug = language_key($oldVariable[$value['source_column']]); + $languageKey = $key . '-' . $slug; + $languageKey = $this->keyGenerator($languageKey, $value['key_column'], $oldVariables, 0); + $oldVariable[$value['key_column']] = $languageKey; + $oldVariables[$oldVariable['id']] = $oldVariable; + $repository->update($oldVariable['id'], [$value['key_column'] => $languageKey]); + } + } + } + } + + $response['status'] = 1; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + } + + protected function keyGenerator($key, $keyColumn, $oldData, $index) + { + + $checkKey = collect($oldData)->where($keyColumn, '=', $key)->first(); + + + if ($checkKey != null) { + + $keyArray1 = explode('-', $key); + $lastArray1Item = end($keyArray1); + + $keyArray2 = explode('_', $lastArray1Item); + $lastArray2Item = end($keyArray2); + $index++; + + + if (is_numeric($lastArray2Item)) { + + array_pop($keyArray2); + array_push($keyArray2, $index); + $array2ToStr = implode($keyArray2, "_"); + + + array_pop($keyArray1); + array_push($keyArray1, $array2ToStr); + $array1ToStr = implode($keyArray1, "-"); + + + $newKey = $array1ToStr; + } else { + + $newKey = $key . '_' . $index; + } + + $key = $this->keyGenerator($newKey, $keyColumn, $oldData, $index); + } + return $key; + + } +} diff --git a/app/Core/Service/LanguageService.php b/app/Core/Service/LanguageService.php new file mode 100644 index 0000000..2702c4d --- /dev/null +++ b/app/Core/Service/LanguageService.php @@ -0,0 +1,196 @@ +request = $request; + $this->languageRepository = $languageRepository; + } + + + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $data = $this->languageRepository->findByCriteria($param, $column); + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $languageData = + [ + "name" => fillOnUndefined($param, "name"), + "rating" => fillOnUndefined($param, "rating"), + "longitude" => fillOnUndefined($param, "longitude"), + "latitude" => fillOnUndefined($param, "latitude"), + "status" => fillOnUndefined($param, "status", 0), + "currency_type" => fillOnUndefined($param, "currency_type"), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => null, + "created_at" => time(), + "updated_at" => time(), + + ]; + + $validationResult = $this->languageCreateValidator->validate($languageData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $languageCreateResult = $this->languageRepository->create($languageData); + + if ($languageCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response['status'] = 1; + $response['data'] = $languageCreateResult["data"]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getAllLanguages($languageCriteria, $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $languages =$this->languageRepository->findByCriteria($languageCriteria,$column); + + if(!$languages){ + throw new ApiErrorException(lang('Language data not found')); + } + + $response['status'] = 1; + $response['data'] = $languages; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + } + + public function getApplicationLanguages() + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $languageCriteria = [ + 'criteria' => [ + ['field' => 'status' , 'condition' => '=', 'value' => 1 ], + ['field' => 'is_application' , 'condition' => '=', 'value' => 1 ], + ['field' => 'is_published' , 'condition' => '=', 'value' => 1 ] + ] , + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + $languageData = $this->select($languageCriteria, ['id','code','name','status', 'language_key']); + $response['status'] = 1; + $response['data'] = $languageData['data']; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function propertyLanguageSpoken($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $languageCriteria = [ + 'criteria' => [ + ['field' => 'status' , 'condition' => '=', 'value' => 1 ], + ] , + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + $languageData = $this->select($languageCriteria, ['id','code','name','status', 'language_key']); + $spokenLanguages = $params['selected_languages']; + $responseLanguage = [] ; + + foreach ($languageData['data'] as $language) { + $responseItem = $language ; + $responseItem['is_selected'] = array_search($language['code'], $spokenLanguages) > -1 ? true : false ; + $responseLanguage[] = $responseItem ; + + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseLanguage]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/ManualPaymentMailService.php b/app/Core/Service/ManualPaymentMailService.php new file mode 100644 index 0000000..6288673 --- /dev/null +++ b/app/Core/Service/ManualPaymentMailService.php @@ -0,0 +1,148 @@ +mailer = $mailer ; + $this->propertyService = $propertyService ; + $this->propertyPaymentService = $propertyPaymentService; + + } + + public function process ($params = []) + { + try + { + + + $paymentDetailParam = [ + 'criteria' => [ + ['field' => 'order_id', 'condition' => '=', 'value' => $params['orderCode']], + ['field' => 'transaction_type', 'condition' => '=', 'value' => 'LNK'], + ['field' => 'status', 'condition' => '=', 'value' => 5], + ], + 'with' => ['relatedTransactions', 'paymentUser'], + 'firstRow' => true + ]; + + $paymentDetail = $this->propertyPaymentService->selectPaymentTransaction($paymentDetailParam); + + + if ($paymentDetail['status'] != 'success' || empty($paymentDetail['data'])) { + throw new ApiErrorException(lang('Payment not found')); + } + + $payment = $paymentDetail['data'] ; + $transactions = collect($payment['related_transactions']) ; + $successTransaction = $transactions->where('status' , '=', 1)->first() ; + $paymentParams = $payment['paramsArray']; + + if($paymentParams['email']){ + + $propertyParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $payment['property_id']], + ], + 'with' => ['propertyBrand', 'propertyContact','propertyExecutive'], + 'firstRow' => true + ]; + + $getProperty = $this->propertyService->select($propertyParam) ; + if ($getProperty['status'] != "success") { + throw new ApiErrorException($getProperty['message']); + } + $property = $getProperty['data']; + + + $bcc[] = "bd@extranetwork.com"; + if(isset($payment['payment_user']['email'])){ + $bcc[] = $payment['payment_user']['email']; + } + + $accountExecutives = collect($property['property_executive'])->where('status',1)->where('executive_type_id',1)->pluck('email')->toArray(); + if(!empty($accountExecutives)) { + $bcc = array_merge($bcc,$accountExecutives); + } + + $mailData = [ + 'to' => [ + 'email' => $paymentParams['email'], + ], + 'bcc' => $bcc + + ]; + + $hostAddress = Config::get('app.bookingEngineUrl').'/link/'.$payment['order_id'].'/'.$params['language_code'].'/receipt' ; + $logo = 'https://www.extranetwork.com/assets/img/logo/logo.png'; + if(isset($property['property_brand']['logo_name'])){ + $logo = Config::get('app.imageUrl'). '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'].'_250x250.'. $property['property_brand']['logo_file_ext']; + } + + $mailViewParams = [ + 'property' => $property, + 'payment_params' => $paymentParams, + 'success_transaction' => $successTransaction, + 'amount' => $successTransaction['amount'], + 'currency' => $successTransaction['currency'], + 'date_time' => date('d.m.Y H:i', $successTransaction['created_at'] ), + 'name_surname' => $successTransaction['paramsArray']['creditCard']['name'], + 'email' => $paymentParams['email'], + 'process_title' => $paymentParams['title'], + 'property_name' => $property['name'], + 'url' => $hostAddress, + 'logo' => $logo, + 'language_code' => $params['language_code'], + ] ; + + $mailParams = [ + 'mailData' => $mailData, + 'mailViewParams' => $mailViewParams + ]; + + $this->mailer->onQueue( + 'manualPaymentMail', + new ManualPaymentMail($mailParams) + ); + + } + + + + + } + catch ( Exception $e ) + { + $message = $e->getFile()." ".$e->getLine()." ".$e->getMessage(); + Log::error($message); + return output( ['status' => -1, 'message' => $e->getMessage()] ); + } + } + +} diff --git a/app/Core/Service/MyWebContentService.php b/app/Core/Service/MyWebContentService.php new file mode 100644 index 0000000..b0ba5db --- /dev/null +++ b/app/Core/Service/MyWebContentService.php @@ -0,0 +1,2596 @@ +propertyRepository = $propertyRepository; + $this->propertyAdditionalInfoService = $propertyAdditionalInfoService; + $this->propertyContactService = $propertyContactService; + $this->propertyFactService = $propertyFactService; + $this->propertyPhotoService = $propertyPhotoService; + $this->propertyRoomService = $propertyRoomService; + $this->propertyExecutiveService = $propertyExecutiveService; + $this->propertyWebPhotoMappingRepository = $propertyWebPhotoMappingRepository; + $this->propertyWebColorMappingService = $propertyWebColorMappingService; + $this->propertyWebAboutUsService = $propertyWebAboutUsService; + $this->mailer = $mailer; + $this->propertyWebContactFormValidator = $propertyWebContactFormValidator; + $this->propertyPlaceService = $propertyPlaceService; + $this->propertyWebRoomMappingService = $propertyWebRoomMappingService; + $this->propertyFactMappingService = $propertyFactMappingService; + $this->propertyWebContentRepository = $propertyWebContentRepository; + $this->propertyWebReviewRepository = $propertyWebReviewRepository; + $this->propertyRoomRatePriceRepository = $propertyRoomRatePriceRepository; + + } + + public function home($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyRooms.propertyRoomViewMapping.propertyRoomViewType', + 'propertyChain', 'propertyRooms.propertyRoomPhotoMapping.propertyRoomPhoto', + 'propertyWeb', 'propertyRooms.propertyRoomType', 'propertyAwardsCertificates.awardsCertificateCategory', + 'propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlaceCategory', + 'propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlacePhotoMapping.propertyPlacePhoto', + 'propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFact', + 'propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFactTitle', + 'propertyWebComponent' + ], + 'firstRow' => true + ]; + + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + + if (!$property) { + throw new ApiErrorException('property not found'); + } + + $propertyWebRoomCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $propertyWebRoom = $this->propertyWebRoomMappingService->select($propertyWebRoomCriteria, ['property_room_id']); + if ($propertyWebRoom['status'] != 'success') { + throw new ApiErrorException($propertyWebRoom['message']); + } + $propertyWebRoomIds = collect($propertyWebRoom['data'])->pluck('property_room_id')->toArray(); + + + $rooms = []; + foreach ($propertyWebRoom['data'] as $roomMappingData) { + $roomCheck = collect($property['property_rooms'])->where('id', $roomMappingData['property_room_id'])->first(); + if ($roomCheck) { + + + $rooms[$roomMappingData['property_room_id']] = $roomCheck; + $rooms[$roomMappingData['property_room_id']]['slug'] = Str::slug($roomCheck['name'], $separator = '-', $language = 'en'); + + unset($rooms[$roomMappingData['property_room_id']]['property_room_photo_mapping']); + $roomThumbnailPhoto = NULL; + + $rooms[$roomMappingData['property_room_id']]['property_room_photos'] = collect($roomCheck['property_room_photo_mapping'])->map(function ($value, $key) use ($property, &$roomThumbnailPhoto) { + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_1024x768.' . $value['property_room_photo']['file_ext']; + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_1024x768.' . $value['property_room_photo']['file_ext']; + } else { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_medium.' . $value['property_room_photo']['file_ext']; + } + if ($key === 0) { + $photoThumbUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_200x200.' . $value['property_room_photo']['file_ext']; + if (File::exists($photoThumbUrlFilePath)) { + $roomThumbnailPhoto = Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_200x200.' . $value['property_room_photo']['file_ext']; + } else { + $roomThumbnailPhoto = Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_thumbnail.' . $value['property_room_photo']['file_ext']; + } + } + return $photoUrlFilePath; + + })->all(); + + $rooms[$roomMappingData['property_room_id']]['cover_photo'] = $rooms[$roomMappingData['property_room_id']]['property_room_photos'] != null ? $rooms[$roomMappingData['property_room_id']]['property_room_photos'][0] : null; + $rooms[$roomMappingData['property_room_id']]['room_thumbnail_photo'] = $roomThumbnailPhoto; + + + } + } + + $rooms = array_values($rooms); + + /* + $rooms = collect($property['property_rooms']) + ->filter(function ($value) use ($propertyWebRoomIds) { + if (!empty($propertyWebRoomIds)) { + if (in_array($value['id'], $propertyWebRoomIds)) { + return $value; + } + } else { + return $value; + } + }) + ->map(function ($value, $valueKey) use ($property, $propertyWebRoomIds) { + + $room = $value; + unset($room['property_room_photo_mapping']); + $roomThumbnailPhoto = NULL; + $room['property_room_photos'] = collect($value['property_room_photo_mapping'])->map(function ($value, $key) use ($property, &$roomThumbnailPhoto) { + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_1024x768.' . $value['property_room_photo']['file_ext']; + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_1024x768.' . $value['property_room_photo']['file_ext']; + } else { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_medium.' . $value['property_room_photo']['file_ext']; + } + if ($key === 0) { + $photoThumbUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_200x200.' . $value['property_room_photo']['file_ext']; + if (File::exists($photoThumbUrlFilePath)) { + $roomThumbnailPhoto = Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_200x200.' . $value['property_room_photo']['file_ext']; + } else { + $roomThumbnailPhoto = Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . '/' . $value['property_room_photo']['photo_name'] . '_thumbnail.' . $value['property_room_photo']['file_ext']; + } + } + return $photoUrlFilePath; + + })->all(); + $room['cover_photo'] = $room['property_room_photos'] != null ? $room['property_room_photos'][0] : null; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + $room['room_thumbnail_photo'] = $roomThumbnailPhoto; + + return $room; + })->values()->all(); + */ + + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + $coverPhotos = $this->coverPhotos($params); + + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $propertyAwardsCertificates = collect($property['property_awards_certificates']) + ->where('status', '=', 1) + ->where('awards_certificate_category.type', '!=', 'PLC') + //->where('awards_certificate_category.type', '!=', 'MNU') + ->map(function ($value) { + + $return = $value; + unset($return['awards_certificate_category']); + $image = null; + + if ($value['file_path']) { + $urlPath = '/property-photos/' . $value['property_id'] . "/awards-certificates/"; + $image = Config::get('app.imageUrl') . $urlPath . $value['file_path']; + } + + + $catLogo = null; + if ($value['awards_certificate_category']['id']) { + $urlPath = '/assets/img/awards/'; + $catLogo = Config::get('app.client_server') . $urlPath . $value['awards_certificate_category']['id'] . '.png'; + } + + $return['file_path'] = $image ? $image : null; + + $category = [ + 'category_name' => $value['awards_certificate_category']['name'], + 'category_language_key' => $value['awards_certificate_category']['language_key'], + 'category_country_code' => $value['awards_certificate_category']['country_code'], + 'category_logo' => $catLogo, + 'category_type' => $value['awards_certificate_category']['type'], + 'category_status' => $value['awards_certificate_category']['status'], + ]; + return array_merge($return, $category); + })->values()->all(); + + array_multisort( + array_column($propertyAwardsCertificates, 'date'), SORT_DESC, + array_column($propertyAwardsCertificates, 'name'), SORT_ASC, + $propertyAwardsCertificates + ); + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_language_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'landing_photo' => $coverPhotos, + 'description' => $property['name'], + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'awards_certificate_category' => $propertyAwardsCertificates, + 'property_contact' => $property['property_contact'] + ]; + + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if (isset($propertyAdditionalInfo['data'])) { + $responseData['additional_info'] = $propertyAdditionalInfo['data']; + } + + + $propertyPlace = []; + /** Property Place **/ + foreach ($property['property_web']['property_web_place_mapping'] as $place) { + + $place = $place['place_detail']; + + if ($place['status'] != 1) { + continue; + } + + $propertyPlace[$place['id']]['id'] = $place['id']; + $propertyPlace[$place['id']]['name'] = $place['name']; + $propertyPlace[$place['id']]['slug'] = Str::slug($place['name'], $separator = '-', $language = 'en'); + + $propertyPlace[$place['id']]['category']['name'] = $place['property_place_category']['name']; + $propertyPlace[$place['id']]['category']['language_key'] = $place['property_place_category']['language_key']; + + $propertyPlace[$place['id']]['photo'] = [ + 'thumb' => '/assets/img/placeholder.webp', + 'fixed' => '/assets/img/placeholder.webp' + ]; + + if (isset($place['property_place_photo_mapping'])) { + $propertyPlacePhotoMapping = collect($place['property_place_photo_mapping'])->sortBy('id')->first(); + if ($propertyPlacePhotoMapping) { + $propertyPlace[$place['id']]['photo'] = [ + 'thumb' => $propertyPlacePhotoMapping['property_place_photo']['photoUrl']['thumb'], + 'fixed' => $propertyPlacePhotoMapping['property_place_photo']['photoUrl']['fixed'], + ]; + } + } + + + $propertyPlaceFact = []; + foreach ($place['property_place_fact_mapping'] as $fact) { + + $factId = $fact['property_place_fact_title_fact_mapping']['place_fact_id']; + $factCategoryId = $fact['property_place_fact_title_fact_mapping']['place_fact_title_id']; + $propertyPlaceFact[$factCategoryId]['category'] = [ + 'name' => $fact['property_place_fact_title_fact_mapping']['place_fact_title']['name'], + 'language_key' => $fact['property_place_fact_title_fact_mapping']['place_fact_title']['language_key'], + ]; + + $propertyPlaceFact[$factCategoryId]['fact'][] = [ + 'name' => $fact['property_place_fact_title_fact_mapping']['place_fact']['name'], + 'language_key' => $fact['property_place_fact_title_fact_mapping']['place_fact']['language_key'], + 'icon' => $fact['property_place_fact_title_fact_mapping']['place_fact']['icon'], + ]; + + } + + $propertyPlace[$place['id']]['feature'] = !empty($propertyPlaceFact) ? array_values($propertyPlaceFact) : []; + + if (count($propertyPlace) == 2) { + //break; + } + + } + + /** Property Place **/ + $responseData['property_place'] = !empty($propertyPlace) ? array_values($propertyPlace) : []; + + + /** Property Facts **/ + $getPropertyFact = $this->propertyFactService->getPropertyFact($params); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $getPropertyFact = $this->getPropertyFactForHtml($getPropertyFact['data']); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $responseData['property_facts'] = $getPropertyFact['data']; + /** Property Facts **/ + + + /** Property Review **/ + $propertyWebReviewData = []; + + + $propertyWebComponentCheck = collect($property['property_web_component'])->where('component_id', 3)->count(); + if ($propertyWebComponentCheck) { + $propertyWebReviewRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'time', 'value' => 'DESC'] + ] + ]; + + $propertyWebReview = $this->propertyWebReviewRepository->findByCriteria($propertyWebReviewRequest); + + + $currentLanguageCheck = []; + if (fillOnUndefined($params, 'currentLanguage')) { + $currentLanguageCheck = collect($propertyWebReview)->where('language_code', $params['currentLanguage'])->toArray(); + } + + if (count($currentLanguageCheck) > 4) { + $propertyWebReviewData = collect($currentLanguageCheck)->take(10)->toArray(); + } else { + $propertyWebReviewData = collect($propertyWebReview)->take(10)->toArray(); + } + + $propertyWebReviewData = $propertyWebReviewData ? array_values($propertyWebReviewData) : []; + } + + $responseData['property_review'] = $propertyWebReviewData; + /** Property Review **/ + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function aboutUs($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $responseData = []; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', + 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates.awardsCertificateCategory', + 'propertyLanguageSpoken.languageCode', + ], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $coverPhotos = $this->coverPhotos($params); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + + $propertyWebAboutUsParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'mode' => $params['mode'] === "preview" ? 2 : 1, // live : 1 preview : 2 + ]; + $propertyWebAboutUs = $this->propertyWebAboutUsService->getPropertyWebAboutUs($propertyWebAboutUsParams); + + if ($propertyWebAboutUs['status'] != 'success') { + throw new ApiErrorException($propertyWebAboutUs['message']); + } + + $aboutUs = isset($propertyWebAboutUs['data']) && !empty($propertyWebAboutUs['data']) ? $propertyWebAboutUs['data'] : []; + + $aboutUsFiltred = collect($aboutUs)->first(function ($value, $key) use ($params) { + if ($value['language_code'] == $params['currentLanguage']) { + return $value['value']; + } + }); + + $propertyAwardsCertificates = collect($property['property_awards_certificates']) + ->where('awards_certificate_category.type', '!=', 'PLC') + ->where('status', '=', 1) + ->map(function ($value) { + + $return = $value; + unset($return['awards_certificate_category']); + $image = null; + + if ($value['file_path']) { + $urlPath = '/property-photos/' . $value['property_id'] . "/awards-certificates/"; + $image = Config::get('app.imageUrl') . $urlPath . $value['file_path']; + } + + + $catLogo = null; + if ($value['awards_certificate_category']['id']) { + $urlPath = '/assets/img/awards/'; + $catLogo = Config::get('app.client_server') . $urlPath . $value['awards_certificate_category']['id'] . '.png'; + } + + $return['file_path'] = $image ? $image : null; + + $category = [ + 'category_name' => $value['awards_certificate_category']['name'], + 'category_language_key' => $value['awards_certificate_category']['language_key'], + 'category_country_code' => $value['awards_certificate_category']['country_code'], + 'category_logo' => $catLogo, + 'category_type' => $value['awards_certificate_category']['type'], + 'category_status' => $value['awards_certificate_category']['status'], + ]; + return array_merge($return, $category); + })->values()->all(); + + array_multisort( + array_column($propertyAwardsCertificates, 'date'), SORT_DESC, + array_column($propertyAwardsCertificates, 'name'), SORT_ASC, + $propertyAwardsCertificates + ); + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + + $propertyLanguageSpoken = null; + $propertyLanguageSpoken = collect($property['property_language_spoken'])->map(function ($spokenLanguage) { + return [ + 'code' => $spokenLanguage['language_code']['code'], + 'name' => $spokenLanguage['language_code']['name'], + 'language_key' => $spokenLanguage['language_code']['language_key'] + ]; + }); + + + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => isset($aboutUsFiltred['value']) ? $aboutUsFiltred['value'] : "", + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'property_contact' => $property['property_contact'], + 'landing_photo' => $coverPhotos, + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'awards_certificate_category' => $propertyAwardsCertificates, + 'property_language_spoken' => $propertyLanguageSpoken, + ]; + + $getPropertyFact = $this->propertyFactService->getPropertyFact($params); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $getPropertyFact = $this->getPropertyFactForHtml($getPropertyFact['data']); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $responseData['property_facts'] = $getPropertyFact['data']; + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if (isset($propertyAdditionalInfo['data'])) { + $responseData['additional_info'] = $propertyAdditionalInfo['data']; + } + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function getPropertyFactForHtml($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $categories = $params; + $responseArray = []; + foreach ($categories as $category) { + $categoryArray = []; + $categoryItem = $category; + foreach ($category['fact_subcategory'] as $subCategory) { + $subCategoryItem = $subCategory; + $facts = collect($subCategory['fact']) + ->where('is_selected', '=', true) + ->map(function ($value) { + return $value; + })->values()->all(); + $subCategoryItem['fact'] = $facts ? $facts : []; + if ($facts) { + $categoryArray[$subCategory['id']] = $subCategoryItem; + } + } + $categoryItem['fact_subcategory'] = $categoryArray; + $responseArray[$category['id']] = $categoryItem; + } + + $response = [ + 'status' => true, + 'data' => $responseArray, + ]; + + } 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); + + } + + public function gallery($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => '', + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'property_contact' => $property['property_contact'], + + ]; + + + $responseData['photos'] = $this->photos($params);; + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function rooms($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + + $propertyWebRoomCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $propertyWebRoom = $this->propertyWebRoomMappingService->select($propertyWebRoomCriteria, ['property_room_id']); + if ($propertyWebRoom['status'] != 'success') { + throw new ApiErrorException($propertyWebRoom['message']); + } + $propertyWebRoomIds = collect($propertyWebRoom['data'])->pluck('property_room_id')->toArray(); + + $rooms = []; + foreach ($propertyWebRoom['data'] as $roomMappingData) { + $roomCheck = collect($property['property_rooms'])->where('id', $roomMappingData['property_room_id'])->first(); + if ($roomCheck) { + $rooms[$roomMappingData['property_room_id']] = $roomCheck; + $rooms[$roomMappingData['property_room_id']]['slug'] = Str::slug($roomCheck['name'], $separator = '-', $language = 'en'); + } + } + + $rooms = array_values($rooms); + + /*$rooms = collect($property['property_rooms']) + ->filter(function ($value) use ($propertyWebRoomIds) { + if (!empty($propertyWebRoomIds)) { + if (in_array($value['id'], $propertyWebRoomIds)) { + return $value; + } + } else { + return $value; + } + }) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all();*/ + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => '', + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'property_contact' => $property['property_contact'], + + ]; + + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyRoomType', 'propertyRoomDefaultPhoto.propertyRoomPhoto', 'propertyRoomFactMapping.propertyFact', 'propertyRoomViewMapping.propertyRoomViewType'] + ]; + $propertyRooms = $this->propertyRoomService->select($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number']); + if ($propertyRooms['status'] != 'success') { + throw new ApiErrorException($propertyRooms['message']); + } + + if (!$propertyRooms['data']) { + throw new ApiErrorException('room data not found'); + } + + $rooms = []; + foreach ($propertyWebRoom['data'] as $roomMappingData) { + $roomCheck = collect($propertyRooms['data'])->where('id', $roomMappingData['property_room_id'])->first(); + if ($roomCheck) { + $rooms[$roomMappingData['property_room_id']] = $roomCheck; + $rooms[$roomMappingData['property_room_id']]['slug'] = Str::slug($roomCheck['name'], $separator = '-', $language = 'en'); + } + } + + $newRoomMapping = []; + foreach ($rooms as $room) { + + $roomPhoto = $room['property_room_default_photo']; + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_1024x768.' . $roomPhoto['property_room_photo']['file_ext']; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_200x200.' . $roomPhoto['property_room_photo']['file_ext']; + + $photoItem['photo_original'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '.' . $roomPhoto['property_room_photo']['file_ext']; + + if (File::exists($photoUrlFilePath)) { + $photoItem['photo_large'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_1024x768.' . $roomPhoto['property_room_photo']['file_ext']; + } else { + $photoItem['photo_large'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_medium.' . $roomPhoto['property_room_photo']['file_ext']; + } + + if (File::exists($photoUrlThumbFilePath)) { + $photoItem['photo_thumbnail'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_200x200.' . $roomPhoto['property_room_photo']['file_ext']; + } else { + $photoItem['photo_thumbnail'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_thumbnail.' . $roomPhoto['property_room_photo']['file_ext']; + } + + $room['default_photo'] = $photoItem; + unset($room['property_room_default_photo']); + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + + $roomFacts = $room['property_room_fact_mapping']; + $newFacts = []; + + foreach ($roomFacts as $fact) { + + $factItem['id'] = $fact['id']; + $factItem['fact_id'] = $fact['fact_id']; + $factItem['name'] = $fact['property_fact']['name']; + $factItem['language_key'] = $fact['property_fact']['language_key']; + $factItem['type'] = $fact['property_fact']['type']; + $factItem['order_number'] = $fact['property_fact']['order_number']; + $factItem['icon'] = $fact['property_fact']['icon']; + $factItem['is_feature'] = $fact['is_feature']; + $newFacts[] = $factItem; + } + + unset($room['property_room_fact_mapping']); + + $room['facts'] = $newFacts; + + // Room Views + $roomViews = $room['property_room_view_mapping']; + $newViews = []; + foreach ($roomViews as $view) { + $viewItem['id'] = $view['property_room_view_type']['id']; + $viewItem['name'] = $view['property_room_view_type']['name']; + $viewItem['language_key'] = $view['property_room_view_type']['language_key']; + $newViews[] = $viewItem; + } + unset($room['property_room_view_mapping']); + $room['views'] = $newViews; + + $newRoomMapping[] = $room; + } + + $responseData['room_detailed_list'] = $newRoomMapping; + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function roomDetail($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', + //'propertyRooms.propertyRoomDefaultPhoto.propertyRoomPhoto', + //'propertyRooms.propertyRoomType', + //'propertyRooms.propertyRoomFactMapping.propertyFact', + 'propertyWebRooms.roomDetail.propertyRoomDefaultPhoto.propertyRoomPhoto', + 'propertyWebRooms.roomDetail.propertyRoomType', + 'propertyWeb', 'propertyAwardsCertificates' + ], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + + $rooms = collect($property['property_web_rooms']) + ->map(function ($value) use ($property) { + $room = $value['room_detail']; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + $room['property_room_default_photo'] = $room['property_room_default_photo']['property_room_photo']['photoUrl']; + return $room; + })->values()->all(); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => '', + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'property_contact' => $property['property_contact'], + + ]; + + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'id', 'condition' => '=', 'value' => $params['room_id']], + ], + 'with' => ['propertyRoomType', 'propertyRoomBedGroup.propertyRoomBedType', 'propertyRoomPhotoMapping.propertyRoomPhoto', 'propertyRoomFactMapping.propertyFact', 'propertyRoomViewMapping.propertyRoomViewType'], + 'firstRow' => 1 + ]; + + $propertyRooms = $this->propertyRoomService->select($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number', 'description']); + + if ($propertyRooms['status'] != 'success') { + throw new ApiErrorException($propertyRooms['message']); + } + + if (!$propertyRooms['data']) { + throw new ApiErrorException('room data not found'); + } + $room = $propertyRooms['data']; + + $bedGroups = collect($room['property_room_bed_group'])->keyBy('bed_group')->keys(); + $group = []; + foreach ($bedGroups as $bedGroup) { + $thisGroup = collect($room['property_room_bed_group'])->where('bed_group', '=', $bedGroup)->map(function ($bedItem) use ($bedGroup) { + $responseMapping = $bedItem; + $responseMapping['bed_type_name'] = $bedItem['property_room_bed_type']['name']; + unset($responseMapping['property_room_bed_type']); + return $responseMapping; + })->toArray(); + $newItem = []; + foreach ($thisGroup as $item) { + $newItem[] = $item; + } + $group[] = $newItem; + } + $room['property_room_bed_group'] = $group; + + $roomDescriptions = json_decode($room['description'], true); + $roomDescription = null; + $currentLanguage = isset($params['currentLanguage']) ? $params['currentLanguage'] : 'en'; + + if (isset($roomDescriptions[$currentLanguage]) && $roomDescriptions[$currentLanguage]) { + $roomDescription = $roomDescriptions[$currentLanguage]; + } + $room['description'] = $roomDescription; + + $roomPhotos = $room['property_room_photo_mapping']; + $newRoomPhotos = []; + + foreach ($roomPhotos as $roomPhoto) { + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_1024x768.' . $roomPhoto['property_room_photo']['file_ext']; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_200x200.' . $roomPhoto['property_room_photo']['file_ext']; + $photoItem['photo_original'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '.' . $roomPhoto['property_room_photo']['file_ext']; + + if (File::exists($photoUrlFilePath)) { + $photoItem['photo_large'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_1024x768.' . $roomPhoto['property_room_photo']['file_ext']; + } else { + $photoItem['photo_large'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_medium.' . $roomPhoto['property_room_photo']['file_ext']; + } + if (File::exists($photoUrlThumbFilePath)) { + $photoItem['photo_thumbnail'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_200x200.' . $roomPhoto['property_room_photo']['file_ext']; + } else { + $photoItem['photo_thumbnail'] = Config::get('app.imageUrl') . '/property-photos/' . $roomPhoto['property_id'] . '/' . $roomPhoto['property_room_photo']['photo_name'] . '_thumbnail.' . $roomPhoto['property_room_photo']['file_ext']; + } + $newRoomPhotos[] = $photoItem; + } + $room['default_photo'] = $newRoomPhotos ? $newRoomPhotos[0] : []; + unset($room['property_room_photo_mapping']); + $room['photos'] = $newRoomPhotos; + + $roomFacts = $room['property_room_fact_mapping']; + $newFacts = []; + foreach ($roomFacts as $fact) { + + $factItem['id'] = $fact['id']; + $factItem['fact_id'] = $fact['fact_id']; + $factItem['name'] = $fact['property_fact']['name']; + $factItem['language_key'] = $fact['property_fact']['language_key']; + $factItem['type'] = $fact['property_fact']['type']; + $factItem['order_number'] = $fact['property_fact']['order_number']; + $factItem['icon'] = $fact['property_fact']['icon']; + $newFacts[] = $factItem; + } + unset($room['property_room_photo_mapping']); + unset($room['property_room_fact_mapping']); + $room['photos'] = $newRoomPhotos; + $room['facts'] = $newFacts; + + // Room Views + $roomViews = $room['property_room_view_mapping']; + $newViews = []; + foreach ($roomViews as $view) { + $viewItem['id'] = $view['property_room_view_type']['id']; + $viewItem['name'] = $view['property_room_view_type']['name']; + $viewItem['language_key'] = $view['property_room_view_type']['language_key']; + $newViews[] = $viewItem; + } + unset($room['property_room_view_mapping']); + $room['views'] = $newViews; + + + + $room['minPrice'] = null; + $room['priceRange'] = null; + $roomRatesCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_room_id', 'condition' => '=', 'value' => $params['room_id']], + ['field' => 'amount', 'condition' => '>', 'value' => 0], + ['field' => 'date', 'condition' => '>=', 'value' => Carbon::now()->format('Y-m-d')], + ['field' => 'date', 'condition' => '<=', 'value' => Carbon::now()->addMonth()->format('Y-m-d')], + ], + 'orderBy' => [ + ['field' => 'amount', 'value' => 'ASC'] + ] + ]; + + $minRoomRatePrice = $this->propertyRoomRatePriceRepository->findbyCriteria($roomRatesCriteria, ['id', 'property_id', 'property_room_id', 'date', 'amount', 'currency']); + + $lowPrice = collect($minRoomRatePrice)->sortBy('amount')->first(); + $highPrice = collect($minRoomRatePrice)->sortByDesc('amount')->first(); + + if($lowPrice && $highPrice) { + + $room['lowPrice'] = [ + 'amount' => $lowPrice['amount'], + 'currency' => $lowPrice['currency'], + ]; + + $room['highPrice'] = [ + 'amount' => $highPrice['amount'], + 'currency' => $highPrice['currency'], + ]; + + } + + + $responseData['room_detail'] = $room; + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function contact($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyType', 'propertyChain', 'propertyExecutive.executiveType', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + + + $newExecutiveArray = []; + foreach ($property['property_executive'] as $propertyExecutive) { + + $executiveItem = [ + 'id' => $propertyExecutive['id'], + 'executive_type_name' => $propertyExecutive['executive_type']['name'], + 'executive_type_locale_key' => $propertyExecutive['executive_type']['language_key'], + 'executive_type_icon' => $propertyExecutive['executive_type']['icon'], + 'name_surname' => $propertyExecutive['name_surname'], + 'email' => $propertyExecutive['email'], + 'phone_code' => $propertyExecutive['phone_code'], + 'phone' => $propertyExecutive['phone'], + 'extension' => $propertyExecutive['extension'], + 'mobile_code' => $propertyExecutive['mobile_code'], + 'mobile' => $propertyExecutive['mobile'], + 'fax_code' => $propertyExecutive['fax_code'], + 'fax' => $propertyExecutive['fax'], + 'view_full_phone' => $propertyExecutive['view_full_phone'], + 'view_full_mobile' => $propertyExecutive['view_full_mobile'], + 'view_full_fax' => $propertyExecutive['view_full_fax'], + ]; + + if ($propertyExecutive['status'] == 1) { + $newExecutiveArray[] = $executiveItem; + } + + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $additionalInfoRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'additional_info_key_id', 'condition' => '=', 'value' => 10] + ], + 'firstRow' => true + ]; + + $myWebContactEmail = $this->propertyAdditionalInfoService->select($additionalInfoRequest); + + if ($myWebContactEmail['status'] != 'success') { + throw new ApiErrorException($myWebContactEmail['message']); + } + + $myWebContactEmail = isset($myWebContactEmail['data']['value']) && !empty($myWebContactEmail['data']['value']) ? $myWebContactEmail['data']['value'] : null; + + $property['property_contact']['myweb_contact_email'] = $myWebContactEmail; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + + + $distances = []; + $factDistancesCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['fact'] + ]; + + $factDistances = $this->propertyFactMappingService->select($factDistancesCriteria); + if ($factDistances['status'] == 'success') { + $factDistances = collect($factDistances['data'])->where('fact.parent_id', 735)->toArray(); + foreach ($factDistances as $factDistance) { + $distances[] = [ + 'name' => $factDistance['fact']['name'], + 'icon' => $factDistance['fact']['icon'], + 'language_key' => $factDistance['fact']['language_key'], + 'description' => $factDistance['descriptionArray'], + ]; + } + } + + $coverPhotos = $this->coverPhotos($params); + + + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'property_executive' => $newExecutiveArray, + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'landing_photo' => $coverPhotos, + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'property_contact' => $property['property_contact'], + 'distances' => $distances + + ]; + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function executiveDetail($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $executiveRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'id', 'condition' => '=', 'value' => $params['executive_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true, + 'with' => ['executiveType'] + ]; + + $getExecutiveFields = ['id', 'property_id', 'executive_type_id', 'name_surname', 'email', 'phone_code', 'phone', 'extension', 'mobile_code', 'mobile', 'fax_code', 'fax']; + $executive = $this->propertyExecutiveService->select($executiveRequest, $getExecutiveFields); + if ($executive['status'] != 'success' || !$executive['data']) { + throw new ApiErrorException($executive['message']); + } + + $propertyExecutive = $executive['data']; + $executiveItem = [ + 'id' => $propertyExecutive['id'], + 'executive_type_name' => $propertyExecutive['executive_type']['name'], + 'executive_type_locale_key' => $propertyExecutive['executive_type']['language_key'], + 'executive_type_icon' => $propertyExecutive['executive_type']['icon'], + 'name_surname' => $propertyExecutive['name_surname'], + 'email' => $propertyExecutive['email'], + 'phone_code' => $propertyExecutive['phone_code'], + 'phone' => $propertyExecutive['phone'], + 'extension' => $propertyExecutive['extension'], + 'mobile_code' => $propertyExecutive['mobile_code'], + 'mobile' => $propertyExecutive['mobile'], + 'fax_code' => $propertyExecutive['fax_code'], + 'fax' => $propertyExecutive['fax'], + 'view_full_phone' => $propertyExecutive['view_full_phone'], + 'view_full_mobile' => $propertyExecutive['view_full_mobile'], + 'view_full_fax' => $propertyExecutive['view_full_fax'], + ]; + + $response = [ + 'status' => true, + 'data' => $executiveItem, + ]; + } 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); + } + + public function coverPhotos($params = []) + { + + $propertyWebPhotoMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')], + ['field' => 'is_cover', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => $params['mode'] == 'preview' ? 2 : 1], + ], + 'with' => ['propertyPhoto'] + ]; + $propertyWebMappingPhotos = $this->propertyWebPhotoMappingRepository->findByCriteria($propertyWebPhotoMappingRequest); + $propertyWebMappingPhotos = $propertyWebMappingPhotos ? $propertyWebMappingPhotos : []; + $coverPhotos = []; + + foreach ($propertyWebMappingPhotos as $key => $photo) { + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_1024x768.' . $photo['property_photo']['file_ext']; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_200x200.' . $photo['property_photo']['file_ext']; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_1024x768.' . $photo['property_photo']['file_ext']; + } else { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_medium.' . $photo['property_photo']['file_ext']; + } + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_200x200.' . $photo['property_photo']['file_ext']; + } else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_thumbnail.' . $photo['property_photo']['file_ext']; + } + $coverPhotos[$key]['default'] = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '.' . $photo['property_photo']['file_ext']; + $coverPhotos[$key]['fixed'] = $photoUrlFilePath; + $coverPhotos[$key]['thumb'] = $photoUrlThumbFilePath; + } + + return $coverPhotos; + + } + + public function photos($params = []) + { + + $propertyWebPhotoMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')], + //['field' => 'is_cover', 'condition' => '=', 'value' => 0], + ['field' => 'status', 'condition' => '=', 'value' => $params['mode'] == 'preview' ? 2 : 1], + ], + 'with' => ['propertyPhoto'] + ]; + $propertyWebMappingPhotos = $this->propertyWebPhotoMappingRepository->findByCriteria($propertyWebPhotoMappingRequest); + $propertyWebMappingPhotos = $propertyWebMappingPhotos ? $propertyWebMappingPhotos : []; + $responsePhotos = []; + + foreach ($propertyWebMappingPhotos as $photo) { + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_1024x768.' . $photo['property_photo']['file_ext']; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_200x200.' . $photo['property_photo']['file_ext']; + + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_1024x768.' . $photo['property_photo']['file_ext']; + } else { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_medium.' . $photo['property_photo']['file_ext']; + } + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_200x200.' . $photo['property_photo']['file_ext']; + } else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_thumbnail.' . $photo['property_photo']['file_ext']; + } + + + $responsePhotos[] = [ + 'id' => $photo['id'], + 'photo_url_thump' => $photoUrlThumbFilePath, + 'photo_url' => $photoUrlFilePath, + 'is_cover' => $photo['is_cover'], + /*'medium_photo_size' => [ + 'width' => Image::make($photoUrlFilePath)->width(), + 'height' => Image::make($photoUrlFilePath)->height() + ] ,*/ + ]; + + } + return $responsePhotos; + + } + + private function checkWebColorMapping($params, $status) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $webColorRequestData = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'status' => $status + ]; + $webColorDatas = $this->propertyWebColorMappingService->getPropertyWebColorMappingWithStatus($webColorRequestData, ['id', 'color_code', 'order_number', 'status']); + + if ($webColorDatas['status'] != 'success') { + throw new ApiErrorException($webColorDatas['message']); + } + + $data = []; + if (!empty($webColorDatas['data']['property_web_color_mapping'])) { + foreach ($webColorDatas['data']['property_web_color_mapping'] as $key => $webColorMappingData) { + $data[$key]['color_number'] = $webColorMappingData['order_number']; + $data[$key]['color_code'] = $webColorMappingData['color_code']; + } + } + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function contactForm($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $validationResult = $this->propertyWebContactFormValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyExecutive.executiveType', 'propertyWeb'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if ($propertyAdditionalInfo['status'] != 'success') { + throw new ApiErrorException('api-unknown-error'); + } + $myWebContactEmail = $propertyAdditionalInfo['data']['myweb_contact_email']; + + if (!$myWebContactEmail) { + throw new ApiErrorException('api-unknown-error'); + } + + $bcc = []; + $mailData = [ + 'to' => [ + 'name' => $property['name'], + 'email' => $myWebContactEmail, + ], + 'bcc' => $bcc + + ]; + + $mailViewParams = $params; + $mailViewParams['logo'] = 'https://www.extranetwork.com/assets/img/logo/logo.png'; + + + $mailParams = [ + 'mailData' => $mailData, + 'mailViewParams' => $mailViewParams + ]; + + $this->mailer->onQueue( + 'contactFormMail', + new ContactFormMail($mailParams) + ); + + $response = [ + 'status' => true, + 'data' => null, + 'message' => 'myweb-contact_form-success_message', + ]; + + } 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); + } + + public function placeCategoryPlaces($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => '', + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'property_contact' => $property['property_contact'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + + ]; + + $categoryMapping = $this->propertyPlaceService->getCategoriesInList($params); + if ($categoryMapping['status'] != 'success') { + throw new ApiErrorException('api-unknown-error'); + } + + $responseData['places'] = $categoryMapping['data']['places']; + $responseData['category'] = $categoryMapping['data']['category']; + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function getPlace($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => '', + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'property_contact' => $property['property_contact'], + + ]; + + + $getPlace = $this->propertyPlaceService->getPlace($params); + if ($getPlace['status'] != 'success') { + throw new ApiErrorException('api-unknown-error'); + } + + if (isset($getPlace['data']['description']) && !is_null($getPlace['data']['description'])) { + $description = json_decode($getPlace['data']['description'], 1); + $getPlace['data']['description'] = null; + if (isset($description[$params['currentLanguage']])) { + $getPlace['data']['description'] = $description[$params['currentLanguage']]; + } + } + + $responseData['place'] = $getPlace['data']; + + //Category's Other Places + $categoryPlaces = $this->propertyPlaceService->getCategoriesInList(['property_id' => $params['property_id'], 'place_category_id' => $getPlace['data']['category']['id']]); + $responseData['categoryPlace'] = $categoryPlaces['status'] == 'success' && !empty($categoryPlaces['data']) && isset($categoryPlaces['data']['places']) ? $categoryPlaces['data']['places'] : []; + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + + } 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); + } + + public function kvkk($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => '', + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'property_contact' => $property['property_contact'], + + ]; + + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if (isset($propertyAdditionalInfo['data'])) { + $responseData['additional_info'] = collect($propertyAdditionalInfo['data'])->only(['kvkk_mersis_no', 'kvkk_data_controller', 'kvkk_contact_person', 'kvkk_addres', 'kvkk_phone', 'kvkk_tax_office', 'kvkk_tax_number', 'kvkk_email', 'kvkk_web_site', 'kvkk_contact_email', 'kvkk_contact_address'])->toArray(); + } + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function multimedia($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyType', 'propertyChain', 'propertyExecutive.executiveType', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country', 'content_code']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + + + $newExecutiveArray = []; + foreach ($property['property_executive'] as $propertyExecutive) { + + $executiveItem = [ + 'id' => $propertyExecutive['id'], + 'executive_type_name' => $propertyExecutive['executive_type']['name'], + 'executive_type_locale_key' => $propertyExecutive['executive_type']['language_key'], + 'executive_type_icon' => $propertyExecutive['executive_type']['icon'], + 'name_surname' => $propertyExecutive['name_surname'], + 'email' => $propertyExecutive['email'], + 'phone_code' => $propertyExecutive['phone_code'], + 'phone' => $propertyExecutive['phone'], + 'extension' => $propertyExecutive['extension'], + 'mobile_code' => $propertyExecutive['mobile_code'], + 'mobile' => $propertyExecutive['mobile'], + 'fax_code' => $propertyExecutive['fax_code'], + 'fax' => $propertyExecutive['fax'], + 'view_full_phone' => $propertyExecutive['view_full_phone'], + 'view_full_mobile' => $propertyExecutive['view_full_mobile'], + 'view_full_fax' => $propertyExecutive['view_full_fax'], + ]; + + if ($propertyExecutive['status'] == 1) { + $newExecutiveArray[] = $executiveItem; + } + + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $additionalInfoRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'additional_info_key_id', 'condition' => '=', 'value' => 10] + ], + 'firstRow' => true + ]; + + $myWebContactEmail = $this->propertyAdditionalInfoService->select($additionalInfoRequest); + + if ($myWebContactEmail['status'] != 'success') { + throw new ApiErrorException($myWebContactEmail['message']); + } + + $myWebContactEmail = isset($myWebContactEmail['data']['value']) && !empty($myWebContactEmail['data']['value']) ? $myWebContactEmail['data']['value'] : null; + + $property['property_contact']['myweb_contact_email'] = $myWebContactEmail; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + + + $distances = []; + $factDistancesCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['fact'] + ]; + + $factDistances = $this->propertyFactMappingService->select($factDistancesCriteria); + if ($factDistances['status'] == 'success') { + $factDistances = collect($factDistances['data'])->where('fact.parent_id', 735)->toArray(); + foreach ($factDistances as $factDistance) { + $distances[] = [ + 'name' => $factDistance['fact']['name'], + 'icon' => $factDistance['fact']['icon'], + 'language_key' => $factDistance['fact']['language_key'], + 'description' => $factDistance['descriptionArray'], + ]; + } + } + + + $responseData = [ + 'id' => $property['id'], + 'name' => $property['name'], + 'content_code' => $property['content_code'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'property_executive' => $newExecutiveArray, + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'property_contact' => $property['property_contact'], + 'distances' => $distances + + ]; + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function content($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => '', + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'property_contact' => $property['property_contact'], + + ]; + + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if (isset($propertyAdditionalInfo['data'])) { + $responseData['additional_info'] = collect($propertyAdditionalInfo['data'])->only(['kvkk_mersis_no', 'kvkk_data_controller', 'kvkk_contact_person', 'kvkk_addres', 'kvkk_phone', 'kvkk_tax_office', 'kvkk_tax_number', 'kvkk_email', 'kvkk_web_site', 'kvkk_contact_email', 'kvkk_contact_address'])->toArray(); + } + + $columns = ['id', 'property_id', 'content_category_id', 'title', 'slug', 'image', 'language_code', 'status', 'created_at']; + $propertyWebContentCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'content_category_id', 'condition' => '=', 'value' => $params['content_category_id']], + ['field' => 'language_code', 'condition' => '=', 'value' => $params['language_code']], + ], + 'with' => ['ContentCategory'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ] + ]; + + $propertyWebContent = $this->propertyWebContentRepository->findByCriteria($propertyWebContentCriteria, $columns); + + $responseData['property_content'] = $propertyWebContent; + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + + } 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); + } + + public function contentDetail($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => '', + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'property_contact' => $property['property_contact'], + + ]; + + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if (isset($propertyAdditionalInfo['data'])) { + $responseData['additional_info'] = collect($propertyAdditionalInfo['data'])->only(['kvkk_mersis_no', 'kvkk_data_controller', 'kvkk_contact_person', 'kvkk_addres', 'kvkk_phone', 'kvkk_tax_office', 'kvkk_tax_number', 'kvkk_email', 'kvkk_web_site', 'kvkk_contact_email', 'kvkk_contact_address'])->toArray(); + } + + $columns = ['id', 'property_id', 'content_category_id', 'title', 'slug', 'image', 'language_code', 'content', 'status', 'created_at']; + $propertyWebContentCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'slug', 'condition' => '=', 'value' => $params['slug']] + ], + 'with' => ['ContentCategory'], + 'firstRow' => true + ]; + + $propertyWebContent = $this->propertyWebContentRepository->findByCriteria($propertyWebContentCriteria, $columns); + + $responseData['property_content'] = $propertyWebContent; + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + + } 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); + } + + public function policy($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $responseData = []; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates.awardsCertificateCategory'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + + $rooms = collect($property['property_rooms']) + ->map(function ($value) use ($property) { + $room = $value; + $room['slug'] = Str::slug($room['name'], $separator = '-', $language = 'en'); + return $room; + })->values()->all(); + + $coverPhotos = $this->coverPhotos($params); + + $myWebServiceMode = fillOnUndefined($params, 'mode', 'live'); // preview or live + $myWebServiceModeStatus = $myWebServiceMode === "live" ? 1 : 2; + + $webColormappingRequestData = [ + 'property_id' => $property['id'], + 'property_web_id' => $property['property_web']['id'] + ]; + $webColormappingDatas = $this->checkWebColorMapping($webColormappingRequestData, $myWebServiceModeStatus); + + if (empty($webColormappingDatas['data'])) { + $property['property_brand']['color_codes'] = isset($property['property_brand']['color_codes']) ? json_decode($property['property_brand']['color_codes'], true) : json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } else { + $property['property_brand']['color_codes'] = $webColormappingDatas['data']; + } + + $property['property_brand']['title'] = isset($property['property_brand']['title']) ? $property['property_brand']['title'] : '{"en":null,"de":null,"es":null,"tr":null}'; + $property['property_brand']['logo_name'] = isset($property['property_brand']['logo_name']) ? $property['property_brand']['logo_name'] : null; + + + $propertyWebAboutUsParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'mode' => $params['mode'] === "preview" ? 2 : 1, // live : 1 preview : 2 + ]; + $propertyWebAboutUs = $this->propertyWebAboutUsService->getPropertyWebAboutUs($propertyWebAboutUsParams); + + if ($propertyWebAboutUs['status'] != 'success') { + throw new ApiErrorException($propertyWebAboutUs['message']); + } + + $aboutUs = isset($propertyWebAboutUs['data']) && !empty($propertyWebAboutUs['data']) ? $propertyWebAboutUs['data'] : []; + + $aboutUsFiltred = collect($aboutUs)->first(function ($value, $key) use ($params) { + if ($value['language_code'] == $params['currentLanguage']) { + return $value['value']; + } + }); + + $propertyAwardsCertificates = collect($property['property_awards_certificates']) + ->where('awards_certificate_category.type', '=', 'PLC') + ->where('status', '=', 1) + ->map(function ($value) { + + $return = $value; + unset($return['awards_certificate_category']); + $image = null; + + if ($value['file_path']) { + $urlPath = '/property-photos/' . $value['property_id'] . "/awards-certificates/"; + $image = Config::get('app.imageUrl') . $urlPath . $value['file_path']; + } + + + $catLogo = null; + if ($value['awards_certificate_category']['id']) { + $urlPath = '/assets/img/awards/'; + $catLogo = Config::get('app.client_server') . $urlPath . $value['awards_certificate_category']['id'] . '.png'; + } + + $return['file_path'] = $image ? $image : null; + + $category = [ + 'category_name' => $value['awards_certificate_category']['name'], + 'category_language_key' => $value['awards_certificate_category']['language_key'], + 'category_country_code' => $value['awards_certificate_category']['country_code'], + 'category_logo' => $catLogo, + 'category_type' => $value['awards_certificate_category']['type'], + 'category_status' => $value['awards_certificate_category']['status'], + ]; + return array_merge($return, $category); + })->values()->all(); + + array_multisort( + array_column($propertyAwardsCertificates, 'date'), SORT_DESC, + array_column($propertyAwardsCertificates, 'name'), SORT_ASC, + $propertyAwardsCertificates + ); + + $haveSafeCertificate = collect($property['property_awards_certificates'])->where('category_id', '=', 2)->where('status', '=', 1)->first(); + + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'about_us' => isset($aboutUsFiltred['value']) ? $aboutUsFiltred['value'] : "", + 'rooms' => $rooms, + 'property_brand' => $property['property_brand'], + 'property_contact' => $property['property_contact'], + 'landing_photo' => $coverPhotos, + 'have_safe_tourism_cert' => isset($haveSafeCertificate), + 'policy' => $propertyAwardsCertificates + + ]; + + $getPropertyFact = $this->propertyFactService->getPropertyFact($params); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $getPropertyFact = $this->getPropertyFactForHtml($getPropertyFact['data']); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $responseData['property_facts'] = $getPropertyFact['data']; + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if (isset($propertyAdditionalInfo['data'])) { + $responseData['additional_info'] = $propertyAdditionalInfo['data']; + } + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function promotion($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $responseData = []; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyPromotionMapping.propertyPromotion.promotionType', + 'propertyPromotionMapping.propertyRoomRateChannelMapping.propertyRoomRateMapping.propertyRoom', + 'propertyPromotionMapping.propertyRoomRateChannelMapping.propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation' + ], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + + + $promotionList = []; + $responseData['promotion'] = []; + + if (!empty($property['property_promotion_mapping'])) { + + //Web Channel + $propertyPromotionMapping = collect($property['property_promotion_mapping'])->where('property_room_rate_channel_mapping.channel_id', 1)->toArray(); + + foreach ($propertyPromotionMapping as $promotion) { + + + $promotionList[$promotion['property_promotion_id']]['id'] = $promotion['property_promotion']['id']; + $promotionList[$promotion['property_promotion_id']]['title'] = __($promotion['property_promotion']['promotion_type']['language_key'], [], $params['language']); + $promotionList[$promotion['property_promotion_id']]['type_code'] = $promotion['property_promotion']['promotion_type']['type_code']; + $promotionList[$promotion['property_promotion_id']]['order_number'] = $promotion['property_promotion']['promotion_type']['order_number']; + $promotionList[$promotion['property_promotion_id']]['detail'] = [ + 'start_date' => $promotion['property_promotion']['start_date'], + 'end_date' => $promotion['property_promotion']['end_date'], + 'reservation_start_date' => $promotion['property_promotion']['reservation_start_date'], + 'reservation_end_date' => $promotion['property_promotion']['reservation_end_date'], + 'is_time' => $promotion['property_promotion']['is_time'], + 'start_time' => $promotion['property_promotion']['start_time'], + 'end_time' => $promotion['property_promotion']['end_time'], + 'day_before' => $promotion['property_promotion']['day_before'], + 'amount' => $promotion['property_promotion']['amount'], + 'min_stay' => $promotion['property_promotion']['min_stay'], + 'is_mobile' => $promotion['property_promotion']['is_mobile'], + 'days' => $promotion['property_promotion']['daysArray'], + ]; + + $propertyRoomRateMapping = $promotion['property_room_rate_channel_mapping']['property_room_rate_mapping']; + + $promotionList[$promotion['property_promotion_id']]['room'][$propertyRoomRateMapping['property_room']['id']]['id'] = $propertyRoomRateMapping['property_room']['id']; + $promotionList[$promotion['property_promotion_id']]['room'][$propertyRoomRateMapping['property_room']['id']]['title'] = $propertyRoomRateMapping['property_room']['name']; + + $promotionList[$promotion['property_promotion_id']]['room'][$propertyRoomRateMapping['property_room']['id']]['rate'][$propertyRoomRateMapping['property_room_rate']['id']]['id'] = $propertyRoomRateMapping['property_room_rate']['id']; + $promotionList[$promotion['property_promotion_id']]['room'][$propertyRoomRateMapping['property_room']['id']]['rate'][$propertyRoomRateMapping['property_room_rate']['id']]['title'] = $propertyRoomRateMapping['property_room_rate']['name']; + $promotionList[$promotion['property_promotion_id']]['room'][$propertyRoomRateMapping['property_room']['id']]['rate'][$propertyRoomRateMapping['property_room_rate']['id']]['accommodation'] = __($propertyRoomRateMapping['property_room_rate']['property_room_rate_accommodation']['language_key'], [], $params['language']); + + //dd($promotionList, $promotion); + + } + + + foreach ($promotionList as $promotionKey => $promotion) { + foreach ($promotion['room'] as $promotionRoomKey => $promotionRoom) { + $promotionList[$promotionKey]['room'][$promotionRoomKey]['rate'] = array_values($promotionList[$promotionKey]['room'][$promotionRoomKey]['rate']); + } + $promotionList[$promotionKey]['room'] = array_values($promotionList[$promotionKey]['room']); + } + + $promotionList = array_values($promotionList); + + $responseData['promotion'] = $promotionList; + + } + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function affiliateRequestMail($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact', 'propertyExecutive.executiveType', 'propertyWeb'], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + if (!$property) { + throw new ApiErrorException('property not found'); + } + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if ($propertyAdditionalInfo['status'] != 'success') { + throw new ApiErrorException('api-unknown-error'); + } + $myWebContactEmail = $propertyAdditionalInfo['data']['myweb_contact_email']; + + if (!$myWebContactEmail) { + throw new ApiErrorException('api-unknown-error'); + } + + $propertyExecutives = collect($property['property_executive'])->where('status', 1)->where('executive_type_id', 8)->pluck('email')->toArray(); + + + $bcc = []; + $mailData = [ + 'to' => [ + 'name' => $property['name'], + 'email' => $myWebContactEmail, + ], + 'bcc' => $propertyExecutives + + ]; + + $mailViewParams = $params; + $mailViewParams['logo'] = 'https://www.extranetwork.com/assets/img/logo/logo.png'; + + $mailParams = [ + 'mailData' => $mailData, + 'mailViewParams' => $mailViewParams + ]; + + + $this->mailer->onQueue( + 'affiliateRequestMail', + new AffiliateRequestMail($mailParams) + ); + + + $response = [ + 'status' => true, + ]; + + } 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); + } + + +} diff --git a/app/Core/Service/NewBookingMailService.php b/app/Core/Service/NewBookingMailService.php new file mode 100644 index 0000000..b1e24bc --- /dev/null +++ b/app/Core/Service/NewBookingMailService.php @@ -0,0 +1,364 @@ +mailer = $mailer; + $this->bookingRepository = $bookingRepository; + $this->userPropertyMappingRepository = $userPropertyMappingRepository; + $this->propertyBrandService = $propertyBrandService; + + } + + public function process($params = []) + { + try { + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['booking_id']] + ], + 'with' => [ + 'bookingPayment', 'bookingRoom.roomRateMapping.propertyRoom', 'bookingRoom.roomRateMapping.propertyRoomRate', + 'bookingContact', 'bookingProperty.propertyBrand', 'bookingProperty.propertyExecutive', + 'bookingPropertyWeb', 'propertyBookingEngine', 'propertyBookingChannel', 'bookingAddon.propertyChannelAddon.propertyAddon.fact', + 'propertyChannelMapping.channelBookingPaymentType' + ], + 'firstRow' => true + ]; + + $bookingData = $this->bookingRepository->findByCriteria($bookingDetailParam); + if (!$bookingData) { + throw new Exception('api-unknown_error'); + } + + $reservationExecutives = []; + if (isset($bookingData['booking_property']['property_executive'])) { + $reservationExecutives = collect($bookingData['booking_property']['property_executive']) + ->where('executive_type_id', '=', '7') // sadece rezervasyon yetkisi olanlar ... + ->values()->all(); + } + + $userPropertyMappingParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingData['property_id']] + ], + 'with' => ['user'] + ]; + $userData = $this->userPropertyMappingRepository->findByCriteria($userPropertyMappingParam); + + $bcc = []; + foreach ($userData as $user) { + if (!empty($user['user'])) { + $bcc[] = $user['user']['email']; + } + } + + + foreach ($reservationExecutives as $reservationExecutive) { + $bcc[] = $reservationExecutive['email']; + } + + if (in_array($bookingData['property_booking_channel']['channel_category_id'], [3])) { + $bcc[] = "sales@extranetwork.com"; + } else { + $bcc[] = "channel@extranetwork.com"; + } + + + //ENW kanalı harici tüm işlemlerde giden kısmı otel kullanıcısı olacak. + if (in_array($bookingData['property_booking_channel']['channel_category_id'], [4, 7])) { + $firstUserData = reset($userData); + $mailData['to'] = [ + 'name' => $bookingData['booking_property']['name'], + 'email' => $firstUserData['user']['email'] + ]; + } else { + $mailData['to'] = [ + 'name' => $bookingData['booking_contact']['nameSurname'], + 'email' => $bookingData['booking_contact']['email'] + ]; + } + + $mailData['bcc'] = $bcc; + + $hostAddress = Config::get('app.bookingEngineUrl') . '/' . $bookingData['property_booking_engine']['token'] . '/' . $bookingData['booking_contact']['language_code'] . '/booking-detail/' . $bookingData['booking_code']; + + $brandRequestData = ['property_id' => $bookingData['property_id']]; + $getPropertyBrand = $this->propertyBrandService->getPropertyBrand($brandRequestData); + if ($getPropertyBrand['status'] != "success") { + throw new Exception($getPropertyBrand['message']); + } + + $propertyBrand = $getPropertyBrand['data']; + $propertyBrandLogo = isset($propertyBrand['name']) + ? Config::get('app.fileSystemDriver') . '/property-photos/' . $propertyBrand['property_id'] . "/logo/" . $propertyBrand['name'] . '_250x250.' . $propertyBrand['logo_file_ext'] + : null; + + $roomOrder = 1; + $bookingChannelRoom = []; + foreach ($bookingData['booking_room'] as $bookingRoom) { + + if (!empty($bookingRoom['room_rate_mapping']['property_room'])) { + $bookingRoom['room_name'] = $bookingRoom['room_rate_mapping']['property_room']['name']; + } + if (!empty($bookingRoom['room_rate_mapping']['property_room_rate'])) { + $bookingRoom['room_rate_name'] = $bookingRoom['room_rate_mapping']['property_room_rate']['name']; + } + + $extraParam = null; + if (!empty($bookingRoom['extra_param'])) { + $extraParamDecode = json_decode($bookingRoom['extra_param'], 1); + + foreach ($extraParamDecode as $extraParamKey => $extraParamValue) { + + $extraParamTitle = explode('_', $extraParamKey); + foreach ($extraParamTitle as $extraParamTitleKey => $extraParamTitleValue) { + $extraParamTitle[$extraParamTitleKey] = ucwords($extraParamTitleValue); + } + $extraParamTitle = implode(' ', $extraParamTitle); + + $extraParam[$extraParamKey] = [ + 'title' => $extraParamTitle, + 'value' => $extraParamValue, + ]; + } + + } + + $additionalFee = null; + if (!empty($bookingRoom['rate_detail'])) { + $rateDetail = json_decode($bookingRoom['rate_detail'], 1); + if (isset($rateDetail['taxes']) && $bookingData['channel_manager_id'] == 2) { + $additionalFee = $rateDetail['taxes']; + } + } + + //$cancellationPolicy + $cancellationPolicy = null; + $cancellationPolicyRoom = json_decode($bookingRoom['cancellation_policy'], 1); + if ($cancellationPolicyRoom) { + $cancellationPolicyTextFormatted = cancellationPolicyTextFormatted( + fillOnUndefined($cancellationPolicyRoom, 'isNonRefundable'), + fillOnUndefined($cancellationPolicyRoom, 'isFreeCancellation'), + fillOnUndefined($cancellationPolicyRoom, 'beforeArrivalDay'), + fillOnUndefined($cancellationPolicyRoom, 'type'), + fillOnUndefined($cancellationPolicyRoom, 'value'), + $bookingData['currency_code'], + $bookingData['booking_contact']['language_code'] + ); + + if ($cancellationPolicyTextFormatted) { + $cancellationPolicy = $cancellationPolicyTextFormatted; + } + } + //$cancellationPolicy + + $bookingChannelRoom[] = [ + 'roomOrder' => $roomOrder, + 'roomName' => $bookingRoom['room_name'], + 'roomRateName' => $bookingRoom['room_rate_name'], + 'occupancyCode' => $bookingRoom['occupancy_code'], + 'occupancyText' => occupancyCodeFormatted($bookingRoom['occupancy_code']), + 'amount' => $bookingRoom['amount'], + 'discount_amount' => $bookingRoom['discount_amount'], + 'total' => $bookingRoom['total'], + 'currencyCode' => $bookingRoom['currency_code'], + 'dailyAmount' => !empty($bookingRoom['daily_amount']) ? json_decode($bookingRoom['daily_amount'], 1) : null, + 'extraParam' => $extraParam, + 'occupancyFormatted' => occupancyCodeFormatted($bookingRoom['occupancy_code']), + 'additionalFee' => $additionalFee, + 'cancellationPolicy' => $cancellationPolicy + ]; + + //dd($bookingChannelRoom); + + //$bookingChannelRoom[] = $roomOrder . '. ' . $bookingRoom['room_name'] . ' - ' . $bookingRoom['room_rate_name'] . ' - ' . occupancyCodeFormatted($bookingRoom['occupancy_code']); + $roomOrder++; + } + + //$bookingChannelRoom = implode('
', $bookingChannelRoom); + + $bookingLanguageCode = isset($bookingData['booking_contact']['language_code']) ? $bookingData['booking_contact']['language_code'] : 'en'; + $bookingChannelPaymentType = __('property_booking_payment_type-pay_at_hotel', [], $bookingLanguageCode); + $bookingChannelPaymentSource = null; + if ($bookingData['booking_payment']['payment_type_code'] == 'CRD') { + $bookingChannelPaymentType = __('property_booking_payment_type-credit_card', [], $bookingLanguageCode); + if ($bookingData['booking_payment']['payment_source_code'] == 'OTA') { + $bookingChannelPaymentSource = __('enw-ota-credit_card', [], $bookingLanguageCode); + } + if ($bookingData['booking_payment']['payment_source_code'] == 'GST') { + $bookingChannelPaymentSource = __('enw-guest-credit_card', [], $bookingLanguageCode); + } + } + + $creditCardInformation = null; + if (!is_null($bookingData['booking_payment']['extra_param'])) { + $paymentData = json_decode($bookingData['booking_payment']['extra_param'], 1); + + if (isset($paymentData['attributes']['guarantee'])) { + $creditCardInformation['cardNumber'] = isset($paymentData['attributes']['guarantee']['card_number']) ? $paymentData['attributes']['guarantee']['card_number'] : null; + $creditCardInformation['cardHolderName'] = isset($paymentData['attributes']['guarantee']['cardholder_name']) ? $paymentData['attributes']['guarantee']['cardholder_name'] : null; + $creditCardInformation['expirationDate'] = isset($paymentData['attributes']['guarantee']['expiration_date']) ? $paymentData['attributes']['guarantee']['expiration_date'] : null; + $creditCardInformation['cvv'] = isset($paymentData['attributes']['guarantee']['cvv']) ? $paymentData['attributes']['guarantee']['cvv'] : null; + } + } + + //Genius Member + $bookingContactExtraParam = null; + if (!empty($bookingData['booking_contact']['extra_param'])) { + $bookingContactExtraParam = json_decode($bookingData['booking_contact']['extra_param'], 1); + } + $isBookingGenius = false; + if (isset($bookingContactExtraParam['is_genius']) && $bookingContactExtraParam['is_genius']) { + $isBookingGenius = true; + } + + + //BOOKING ADDON + $bookingAddonList = []; + $bookingAddons = $bookingData['booking_addon']; + foreach ($bookingAddons as $bookingAddon) { + + $bookingAddonAttributeList = []; + $isHasAttribute = false; + $attributeArray = []; + + if (!empty($bookingAddon['property_channel_addon']['property_addon']['attributeArray'])) { + $isHasAttribute = true; + $attributeArray = $bookingAddon['property_channel_addon']['property_addon']['attributeArray']; + $bookingAddonAttributes = json_decode($bookingAddon['attribute'], 1); + if (!is_null($bookingAddonAttributes)) { + foreach ($bookingAddonAttributes as $key => $bookingAddonAttribute) { + foreach ($bookingAddonAttribute as $bookingAddonAttributeKey => $bookingAddonAttributeValue) { + + if (isset($bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey])) { + $bookingAddonAttributeList[] = [ + 'key' => $bookingAddonAttributeKey, + 'name' => $bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey]['name'], + 'language_key' => $bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey]['language_key'], + 'value' => $bookingAddonAttributeValue, + ]; + } + + } + } + } + } + + $bookingAddonList[] = [ + 'id' => $bookingAddon['id'], + 'booking_id' => $bookingAddon['booking_id'], + 'property_channel_addon_id' => $bookingAddon['property_channel_addon_id'], + 'count' => $bookingAddon['count'], + 'amount' => $bookingAddon['amount'], + 'total' => $bookingAddon['total'], + 'currency_code' => $bookingAddon['currency_code'], + 'name' => $bookingAddon['property_channel_addon']['property_addon']['fact']['name'], + 'title' => $bookingAddon['property_channel_addon']['title'], + 'language_key' => $bookingAddon['property_channel_addon']['property_addon']['fact']['language_key'], + 'icon' => $bookingAddon['property_channel_addon']['property_addon']['fact']['icon'], + 'isHasAttribute' => $isHasAttribute, + 'attributeArray' => $attributeArray, + 'attribute' => $bookingAddonAttributeList + ]; + + } + + $payAtHotelCreditCardCheck = false; + if(isset($bookingData['property_channel_mapping']) && !empty($bookingData['property_channel_mapping'])) { + $payAtHotelCreditCardCheck = collect($bookingData['property_channel_mapping']['channel_booking_payment_type'])->where('payment_type_id',2)->where('is_get_payment_data',1)->isNotEmpty(); + } + + $mailViewParams = [ + 'bookingId' => $bookingData['id'], + 'bookingCode' => $bookingData['booking_code'], + 'propertyId' => $bookingData['booking_property']['id'], + 'booking_code' => $bookingData['booking_code'], + 'search_key' => $bookingData['search_key'], + 'channel_token' => $bookingData['channel_token'], + 'booking_engine_token' => $bookingData['booking_engine_token'], + 'checkin_date' => Carbon::parse($bookingData['checkin_date'])->format('d.m.Y'), + 'checkout_date' => Carbon::parse($bookingData['checkout_date'])->format('d.m.Y'), + 'payment_type_code' => $bookingData['booking_payment']['payment_type_code'], + 'payment_source_code' => $bookingData['booking_payment']['payment_source_code'], + 'room_amount' => $bookingData['room_amount'], + 'addon_amount' => $bookingData['addon_amount'], + 'discount_amount' => $bookingData['discount_amount'], + 'total' => $bookingData['total'], + 'currency_code' => $bookingData['currency_code'], + 'name_surname' => $bookingData['booking_contact']['nameSurname'], + 'countryCode' => upperCase($bookingData['booking_contact']['country_code']), + 'email' => $bookingData['booking_contact']['email'], + 'property_name' => $bookingData['booking_property']['name'], + 'url' => $hostAddress, + 'logo' => $bookingData['booking_property']['property_brand']['logoUrl'], + 'language_code' => $bookingData['booking_contact']['language_code'], + 'bookingChannelId' => $bookingData['channel_id'], + 'bookingChannelName' => fillOnUndefined($bookingData['property_booking_channel'], 'name'), + 'bookingChannelCategoryId' => $bookingData['property_booking_channel']['channel_category_id'], + 'bookingChannelCode' => $bookingData['channel_booking_code'], + 'bookingChannelContactNameSurname' => $bookingData['booking_contact']['nameSurname'], + 'bookingChannelContactEmail' => $bookingData['booking_contact']['email'], + 'bookingChannelContactPhone' => $bookingData['booking_contact']['phone_number'], + 'bookingChannelNote' => $bookingData['booking_contact']['note'], + 'bookingChannelRoom' => $bookingChannelRoom, + 'bookingAddon' => $bookingAddonList, + 'bookingChannelPaymentType' => $bookingChannelPaymentType, + 'bookingChannelPaymentSource' => $bookingChannelPaymentSource, + 'creditCardInformation' => $creditCardInformation, + 'payAtHotelCreditCardCheck' => $payAtHotelCreditCardCheck, + 'isBookingGenius' => $isBookingGenius, + 'web' => isset($bookingData['booking_property_web']['webProtocolUrl']) ? $bookingData['booking_property_web']['webProtocolUrl'] : null + ]; + + $mailViewParams['showCreditCardUrl'] = null; + if (!empty($mailViewParams['creditCardInformation'])) { + $mailViewParams['showCreditCardUrl'] = Config::get('app.client_server') . '/app/network/reservation/' . $mailViewParams['bookingId'] . '?propertyid=' . $mailViewParams['propertyId']; + } + + $mailParams = [ + 'mailData' => $mailData, + 'mailViewParams' => $mailViewParams + + ]; + + $this->mailer->onQueue('newBookingMail', new NewBookingMail($mailParams)); + + + } catch + (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + return output(['status' => -1, 'message' => $e->getMessage()]); + } + } + +} diff --git a/app/Core/Service/NotificationService.php b/app/Core/Service/NotificationService.php new file mode 100644 index 0000000..82a332a --- /dev/null +++ b/app/Core/Service/NotificationService.php @@ -0,0 +1,162 @@ +bookingRepository = $bookingRepository; + $this->propertyRepository = $propertyRepository; + $this->language = $languageService::getDefaultLang(); + $this->validLanguages = $languageService::getValidLanguages(); + } + + public function sendLogNotification($params = []) + { + + $response = ['status' => false, 'message' => '']; + try { + + + $propertyDetailParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'firstRow' => true + ]; + + $propertyData = $this->propertyRepository->findByCriteria($propertyDetailParam); + + if (!$propertyData) { + throw new Exception('api-unknown_error'); + } + + $param = [ + 'property_id' => $params['property_id'], + 'subject' => 'Otel Bilgileri Güncelleme', + 'message' => $propertyData['name'] . ', Otel Bilgileri Güncellendi.' + ]; + + Notification::route('OneSignal', null)->notify(new PushNotificationPropertyUser($param)); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + public function sendNewBookingNotification($params = []) + { + + $response = ['status' => false, 'message' => '']; + try { + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['booking_id']] + ], + 'with' => ['bookingPayment', 'bookingProperty'], + 'firstRow' => true + ]; + + $bookingData = $this->bookingRepository->findByCriteria($bookingDetailParam); + + if (!$bookingData) { + throw new Exception('api-unknown_error'); + } + + if (in_array(strtolower($bookingData['booking_property']['country']), $this->validLanguages)) { + $this->language = strtolower($bookingData['booking_property']['country']); + } + + $param = [ + 'property_id' => $bookingData['booking_property']['id'], + 'subject' => '🛎️ '.__('notification-new_booking', [], $this->language), + 'message' => __('notification-new_booking_desc', ['hotel_name' => $bookingData['booking_property']['name'], 'amount' => $bookingData['total'], 'currency' => $bookingData['currency_code']], $this->language), + ]; + + Notification::route('OneSignal', null)->notify(new PushNotificationPropertyUser($param)); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + public function sendTicketNotification($params = []) + { + + $response = ['status' => false, 'message' => '']; + try { + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['booking_id']] + ], + 'with' => ['bookingPayment', 'bookingProperty'], + 'firstRow' => true + ]; + + $bookingData = $this->bookingRepository->findByCriteria($bookingDetailParam); + + if (!$bookingData) { + throw new Exception('api-unknown_error'); + } + + if (in_array(strtolower($bookingData['booking_property']['country']), $this->validLanguages)) { + $this->language = strtolower($bookingData['booking_property']['country']); + } + + $param = [ + 'property_id' => $bookingData['booking_property']['id'], + 'subject' => '📥 '.__('notification-new_ticket', [], $this->language), + 'message' => __('notification-new_ticket_desc', [ + 'hotel' => $bookingData['booking_property']['name'], + 'bookingCode' => $bookingData['booking_code'] + ], $this->language), + ]; + + Notification::route('OneSignal', null)->notify(new PushNotificationPropertyUser($param)); + + $response['status'] = true; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + +} diff --git a/app/Core/Service/OfferAccommodationMappingService.php b/app/Core/Service/OfferAccommodationMappingService.php new file mode 100644 index 0000000..0e07725 --- /dev/null +++ b/app/Core/Service/OfferAccommodationMappingService.php @@ -0,0 +1,282 @@ +offerAccommodationMappingRepository = $offerAccommodationMappingRepository; + $this->offerAccommodationMappingCreateValidator = $offerAccommodationMappingCreateValidator; + $this->offerAccommodationMappingAddValidator = $offerAccommodationMappingAddValidator; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerAccommodationMappingCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_accommodation_id" => fillOnUndefined($param, "property_accommodation_id"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->offerAccommodationMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerAccommodationMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerAccommodationMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerAccommodationMappingRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + + public function insertAccommodationMapping($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerAccommodationMappingAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $insertDataArray = [] ; + foreach ($param['accommodation_mapping'] as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_accommodation_id" => $item, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $offerContactInsertResult = $this->offerAccommodationMappingRepository->createAll($insertDataArray); + if ($offerContactInsertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $offerContactInsertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function updateAccommodationMapping($param = [], $offerAccommodationMapping) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerAccommodationMappingAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertThisIds = array_diff($param['accommodation_mapping'], $offerAccommodationMapping); + $deleteThisIds = array_diff($offerAccommodationMapping, $param['accommodation_mapping']); + $responseDeleteIds = $deleteThisIds; + + + + $insertDataArray = [] ; + foreach ($insertThisIds as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_accommodation_id" => $item, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $offerContactInsertResult = $this->offerAccommodationMappingRepository->createAll($insertDataArray); + if ($offerContactInsertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $requestData = [ + 'criteria' => [ + ['field' => 'offer_id', 'condition' => '=', 'value' => fillOnUndefined($param, "offer_id")], + ], + "whereIn" => + [ + ["field" => "property_accommodation_id", "value" => $deleteThisIds] + ] + ]; + $eraseMappingData = $this->offerAccommodationMappingRepository->findByCriteria($requestData, ['id']); + $eraseMappingData = $eraseMappingData ? $eraseMappingData : [] ; + $deleteThisIds = collect($eraseMappingData)->keyBy('id')->keys()->all(); + + if($deleteThisIds){ + $deleteThisArray = $this->offerAccommodationMappingRepository->destroy($deleteThisIds); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + $response = [ + 'status' => true, + 'data' => $responseDeleteIds, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + +} diff --git a/app/Core/Service/OfferCancellationPolicyService.php b/app/Core/Service/OfferCancellationPolicyService.php new file mode 100644 index 0000000..96b5f1e --- /dev/null +++ b/app/Core/Service/OfferCancellationPolicyService.php @@ -0,0 +1,175 @@ +offerCancellationPolicyRepository = $offerCancellationPolicyRepository; + $this->offerCancellationPolicyCreateValidator = $offerCancellationPolicyCreateValidator; + } + + public function insertCancellationPolicy($param = [], $oldOfferCancellationPolicy) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $validationResult = $this->offerCancellationPolicyCreateValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $cancellationPolicies = $param['cancellation_policy']; + $cancellationPolicyData = []; + $userData = [] ; + + if($param['has_cancellation_policy'] == true){ + foreach ($cancellationPolicies as $cancellationPolicy){ + + $cancellationPolicyData[] = [ + + "offer_id" => $param['offer_id'], + "days_before" => fillOnUndefined($cancellationPolicy, "days_before"), + "type" => fillOnUndefined($cancellationPolicy, "type"), + "value" => fillOnUndefined($cancellationPolicy, "value"), + "status" => fillOnUndefined($cancellationPolicy, "status",1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time() + ]; + } + + $userCreateResult = $this->offerCancellationPolicyRepository->createAll($cancellationPolicyData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + + } + + + if($oldOfferCancellationPolicy){ + $deleteThisArray = $this->offerCancellationPolicyRepository->destroy($oldOfferCancellationPolicy); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerCancellationPolicyRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerCancellationPolicyRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerCancellationPolicyRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + +} diff --git a/app/Core/Service/OfferContactMappingService.php b/app/Core/Service/OfferContactMappingService.php new file mode 100644 index 0000000..21de590 --- /dev/null +++ b/app/Core/Service/OfferContactMappingService.php @@ -0,0 +1,279 @@ +offerContactMappingRepository = $offerContactMappingRepository; + $this->offerContactMappingCreateValidator = $offerContactMappingCreateValidator; + $this->offerContactMappingAddValidator = $offerContactMappingAddValidator; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerContactMappingCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_executive_id" => fillOnUndefined($param, "property_executive_id"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->offerContactMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerContactMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerContactMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerContactMappingRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function insertContactMapping($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerContactMappingAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $insertDataArray = [] ; + foreach ($param['contact_mapping'] as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_executive_id" => $item, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $offerContactInsertResult = $this->offerContactMappingRepository->createAll($insertDataArray); + if ($offerContactInsertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $offerContactInsertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function updateContactMapping($param = [], $offerContactMapping) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerContactMappingAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertThisIds = array_diff($param['contact_mapping'], $offerContactMapping); + $deleteThisIds = array_diff($offerContactMapping, $param['contact_mapping']); + + $insertDataArray = [] ; + foreach ($insertThisIds as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_executive_id" => $item, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $offerContactInsertResult = $this->offerContactMappingRepository->createAll($insertDataArray); + if ($offerContactInsertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $requestData = [ + 'criteria' => [ + ['field' => 'offer_id', 'condition' => '=', 'value' => fillOnUndefined($param, "offer_id")], + ], + "whereIn" => + [ + ["field" => "property_executive_id", "value" => $deleteThisIds] + ] + ]; + $eraseMappingData = $this->offerContactMappingRepository->findByCriteria($requestData, ['id']); + $eraseMappingData = $eraseMappingData ? $eraseMappingData : [] ; + $deleteThisIds = collect($eraseMappingData)->keyBy('id')->keys()->all(); + + if($deleteThisIds){ + $deleteThisArray = $this->offerContactMappingRepository->destroy($deleteThisIds); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $userData = $offerContactInsertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + +} diff --git a/app/Core/Service/OfferFactMappingService.php b/app/Core/Service/OfferFactMappingService.php new file mode 100644 index 0000000..a25bf88 --- /dev/null +++ b/app/Core/Service/OfferFactMappingService.php @@ -0,0 +1,338 @@ +offerFactMappingRepository = $offerFactMappingRepository; + $this->offerFactMappingCreateValidator = $offerFactMappingCreateValidator; + $this->offerFactMappingAddValidator = $offerFactMappingAddValidator; + $this->propertyFactService = $propertyFactService; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerFactMappingCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "category_id" => fillOnUndefined($param, "category_id"), + "property_fact_parent_id" => fillOnUndefined($param, "property_fact_parent_id"), + "property_fact_id" => fillOnUndefined($param, "property_fact_id"), + + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->offerFactMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerFactMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerFactMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerFactMappingRepository->updateOrCreate($criteria, $saveData); + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function insertNewOfferFacts($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerFactMappingAddValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $propertyFactsCheckParam = [ + 'fact_ids' => $param['fact_mapping'], + 'property_id' => $param['property_id'] + ]; + $propertyFactsCheck = $this->propertyFactService->checkPropertyFactMapping($propertyFactsCheckParam); + if($propertyFactsCheck['status'] != 'success'){ + throw new ApiErrorException($propertyFactsCheck['message']); + } + + $requestData = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $factData = $this->propertyFactService->select($requestData); + + if ($factData['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $facts = collect($factData['data'])->keyBy('id'); + $insertDataArray = [] ; + + if(!empty($param['hotel_features_mapping'])){ + $param['fact_mapping'] = array_merge($param['fact_mapping'], $param['hotel_features_mapping']); + } + + foreach ($param['fact_mapping'] as $fact) { + $theFact = $facts[$fact]; + $parentFact = $facts[$theFact['parent_id']]; + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "category_id" => $parentFact['parent_id'], + "property_fact_parent_id" => $theFact['parent_id'], + "property_fact_id" =>$fact, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $offerFactInsertResult = $this->offerFactMappingRepository->createAll($insertDataArray); + if ($offerFactInsertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $offerFactInsertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function updateOfferFacts($param = [], $offerAllFactIds = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerFactMappingAddValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + if(!empty($param['hotel_features_mapping'])){ + $param['fact_mapping'] = array_merge($param['fact_mapping'], $param['hotel_features_mapping']); + } + + $insertThisIds = array_diff($param['fact_mapping'], $offerAllFactIds); + $deleteThisIds = array_diff($offerAllFactIds, $param['fact_mapping']); + + + $requestData = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $factData = $this->propertyFactService->select($requestData); + + if ($factData['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $facts = collect($factData['data'])->keyBy('id'); + $insertDataArray = [] ; + + + foreach ($insertThisIds as $fact) { + $theFact = $facts[$fact]; + $parentFact = $facts[$theFact['parent_id']]; + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "category_id" => $parentFact['parent_id'], + "property_fact_parent_id" => $theFact['parent_id'], + "property_fact_id" =>$fact, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $offerFactInsertResult = $this->offerFactMappingRepository->createAll($insertDataArray); + if ($offerFactInsertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + + $requestData = [ + 'criteria' => [ + ['field' => 'offer_id', 'condition' => '=', 'value' => fillOnUndefined($param, "offer_id")], + ], + "whereIn" => + [ + ["field" => "property_fact_id", "value" => $deleteThisIds] + ] + ]; + $factEraseMappingData = $this->offerFactMappingRepository->findByCriteria($requestData, ['id']); + $factEraseMappingData = $factEraseMappingData ? $factEraseMappingData : [] ; + $deleteThisIds = collect($factEraseMappingData)->keyBy('id')->keys()->all(); + + if($deleteThisIds){ + $deleteThisArray = $this->offerFactMappingRepository->destroy($deleteThisIds); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $userData = $offerFactInsertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + +} diff --git a/app/Core/Service/OfferImportantNotesService.php b/app/Core/Service/OfferImportantNotesService.php new file mode 100644 index 0000000..dbc1207 --- /dev/null +++ b/app/Core/Service/OfferImportantNotesService.php @@ -0,0 +1,219 @@ +offerImportantNotesRepository = $offerImportantNotesRepository; + $this->offerImportantNotesCreateValidator = $offerImportantNotesCreateValidator; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerImportantNotesCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "note" => fillOnUndefined($param, "note"), + + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->offerImportantNotesRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerImportantNotesRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerImportantNotesRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerImportantNotesRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + + public function insertImportantNotes($param = [], $oldOfferImportantNotes) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerImportantNotesCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertDataArray = [] ; + foreach ($param['important_notes'] as $item) { + if(!$item){ + continue; + } + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "note" => $item, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $insertResult = $this->offerImportantNotesRepository->createAll($insertDataArray); + if ($insertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + + if($oldOfferImportantNotes){ + $deleteThisArray = $this->offerImportantNotesRepository->destroy($oldOfferImportantNotes); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $userData = $insertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + + +} diff --git a/app/Core/Service/OfferPaymentTypeService.php b/app/Core/Service/OfferPaymentTypeService.php new file mode 100644 index 0000000..e445d92 --- /dev/null +++ b/app/Core/Service/OfferPaymentTypeService.php @@ -0,0 +1,155 @@ +offerPaymentTypeRepository = $offerPaymentTypeRepository; + $this->offerPaymentTypeCreateValidator = $offerPaymentTypeCreateValidator; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerPaymentTypeCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + "name" => fillOnUndefined($param, "name"), + + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->offerPaymentTypeRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerPaymentTypeRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerPaymentTypeRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerPaymentTypeRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + +} diff --git a/app/Core/Service/OfferPhotoMappingService.php b/app/Core/Service/OfferPhotoMappingService.php new file mode 100644 index 0000000..edb0180 --- /dev/null +++ b/app/Core/Service/OfferPhotoMappingService.php @@ -0,0 +1,449 @@ +offerPhotoMappingRepository = $offerPhotoMappingRepository; + $this->offerPhotoMappingCreateValidator = $offerPhotoMappingCreateValidator; + $this->offerPhotoMappingAddValidator = $offerPhotoMappingAddValidator; + $this->offerCoverPhotoAddValidator = $offerCoverPhotoAddValidator; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerPhotoMappingCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_photo_id" => fillOnUndefined($param, "property_photo_id"), + + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->offerPhotoMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerPhotoMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerPhotoMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerPhotoMappingRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function insertPhotoMapping($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerPhotoMappingAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $insertDataArray = [] ; + foreach ($param['photo_mapping'] as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_photo_id" => $item, + "is_cover" => 0, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + $insertResult = $this->offerPhotoMappingRepository->createAll($insertDataArray); + if ($insertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $insertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function insertCoverPhotoMapping($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerCoverPhotoAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $insertDataArray = [] ; + foreach ($param['cover_photos'] as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_photo_id" => $item, + "is_cover" => 1, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + $insertResult = $this->offerPhotoMappingRepository->createAll($insertDataArray); + if ($insertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $insertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function updatePhotoMapping($param = [], $offerPhotoMapping) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerPhotoMappingAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertThisIds = array_diff($param['photo_mapping'], $offerPhotoMapping); + $deleteThisIds = array_diff($offerPhotoMapping, $param['photo_mapping']); + + $insertDataArray = [] ; + foreach ($insertThisIds as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_photo_id" => $item, + "is_cover" => 0, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + $insertResult = $this->offerPhotoMappingRepository->createAll($insertDataArray); + if ($insertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + + $requestData = [ + 'criteria' => [ + ['field' => 'offer_id', 'condition' => '=', 'value' => fillOnUndefined($param, "offer_id")], + ], + "whereIn" => + [ + ["field" => "property_photo_id", "value" => $deleteThisIds] + ] + ]; + $eraseMappingData = $this->offerPhotoMappingRepository->findByCriteria($requestData, ['id']); + $eraseMappingData = $eraseMappingData ? $eraseMappingData : [] ; + $deleteThisIds = collect($eraseMappingData)->keyBy('id')->keys()->all(); + + if($deleteThisIds){ + $deleteThisArray = $this->offerPhotoMappingRepository->destroy($deleteThisIds); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + + $userData = $insertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function updateCoverPhotoMapping($param = [], $offerCoverPhotoMapping) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $validationResult = $this->offerCoverPhotoAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $insertThisIds = array_diff($param['cover_photos'], $offerCoverPhotoMapping); + $deleteThisIds = array_diff($offerCoverPhotoMapping, $param['cover_photos']); + + + $insertDataArray = [] ; + foreach ($insertThisIds as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_photo_id" => $item, + "is_cover" => 1, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + $insertResult = $this->offerPhotoMappingRepository->createAll($insertDataArray); + if ($insertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + + $requestData = [ + 'criteria' => [ + ['field' => 'offer_id', 'condition' => '=', 'value' => fillOnUndefined($param, "offer_id")], + ], + "whereIn" => + [ + ["field" => "property_photo_id", "value" => $deleteThisIds] + ] + ]; + $eraseMappingData = $this->offerPhotoMappingRepository->findByCriteria($requestData, ['id']); + $eraseMappingData = $eraseMappingData ? $eraseMappingData : [] ; + $deleteThisIds = collect($eraseMappingData)->keyBy('id')->keys()->all(); + + if($deleteThisIds){ + $deleteThisArray = $this->offerPhotoMappingRepository->destroy($deleteThisIds); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + + $userData = $insertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function destroy($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $deleteCriteria = [ + 'criteria' => [ + ['field' => 'property_photo_id', 'condition' => '=', 'value' => $params['photo_id']], + ] + ]; + + $deleteData = $this->offerPhotoMappingRepository->findByCriteria($deleteCriteria, ['id']); + $deleteData = $deleteData ? $deleteData : [] ; + if($deleteData){ + $deleteIds = array_column($deleteData, 'id') ; + $destroyResult = $this->offerPhotoMappingRepository->destroy($deleteIds); + if ($destroyResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null + ]; + + } 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); + + + } + +} diff --git a/app/Core/Service/OfferPriceService.php b/app/Core/Service/OfferPriceService.php new file mode 100644 index 0000000..749b21c --- /dev/null +++ b/app/Core/Service/OfferPriceService.php @@ -0,0 +1,232 @@ +offerPriceRepository = $offerPriceRepository; + $this->offerPriceCreateValidator = $offerPriceCreateValidator; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerPriceCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + + + "offer_id" => fillOnUndefined($param, "offer_id"), + "date" => fillOnUndefined($param, "date"), + "property_room_id" => fillOnUndefined($param, "property_room_id"), + "property_accommodation_id" => fillOnUndefined($param, "property_accommodation_id"), + "number_of_rooms" => fillOnUndefined($param, "number_of_rooms"), + "currency" => fillOnUndefined($param, "currency"), + "amount" => fillOnUndefined($param, "amount"), + "total_amount" => fillOnUndefined($param, "total_amount"), + + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->offerPriceRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerPriceRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerPriceRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerPriceRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function insert($params){ + + return $this->offerPriceRepository->insert($params); + } + + public function destroy($params){ + + return $this->offerPriceRepository->destroy($params); + } + + public function deleteThisOfferPrices($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteThisIds = [] ; + + $requestData = [ + 'criteria' => [ + ['field' => 'offer_id', 'condition' => '=', 'value' => fillOnUndefined($params, "offer_id")], + ], + "whereIn" => + [ + ["field" => "property_room_id", "value" => fillOnUndefined($params, "property_room_id", [])] + ] + ]; + $eraseMappingData = $this->offerPriceRepository->findByCriteria($requestData, ['id']); + $eraseMappingData = $eraseMappingData ? $eraseMappingData : [] ; + $delete = collect($eraseMappingData)->keyBy('id')->keys()->all(); + $deleteThisIds = array_merge($deleteThisIds, $delete) ; + + $requestData = [ + 'criteria' => [ + ['field' => 'offer_id', 'condition' => '=', 'value' => fillOnUndefined($params, "offer_id")], + ], + "whereIn" => + [ + ["field" => "property_accommodation_id", "value" => fillOnUndefined($params, "property_accommodation_id", [])] + ] + ]; + $eraseMappingData = $this->offerPriceRepository->findByCriteria($requestData, ['id']); + $eraseMappingData = $eraseMappingData ? $eraseMappingData : [] ; + $delete = collect($eraseMappingData)->keyBy('id')->keys()->all(); + $deleteThisIds = array_merge($deleteThisIds, $delete) ; + + if($deleteThisIds){ + $deleteThisArray = $this->offerPriceRepository->destroy($deleteThisIds); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/OfferRoomMappingService.php b/app/Core/Service/OfferRoomMappingService.php new file mode 100644 index 0000000..a938b1c --- /dev/null +++ b/app/Core/Service/OfferRoomMappingService.php @@ -0,0 +1,296 @@ +offerRoomMappingRepository = $offerRoomMappingRepository; + $this->offerRoomMappingCreateValidator = $offerRoomMappingCreateValidator; + $this->roomMappingAddValidator = $roomMappingAddValidator; + $this->propertyRoomService = $propertyRoomService; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerRoomMappingCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + + + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_room_id" => fillOnUndefined($param, "property_room_id"), + + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->offerRoomMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerRoomMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerRoomMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerRoomMappingRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function insertRoomMapping($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->roomMappingAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $propertyRoomCheckParam = [ + 'room_ids' => $param['room_mapping'], + 'property_id' => $param['property_id'] + ]; + $propertyRoomCheck = $this->propertyRoomService->checkPropertyRoomMapping($propertyRoomCheckParam) ; + if($propertyRoomCheck['status'] != 'success'){ + throw new ApiErrorException($propertyRoomCheck['message']); + } + + $insertDataArray = [] ; + foreach ($param['room_mapping'] as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_room_id" => $item, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $insertResult = $this->offerRoomMappingRepository->createAll($insertDataArray); + if ($insertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $insertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function updateRoomMapping($param = [], $offerRoomMapping) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->roomMappingAddValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertThisIds = array_diff($param['room_mapping'], $offerRoomMapping); + $deleteThisIds = array_diff($offerRoomMapping, $param['room_mapping']); + $responseDeleteIds = $deleteThisIds; + + $insertDataArray = [] ; + foreach ($insertThisIds as $item) { + $insertDataArray[] = + [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_room_id" => $item, + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $insertResult = $this->offerRoomMappingRepository->createAll($insertDataArray); + if ($insertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $requestData = [ + 'criteria' => [ + ['field' => 'offer_id', 'condition' => '=', 'value' => fillOnUndefined($param, "offer_id")], + ], + "whereIn" => + [ + ["field" => "property_room_id", "value" => $deleteThisIds] + ] + ]; + $eraseMappingData = $this->offerRoomMappingRepository->findByCriteria($requestData,['id']); + $eraseMappingData = $eraseMappingData ? $eraseMappingData : [] ; + $deleteThisIds = collect($eraseMappingData)->keyBy('id')->keys()->all(); + + if($deleteThisIds){ + $deleteThisArray = $this->offerRoomMappingRepository->destroy($deleteThisIds); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + $response = [ + 'status' => true, + 'data' => $responseDeleteIds, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + +} diff --git a/app/Core/Service/OfferService.php b/app/Core/Service/OfferService.php new file mode 100644 index 0000000..9b0c284 --- /dev/null +++ b/app/Core/Service/OfferService.php @@ -0,0 +1,1484 @@ +offerRepository = $offerRepository; + $this->offerConfirmTypeRepository = $offerConfirmTypeRepository; + $this->offerCreateValidator = $offerCreateValidator; + $this->offerFactMappingService = $offerFactMappingService; + $this->offerContactMappingService = $offerContactMappingService; + $this->offerRoomMappingService = $offerRoomMappingService; + $this->offerImportantNotesService = $offerImportantNotesService; + $this->offerPhotoMappingService = $offerPhotoMappingService; + $this->offerCancellationPolicyService = $offerCancellationPolicyService; + $this->offerAccommodationMappingService = $offerAccommodationMappingService; + $this->offerPriceService = $offerPriceService; + $this->offerUpdateValidator = $offerUpdateValidator; + $this->offerStatusValidator = $offerStatusValidator; + $this->propertyRoomService = $propertyRoomService; + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "ticket_code" => $this->generateTicket(6), + "offer_code" => generateRandomString(16), + "title" => fillOnUndefined($param, "title"), + "email" => fillOnUndefined($param, "email"), + "expire_date" => fillOnUndefined($param, "expire_date"), + "confirm_type" => fillOnUndefined($param, "confirm_type", 'INS'), + "language" => fillOnUndefined($param, "language"), + "currency" => fillOnUndefined($param, "currency"), + "total" => fillOnUndefined($param, "total", 0), + "payment_type_id" => fillOnUndefined($param, "payment_type_id"), + "payment_type_mapping_id" => fillOnUndefined($param, "payment_type_mapping_id"), + "status" => fillOnUndefined($param, "status", 1), + "accept_status" => fillOnUndefined($param, "accept_status", 2), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->offerRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function selectOfferConfirmType($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerConfirmTypeRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->offerRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->offerRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + + public function addNewOffer($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + DB::beginTransaction(); + + $offerData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "title" => fillOnUndefined($param, "title"), + "email" => fillOnUndefined($param, "email"), + "expire_date" => fillOnUndefined($param, "expire_date"), + "confirm_type" => fillOnUndefined($param, "confirm_type"), + "language" => fillOnUndefined($param, "language"), + "currency" => fillOnUndefined($param, "currency"), + "total" => fillOnUndefined($param, "total"), + "payment_type_id" => fillOnUndefined($param, "payment_type_id"), + "payment_type_mapping_id" => fillOnUndefined($param, "payment_type_mapping_id"), + "status" => fillOnUndefined($param, "status", 1), + "accept_status" => 3, //Pending Customer Confirm + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + + ]; + + $offerCreateResult = $this->create($offerData); + + if ($offerCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $offerData = $offerCreateResult["data"]; + + $offerData = [ + 'offer_id' => $offerData['id'], + 'property_id' => fillOnUndefined($param, "property_id"), + 'user_id' => fillOnUndefined($param, "user_id"), + 'fact_mapping' => fillOnUndefined($param, 'fact_mapping', []), + 'contact_mapping' => fillOnUndefined($param, 'contact_mapping', []), + 'room_mapping' => fillOnUndefined($param, 'room_mapping', []), + 'important_notes' => fillOnUndefined($param, 'important_notes', []), + 'photo_mapping' => fillOnUndefined($param, 'photo_mapping', []), + 'cover_photos' => fillOnUndefined($param, 'cover_photos', []), + 'has_cancellation_policy' => fillOnUndefined($param, 'has_cancellation_policy', false), + 'cancellation_policy' => fillOnUndefined($param, 'cancellation_policy', []), + 'accommodation_mapping' => fillOnUndefined($param, 'accommodation_mapping', []), + 'hotel_features_mapping' => fillOnUndefined($param, 'hotel_features_mapping', []), + ]; + $addOfferFactResponse = $this->offerFactMappingService->insertNewOfferFacts($offerData); + if ($addOfferFactResponse['status'] != 'success') { + throw new ApiErrorException($addOfferFactResponse['message']); + } + + $offerContactResponse = $this->offerContactMappingService->insertContactMapping($offerData); + if ($offerContactResponse['status'] != 'success') { + throw new ApiErrorException($offerContactResponse['message']); + } + + $offerRoomResponse = $this->offerRoomMappingService->insertRoomMapping($offerData); + if ($offerRoomResponse['status'] != 'success') { + throw new ApiErrorException($offerRoomResponse['message']); + } + + $offerRoomResponse = $this->offerAccommodationMappingService->insertAccommodationMapping($offerData); + if ($offerRoomResponse['status'] != 'success') { + throw new ApiErrorException($offerRoomResponse['message']); + } + + + $offerImportantNotesResponse = $this->offerImportantNotesService->insertImportantNotes($offerData, []); + if ($offerImportantNotesResponse['status'] != 'success') { + throw new ApiErrorException($offerImportantNotesResponse['message']); + } + + $offerPhotoMapping = $this->offerPhotoMappingService->insertPhotoMapping($offerData); + if ($offerPhotoMapping['status'] != 'success') { + throw new ApiErrorException($offerPhotoMapping['message']); + } + + $offerCoverPhotoMapping = $this->offerPhotoMappingService->insertCoverPhotoMapping($offerData); + if ($offerCoverPhotoMapping['status'] != 'success') { + throw new ApiErrorException($offerCoverPhotoMapping['message']); + } + + $offerCancellationPolicy = $this->offerCancellationPolicyService->insertCancellationPolicy($offerData, []); + if ($offerCancellationPolicy['status'] != 'success') { + throw new ApiErrorException($offerCancellationPolicy['message']); + } + + + $response = [ + 'status' => true, + 'data' => $offerCreateResult["data"], + ]; + + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPriceList($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $offerRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $param['offer_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ], + 'with' => ['offerAccommodationMapping.propertyAccommodation', 'offerPrice', 'offerRoomMapping.propertyRoom'], + 'firstRow' => 1 + ]; + $columns = [ + "id", + "property_id", + "ticket_code", + "offer_code", + "title", + "description", + "expire_date", + "language", + "currency", + "total", + "payment_type_id", + "accept_status", + "status" + ]; + + $offerData = $this->offerRepository->findByCriteria($offerRequest, $columns); + if (!$offerData) { + throw new ApiErrorException('Offer data not found'); + } + + + $offerAccommodationMapping = collect($offerData['offer_accommodation_mapping'])->map(function ($value) { + return $value['property_accommodation']; + })->values()->all(); + + $offerPrice = $offerData['offer_price']; + $offerRoomMapping = collect($offerData['offer_room_mapping'])->map(function ($value) { + return $value['property_room']; + })->values()->all(); + + $responseOfferData = [ + + "id" => $offerData['id'], + "property_id" => $offerData['property_id'], + "ticket_code" => $offerData['ticket_code'], + "offer_code" => $offerData['offer_code'], + "title" => $offerData['title'], + "expire_date" => $offerData['expire_date'], + "language" => $offerData['language'], + "currency" => $offerData['currency'], + "total" => $offerData['total'], + "accept_status" => $offerData['accept_status'], + "status" => $offerData['status'], + + ]; + + + $priceDataStartDate = collect($offerPrice)->keyBy('date')->keys()->min(); + $startDate = Carbon::parse($priceDataStartDate); + $endDate = collect($offerPrice)->keyBy('date')->keys()->max(); + + $diffInDays = collect($offerPrice)->count() > 0 ? $startDate->diffInDays(Carbon::parse($endDate)) : -1; + + $roomArray = []; + $priceArray = []; + foreach ($offerPrice as $price) { + + $priceArray[$offerData['id'] . "|" . $price['property_room_id'] . "|" . $price['property_accommodation_id'] . "|" . $price['date']] = $price; + } + + foreach ($offerRoomMapping as $room) { + $roomItem = [ + 'room_id' => $room['id'], + 'room_name' => $room['name'], + ]; + $accommodationArray = []; + foreach ($offerAccommodationMapping as $offerAccommodation) { + + $accommodationItem = [ + 'id' => $offerAccommodation['id'], + 'name' => $offerAccommodation['name'], + 'language_key' => $offerAccommodation['language_key'], + ]; + $roomPrice = []; + $startDate = Carbon::parse($priceDataStartDate); + $numberOfRooms = null; + for ($i = 0; $i <= $diffInDays; $i++) { + $checkKey = $offerData['id'] . "|" . $room['id'] . "|" . $offerAccommodation['id'] . "|" . $startDate->format('Y-m-d'); + if (isset($priceArray[$checkKey])) { + $roomPrice[$startDate->format('Y-m-d')] = $priceArray[$checkKey]['amount']; + $numberOfRooms = $priceArray[$checkKey]['number_of_rooms']; + } else { + $roomPrice[$startDate->format('Y-m-d')] = null; + } + $startDate = $startDate->addDay(); + } + $accommodationItem['number_of_rooms'] = $numberOfRooms; + $accommodationItem['price'] = $roomPrice; + $accommodationArray[] = $accommodationItem; + } + $roomItem['accommodation'] = $accommodationArray; + $roomArray[] = $roomItem; + } + + $responseOfferData['room_price'] = $roomArray; + $responseOfferData['start_date'] = $priceDataStartDate; + $responseOfferData['end_Date'] = $endDate; + + $response = [ + 'status' => true, + 'data' => $responseOfferData, + ]; + + } 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); + } + + public function getPrice($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $offerRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $param['offer_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ], + 'with' => [ + 'offerAccommodationMapping.propertyAccommodation', + 'offerPrice', + 'offerRoomMapping.propertyRoom.propertyRoomType', + 'offerRoomMapping.propertyRoom.propertyRoomFactMapping.propertyFact', + 'offerRoomMapping.propertyRoom.PropertyRoomPhotoMapping.propertyRoomPhoto', + 'offerRoomMapping.propertyRoom.propertyRoomViewMapping.PropertyRoomViewType' + ], + 'firstRow' => 1 + ]; + $columns = [ + "id", + "property_id", + "title", + "description", + "expire_date", + "language", + "currency", + "total", + "payment_type_id", + "accept_status", + "status" + ]; + + $offerData = $this->offerRepository->findByCriteria($offerRequest, $columns); + if (!$offerData) { + throw new ApiErrorException('Offer data not found'); + } + + if(empty($offerData['offer_price'])) { + throw new ApiErrorException('Offer prices not include'); + } + + $offerAccommodationMapping = collect($offerData['offer_accommodation_mapping'])->map(function ($value) { + return $value['property_accommodation']; + })->values()->all(); + + $offerPrice = $offerData['offer_price']; + $offerRoomMapping = collect($offerData['offer_room_mapping'])->map(function ($value) { + return $value['property_room']; + })->values()->all(); + + + $roomArray = []; + + $offerPrice = collect($offerPrice); + + $sumAmount = 0; + $sumTotalAmount = 0; + foreach ($offerRoomMapping as $room) { + $accommodationArray = []; + + $room['property_room_type'] = collect($room['property_room_type'])->first(); + $room['exclude_occupancy'] = json_decode($room['exclude_occupancy']); + + + $occupancyParams = [ + 'max_adult' => $room['max_adult'], + 'max_child' => $room['max_child'], + 'max_occupancy' => $room['max_occupancy'], + 'exclude_occupancy' => $room['exclude_occupancy'], + ]; + $room['include_occupancy'] = $this->propertyRoomService->roomIncludeOccupancies($occupancyParams); + + + $roomFact = collect($room['property_room_fact_mapping'])->map(function ($value) use ($param) { + return collect($value['property_fact']); + })->values()->all(); + + $room['room_fact'] = $roomFact; + + $roomViews = collect($room['property_room_view_mapping'])->map(function ($value) use ($param) { + return collect($value['property_room_view_type']); + })->values()->all(); + + $room['room_views'] = $roomViews; + + $offerRoomMapping = collect($room['property_room_photo_mapping'])->map(function ($value) use ($param) { + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $param['property_id'] . '/' . $value['property_room_photo']['photo_name'] . '_1024x768.' . $value['property_room_photo']['file_ext']; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $param['property_id'] . '/' . $value['property_room_photo']['photo_name'] . '_200x200.' . $value['property_room_photo']['file_ext']; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $param['property_id'] . '/' . $value['property_room_photo']['photo_name'] . '_1024x768.' . $value['property_room_photo']['file_ext']; + } else { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $param['property_id'] . '/' . $value['property_room_photo']['photo_name'] . '_medium.' . $value['property_room_photo']['file_ext']; + } + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $param['property_id'] . '/' . $value['property_room_photo']['photo_name'] . '_200x200.' . $value['property_room_photo']['file_ext']; + } else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $param['property_id'] . '/' . $value['property_room_photo']['photo_name'] . '_thumbnail.' . $value['property_room_photo']['file_ext']; + } + + $original = Config::get('app.imageUrl') . '/property-photos/' . $param['property_id'] . '/' . $value['property_room_photo']['photo_name'] . '.' . $value['property_room_photo']['file_ext']; + return [ + 'default' => $original, + 'fixed' => $photoUrlFilePath, + 'thumb' => $photoUrlThumbFilePath + ]; + })->values()->all(); + $room['room_photos'] = $offerRoomMapping; + + + unset($room['property_room_fact_mapping']); + unset($room['property_room_photo_mapping']); + unset($room['property_room_view_mapping']); + + + $roomItem = $room; + + foreach ($offerAccommodationMapping as $offerAccommodation) { + + $accommodationItem = $offerAccommodation; + + $amount = $offerPrice->where('property_room_id', '=', $room['id']) + ->where('property_accommodation_id', '=', $offerAccommodation['id']) + ->sum('amount'); + + $totalAmount = $offerPrice->where('property_room_id', '=', $room['id']) + ->where('property_accommodation_id', '=', $offerAccommodation['id']) + ->sum('total_amount'); + + $numberOfRooms = $offerPrice->where('property_room_id', '=', $room['id']) + ->where('property_accommodation_id', '=', $offerAccommodation['id'])->sortBy('date') + ->pluck('number_of_rooms')->first(); + + $roomAccommodationPrices = $offerPrice->where('property_room_id', '=', $room['id']) + ->where('property_accommodation_id', '=', $offerAccommodation['id'])->sortBy('date') + ->toArray(); + + + $accommodationItem['price_daily'] = []; + + if ($roomAccommodationPrices) { + + foreach ($roomAccommodationPrices as $dailyPrice) { + $accommodationItem['price_daily'][] = [ + 'date' => $dailyPrice['date'], + 'date_formatted' => Carbon::parse($dailyPrice['date'])->format('d/m/Y'), + 'amount' => moneyDoubleFormat($dailyPrice['amount']), + 'number_of_rooms' => $dailyPrice['number_of_rooms'], + 'total' => moneyDoubleFormat($dailyPrice['total_amount']), + //'currency' => $dailyPrice['currency'], + ]; + } + + + $sumAmount += $amount; + $sumTotalAmount += $totalAmount; + $accommodationItem['price'] = [ + 'amount' => moneyDoubleFormat($amount), + 'total_amount' => moneyDoubleFormat($totalAmount), + 'number_of_rooms' => $numberOfRooms + ]; + + $accommodationArray[] = $accommodationItem; + } + } + if ($accommodationArray) { + $roomItem['accommodation'] = $accommodationArray; + $roomArray[] = $roomItem; + } + } + + $response = [ + 'status' => true, + 'data' => [ + 'room_list' => $roomArray, + 'amount' => moneyDoubleFormat($sumAmount), + 'total_amount' => moneyDoubleFormat($sumTotalAmount) + + ], + ]; + + } 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); + } + + public function storePriceList($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + DB::beginTransaction(); + + $offerData = + [ + "check_in" => fillOnUndefined($param, "start_date"), + "check_out" => fillOnUndefined($param, "end_date") + ]; + + $offerCheckInCheckOutUpdate = $this->update($param["offer_id"], $offerData); + + if ($offerCheckInCheckOutUpdate['status'] != 'success') { + throw new ApiErrorException($offerCheckInCheckOutUpdate['message']); + } + + $checkOfferApprovedParam = [ + 'offer_id' => $param['offer_id'] + ]; + $checkOfferApprovedStatus = $this->checkOfferApprovedStatus($checkOfferApprovedParam); + if ($checkOfferApprovedStatus['status'] != 'success') { + throw new ApiErrorException($checkOfferApprovedStatus['message']); + } + + $offerRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $param['offer_id']], + ], + 'with' => ['offerPrice'], + 'firstRow' => 1 + ]; + $columns = [ + "id", + "property_id", + "title", + "description", + "expire_date", + "language", + "currency", + "total", + "payment_type_id", + ]; + + $offerData = $this->offerRepository->findByCriteria($offerRequest, $columns); + if (!$offerData) { + throw new ApiErrorException('Offer data not found'); + } + + + $oldOfferPrices = $offerData['offer_price']; + $newRoomPrices = $param['room_price']; + $checkTotalAmount = 0; + + $insertPrices = []; + foreach ($newRoomPrices as $room) { + foreach ($room['accommodation'] as $accommodation) { + if ( + isset($accommodation['number_of_rooms']) && + !empty($accommodation['number_of_rooms']) && + $accommodation['number_of_rooms'] !== "" && + is_numeric($accommodation['number_of_rooms']) + ) { + foreach ($accommodation['price'] as $date => $price) { + if (is_numeric($price)) { + $totalAmount = $price * $accommodation['number_of_rooms']; + $insertItem = [ + 'offer_id' => $offerData['id'], + 'date' => $date, + 'property_room_id' => $room['room_id'], + 'property_accommodation_id' => $accommodation['id'], + 'number_of_rooms' => $accommodation['number_of_rooms'], + 'currency' => $offerData['currency'], + 'amount' => $price, + 'total_amount' => $totalAmount, + 'status' => fillOnUndefined($param, "status", 1), + 'created_by' => fillOnUndefined($param, "user_id", 0), + 'updated_by' => fillOnUndefined($param, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + $checkTotalAmount += $price; + $insertPrices[] = $insertItem; + } + } + } else { + throw new ApiErrorException(lang('Room (' . $room['room_name'] . '-' . $accommodation['name'] . ') number of rooms data not valid', [])); + } + } + } + + $deleteThis = collect($oldOfferPrices)->keyBy('id')->keys()->toArray(); + + if ($deleteThis) { + $deleteRoomRatePrices = $this->offerPriceService->destroy($deleteThis); + if ($deleteRoomRatePrices['status'] != 'success') { + throw new ApiErrorException($deleteRoomRatePrices['message']); + } + } + + $insertRoomRatePrices = $this->offerPriceService->insert($insertPrices); + if ($insertRoomRatePrices['status'] != 'success') { + throw new ApiErrorException($insertRoomRatePrices['message']); + } + + if ($checkTotalAmount <= 0) { + throw new ApiErrorException('Total amount cannot be zero. Please check your number of rooms and rates.'); + } + + + $offerPriceParams = [ + "offer_id" => fillOnUndefined($param, "offer_id"), + "property_id" => fillOnUndefined($param, "property_id"), + "user_id" => fillOnUndefined($param, "user_id"), + ]; + + $offerPriceTotal = $this->offerPriceTotalUpdate($offerPriceParams); + if ($offerPriceTotal['status'] != 'success') { + throw new ApiErrorException($offerPriceTotal['message']); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + private function offerPriceTotalUpdate($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $checkOfferApprovedParam = [ + 'offer_id' => $params['offer_id'] + ]; + $checkOfferApprovedStatus = $this->checkOfferApprovedStatus($checkOfferApprovedParam); + if ($checkOfferApprovedStatus['status'] != 'success') { + throw new ApiErrorException($checkOfferApprovedStatus['message']); + } + + $offerPriceTotalRequest = [ + 'criteria' => [ + ['field' => 'offer_id', 'condition' => '=', 'value' => $params['offer_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + + ]; + $offerPriceTotal = $this->offerPriceService->select($offerPriceTotalRequest); + if ($offerPriceTotal['status'] != 'success') { + throw new ApiErrorException($offerPriceTotal['message']); + } + + $totalPrice = collect($offerPriceTotal['data'])->sum(['total_amount']); + + $offerStatusData = + [ + "total" => $totalPrice, + "updated_by" => fillOnUndefined($params, "user_id") + + ]; + + $offerStatusResult = $this->update($params, $offerStatusData); + + if ($offerStatusResult['status'] != 'success') { + throw new ApiErrorException(lang('Offer Price Total data is not updated.')); + } + + $response = [ + 'status' => true, + 'data' => $offerStatusResult["data"], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + + public function findOffer($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $offerRequest = [ + 'criteria' => [ + // ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['offer_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => [ + 'offerAccommodationMapping.propertyAccommodation', + 'offerCancellationPolicy', 'offerContactMapping.propertyExecutive.executiveType', + 'offerFactMapping.propertyFact', 'offerImportantNotes', 'offerPhotoMapping.propertyPhoto', + 'offerPrice', 'offerRoomMapping.propertyRoom', 'offerAcceptStatus', + 'offerLanguage', 'createUser', 'property.propertyBrand'], + 'firstRow' => 1 + ]; + $columns = [ + "id", + "email", + "property_id", + "ticket_code", + "offer_code", + "title", + "description", + "expire_date", + "language", + "currency", + "total", + "status", + "payment_type_id", + "payment_type_mapping_id", + "status", + "accept_status", + "confirm_type", + "check_in", + "check_out", + "created_by" + ]; + + $offerData = $this->offerRepository->findByCriteria($offerRequest, $columns); + if (!$offerData) { + throw new ApiErrorException('Offer data not found'); + } + + $offerAccommodationMapping = collect($offerData['offer_accommodation_mapping'])->map(function ($value) { + return $value['property_accommodation']; + })->values()->all(); + + $offerCancellationPolicy = collect($offerData['offer_cancellation_policy'])->map(function ($value) { + return $value; + })->values()->all(); + + $offerContactMapping = collect($offerData['offer_contact_mapping'])->map(function ($value) use ($offerData) { + $response = $value['property_executive']; + + $response['executive_type'] = $response['executive_type']['language_key']; + return $response; + })->values()->all(); + + $offerFactMapping = collect($offerData['offer_fact_mapping'])->map(function ($value) { + return $value['property_fact']; + })->values()->all(); + + $offerImportantNotes = $offerData['offer_important_notes']; + $offerPhotoMapping = collect($offerData['offer_photo_mapping']) + ->where('is_cover', '=', 0) + ->map(function ($value) use ($params) { + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_1024x768.' . $value['property_photo']['file_ext']; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_200x200.' . $value['property_photo']['file_ext']; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_1024x768.' . $value['property_photo']['file_ext']; + } else { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_medium.' . $value['property_photo']['file_ext']; + } + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_200x200.' . $value['property_photo']['file_ext']; + } else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_thumbnail.' . $value['property_photo']['file_ext']; + } + $value['property_photo']['image_url'] = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '.' . $value['property_photo']['file_ext']; + $value['property_photo']['thumb_image_url'] = $photoUrlThumbFilePath; + $value['property_photo']['medium_image_url'] = $photoUrlFilePath; + return $value['property_photo']; + })->values()->all(); + + + $offerCoverPhotoMapping = collect($offerData['offer_photo_mapping']) + ->where('is_cover', '=', 1) + ->map(function ($value) use ($params) { + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_1024x768.' . $value['property_photo']['file_ext']; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_200x200.' . $value['property_photo']['file_ext']; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_1024x768.' . $value['property_photo']['file_ext']; + } else { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_medium.' . $value['property_photo']['file_ext']; + } + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_200x200.' . $value['property_photo']['file_ext']; + } else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '_thumbnail.' . $value['property_photo']['file_ext']; + } + $value['property_photo']['image_url'] = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $value['property_photo']['photo_name'] . '.' . $value['property_photo']['file_ext']; + $value['property_photo']['thumb_image_url'] = $photoUrlThumbFilePath; + $value['property_photo']['medium_image_url'] = $photoUrlFilePath; + return $value['property_photo']; + })->values()->all(); + + + $offerPrice = $offerData['offer_price']; + $offerRoomMapping = collect($offerData['offer_room_mapping'])->map(function ($value) { + return $value['property_room']; + })->values()->all(); + + $responseOfferData = [ + + "id" => $offerData['id'], + "email" => $offerData['email'], + "property_id" => $offerData['property_id'], + "ticket_code" => $offerData['ticket_code'], + "offer_code" => $offerData['offer_code'], + "title" => $offerData['title'], + "description" => $offerData['description'], + "expire_date" => $offerData['expire_date'], + "check_in" => $offerData['check_in'], + "check_out" => $offerData['check_out'], + "language" => $offerData['language'], + "currency" => $offerData['currency'], + "total" => $offerData['total'], + "status" => $offerData['status'], + "payment_type_id" => $offerData['payment_type_id'], + "payment_type_mapping_id" => $offerData['payment_type_mapping_id'], + "accept_status" => $offerData['accept_status'], + "confirm_type" => $offerData['confirm_type'], + 'offer_accommodation_mapping' => $offerAccommodationMapping, + 'offer_has_cancellation_policy' => count($offerCancellationPolicy) > 0 ? true : false, + 'offer_cancellation_policy' => $offerCancellationPolicy, + 'offer_contact_mapping' => $offerContactMapping, + 'offer_fact_mapping' => $offerFactMapping, + 'offer_important_notes' => $offerImportantNotes, + 'offer_photo_mapping' => $offerPhotoMapping, + 'offer_cover_photos' => $offerCoverPhotoMapping, + 'offer_price' => $offerPrice, + 'offer_room_mapping' => $offerRoomMapping, + 'offer_language' => $offerData['offer_language'], + 'offer_accept_status' => $offerData['offer_accept_status'], + 'create_user' => $offerData['create_user'], + 'property' => $offerData['property'], + 'property_brand' => $offerData['property']['property_brand'], + ]; + + $response = [ + 'status' => true, + 'data' => $responseOfferData, + ]; + + } 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); + + } + + public function editFormCheckValues($params) + { + + $responseData = $params; + $offer = fillOnUndefined($params, 'get_offer', []); + + $offerContactMapping = collect($offer['offer_contact_mapping'])->keyBy('id')->keys()->all(); + $offerPhotoMapping = collect($offer['offer_photo_mapping'])->keyBy('id')->keys()->all(); + $offerCoverPhotoMapping = collect($offer['offer_cover_photos'])->keyBy('id')->keys()->all(); + $offerRoomMapping = collect($offer['offer_room_mapping'])->keyBy('id')->keys()->all(); + + $responseData['executives'] = collect($params['executives'])->map(function ($value) use ($offerContactMapping) { + $value['is_selected'] = array_search($value['id'], $offerContactMapping) > -1 ? true : false; + return $value; + })->values()->all(); + + $responseData['photos'] = collect($params['photos'])->map(function ($value) use ($offerPhotoMapping, $offerCoverPhotoMapping) { + $value['is_selected'] = array_search($value['id'], $offerPhotoMapping) > -1 ? true : false; + $value['is_cover'] = array_search($value['id'], $offerCoverPhotoMapping) > -1 ? true : false; + + return $value; + })->values()->all(); + + $responseData['property_rooms'] = collect($params['property_rooms'])->map(function ($value) use ($offerRoomMapping) { + $value['is_selected'] = array_search($value['id'], $offerRoomMapping) > -1 ? true : false; + return $value; + })->values()->all(); + unset($offer['offer_accommodation_mapping']); + unset($offer['offer_contact_mapping']); + unset($offer['offer_fact_mapping']); + unset($offer['offer_photo_mapping']); + unset($offer['offer_cover_photos']); + unset($offer['offer_room_mapping']); + unset($offer['offer_price']); + $responseData['get_offer'] = $offer; + return $responseData; + } + + public function updateOffer($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + DB::beginTransaction(); + + $checkOfferApprovedParam = [ + 'offer_id' => $param['offer_id'] + ]; + $checkOfferApprovedStatus = $this->checkOfferApprovedStatus($checkOfferApprovedParam); + if ($checkOfferApprovedStatus['status'] != 'success') { + throw new ApiErrorException($checkOfferApprovedStatus['message']); + } + + $offerId = fillOnUndefined($param, "offer_id"); + $offerData = [ + "title" => fillOnUndefined($param, "title"), + "email" => fillOnUndefined($param, "email"), + "expire_date" => fillOnUndefined($param, "expire_date"), + "language" => fillOnUndefined($param, "language"), + "currency" => fillOnUndefined($param, "currency"), + "payment_type_id" => fillOnUndefined($param, "payment_type_id"), + "payment_type_mapping_id" => fillOnUndefined($param, "payment_type_mapping_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + ]; + + if ($checkOfferApprovedStatus['data']['confirm_type'] != $param['confirm_type']) { + if ($checkOfferApprovedStatus['data']['accept_status'] == 3) { + $offerData['confirm_type'] = fillOnUndefined($param, "confirm_type"); + } else { + throw new ApiErrorException('Offer information cannot be updated due to approval phase.'); + } + } + + if ($checkOfferApprovedStatus['data']['payment_type_mapping_id'] != $param['payment_type_mapping_id']) { + if ($checkOfferApprovedStatus['data']['accept_status'] == 3) { + $offerData['payment_type_mapping_id'] = fillOnUndefined($param, "payment_type_mapping_id"); + } else { + throw new ApiErrorException('Offer information cannot be updated due to approval phase.'); + } + } + + $validationResult = $this->offerUpdateValidator->validate($offerData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $offerCreateResult = $this->update($offerId, $offerData); + + if ($offerCreateResult['status'] != 'success') { + throw new ApiErrorException(lang('Offer data is not updated.')); + } + $offerData = $offerCreateResult["data"]; + + + $offerData = [ + 'offer_id' => $offerData['id'], + 'user_id' => fillOnUndefined($param, "user_id"), + 'fact_mapping' => fillOnUndefined($param, 'fact_mapping', []), + 'contact_mapping' => fillOnUndefined($param, 'contact_mapping', []), + 'room_mapping' => fillOnUndefined($param, 'room_mapping', []), + 'important_notes' => fillOnUndefined($param, 'important_notes', []), + 'photo_mapping' => fillOnUndefined($param, 'photo_mapping', []), + 'cover_photos' => fillOnUndefined($param, 'cover_photos', []), + 'cancellation_policy' => fillOnUndefined($param, 'cancellation_policy', []), + 'has_cancellation_policy' => fillOnUndefined($param, 'has_cancellation_policy', false), + 'accommodation_mapping' => fillOnUndefined($param, 'accommodation_mapping', []), + 'hotel_features_mapping' => fillOnUndefined($param, 'hotel_features_mapping', []), + ]; + + $getOffer = $this->findOffer($param); + + if ($getOffer['status'] != 'success') { + throw new ApiErrorException($getOffer['message']); + } + + $offer = $getOffer['data']; + + + $offerFactMapping = collect($offer['offer_fact_mapping'])->keyBy('id')->keys()->all(); + $offerContactMapping = collect($offer['offer_contact_mapping'])->keyBy('id')->keys()->all(); + $offerPhotoMapping = collect($offer['offer_photo_mapping'])->keyBy('id')->keys()->all(); + $offerCoverPhotoMapping = collect($offer['offer_cover_photos'])->keyBy('id')->keys()->all(); + $offerRoomMapping = collect($offer['offer_room_mapping'])->keyBy('id')->keys()->all(); + $offerAccommodationMapping = collect($offer['offer_accommodation_mapping'])->keyBy('id')->keys()->all(); + $offerCancellationPolicy = collect($offer['offer_cancellation_policy'])->keyBy('id')->keys()->all(); + $offerImportantNotes = collect($offer['offer_important_notes'])->keyBy('id')->keys()->all(); + + + $addOfferFactResponse = $this->offerFactMappingService->updateOfferFacts($offerData, $offerFactMapping); + if ($addOfferFactResponse['status'] != 'success') { + throw new ApiErrorException($addOfferFactResponse['message']); + } + + $offerContactResponse = $this->offerContactMappingService->updateContactMapping($offerData, $offerContactMapping); + if ($offerContactResponse['status'] != 'success') { + throw new ApiErrorException($offerContactResponse['message']); + } + + $offerPhotoMapping = $this->offerPhotoMappingService->updatePhotoMapping($offerData, $offerPhotoMapping); + if ($offerPhotoMapping['status'] != 'success') { + throw new ApiErrorException($offerPhotoMapping['message']); + } + + $offerCoverPhotoMapping = $this->offerPhotoMappingService->updateCoverPhotoMapping($offerData, $offerCoverPhotoMapping); + if ($offerCoverPhotoMapping['status'] != 'success') { + throw new ApiErrorException($offerCoverPhotoMapping['message']); + } + + $offerRoomResponse = $this->offerRoomMappingService->updateRoomMapping($offerData, $offerRoomMapping); + if ($offerRoomResponse['status'] != 'success') { + throw new ApiErrorException($offerRoomResponse['message']); + } + + $offerAccommodationResponse = $this->offerAccommodationMappingService->updateAccommodationMapping($offerData, $offerAccommodationMapping); + if ($offerAccommodationResponse['status'] != 'success') { + throw new ApiErrorException($offerAccommodationResponse['message']); + } + + $offerImportantNotesResponse = $this->offerImportantNotesService->insertImportantNotes($offerData, $offerImportantNotes); + if ($offerImportantNotesResponse['status'] != 'success') { + throw new ApiErrorException($offerImportantNotesResponse['message']); + } + + $offerCancellationPolicy = $this->offerCancellationPolicyService->insertCancellationPolicy($offerData, $offerCancellationPolicy); + if ($offerCancellationPolicy['status'] != 'success') { + throw new ApiErrorException($offerCancellationPolicy['message']); + } + + $deleteThisPrices = [ + 'offer_id' => $offer['id'], + 'property_room_id' => fillOnUndefined($offerRoomResponse, 'data', []), + 'property_accommodation_id' => fillOnUndefined($offerAccommodationResponse, 'data', []), + + ]; + + $deleteThisOfferPrices = $this->offerPriceService->deleteThisOfferPrices($deleteThisPrices); + if ($deleteThisOfferPrices['status'] != 'success') { + throw new ApiErrorException($deleteThisOfferPrices['message']); + } + + + $response = [ + 'status' => true, + 'data' => $offerCreateResult["data"], + ]; + + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function updateStatus($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->offerStatusValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $offerId = fillOnUndefined($param, "offer_id"); + + $checkOfferApprovedParam = [ + 'offer_id' => $offerId + ]; + $checkOfferApprovedStatus = $this->checkOfferApprovedStatus($checkOfferApprovedParam); + if ($checkOfferApprovedStatus['status'] != 'success') { + throw new ApiErrorException($checkOfferApprovedStatus['message']); + } + + $offerStatusData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "status" => fillOnUndefined($param, "status"), + "updated_by" => fillOnUndefined($param, "user_id") + + ]; + $offerStatusResult = $this->update($offerId, $offerStatusData); + + if ($offerStatusResult['status'] != 'success') { + throw new ApiErrorException(lang('Offer Status data is not updated.')); + } + + $response = [ + 'status' => true, + 'data' => $offerStatusResult["data"], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function updateAcceptStatus($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $offerId = fillOnUndefined($param, "offer_id"); + $offerCode = fillOnUndefined($param, "offer_code"); + $acceptStatus = fillOnUndefined($param, "accept_status", 1); + + $checkOfferApprovedParam = [ + 'offer_id' => $offerId + ]; + $checkOfferApprovedStatus = $this->checkOfferApprovedStatus($checkOfferApprovedParam); + + if ($checkOfferApprovedStatus['status'] != 'success') { + throw new ApiErrorException($checkOfferApprovedStatus['message']); + } + + $offerRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $offerId], + ['field' => 'offer_code', 'condition' => '=', 'value' => $offerCode] + ], + 'firstRow' => 1 + ]; + + $offerData = $this->offerRepository->findByCriteria($offerRequest); + if (!$offerData) { + throw new ApiErrorException('Offer data not found'); + } + + $offerAcceptStatusData = [ + "accept_status" => $acceptStatus, + "updated_at" => Carbon::now()->timestamp + ]; + + $offerAcceptStatusResult = $this->update($offerData['id'], $offerAcceptStatusData); + + if ($offerAcceptStatusResult['status'] != 'success') { + throw new ApiErrorException(lang('Offer Accept Status data is not updated.')); + } + + $response = [ + 'status' => true, + 'data' => $offerAcceptStatusResult["data"], + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function checkOfferApprovedStatus($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $offerRequest = [ + 'criteria' => [ + // ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($param, "offer_id")], + ], + 'firstRow' => 1 + ]; + + $offerData = $this->offerRepository->findByCriteria($offerRequest); + if (!$offerData) { + throw new ApiErrorException('Update failed. Offer data not found'); + } + + if (!in_array($offerData['accept_status'], [1, 2, 3])) { + throw new ApiErrorException('Update failed. Offer update is locked.'); + } + + $response = [ + 'status' => true, + 'data' => $offerData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + private function generateTicket($length = 6) + { + $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charactersLength = strlen($characters); + $randomString = ''; + for ($i = 0; $i < $length; $i++) { + $randomString .= $characters[rand(0, $charactersLength - 1)]; + } + $checkTicketCriteria = [ + 'criteria' => [ + ['field' => 'ticket_code', 'condition' => '=', 'value' => $randomString], + ], + 'firstRow' => 1 + ]; + $checkTicket = $this->offerRepository->findByCriteria($checkTicketCriteria); + if ($checkTicket) { + $this->generateTicket($length); + } + return $randomString; + } + + public function checkPropertyOfferPermissionAccess($param) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $offerRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($param, "offer_id")], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, "property_id")], + ], + 'firstRow' => 1 + ]; + + $offerData = $this->offerRepository->findByCriteria($offerRequest, + [ + 'id', 'property_id', 'offer_code', 'status', 'confirm_type', 'accept_status', + 'payment_type_mapping_id', 'title', 'ticket_code', 'offer_code', 'email', 'currency', 'total', + 'language', 'created_by' + ] + ); + + if (!$offerData) { + throw new ApiErrorException('Offer permission error'); + } + $response = [ + 'status' => true, + 'data' => $offerData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + +} diff --git a/app/Core/Service/OneSignalService.php b/app/Core/Service/OneSignalService.php new file mode 100644 index 0000000..252dcdd --- /dev/null +++ b/app/Core/Service/OneSignalService.php @@ -0,0 +1,68 @@ +restClient = new Client(); + } + + public function sendOneSignalPushNotification($restUrl , $payloadJson) + { + + $oneSignalAppId = '1d4372b2-4f8f-47c3-aab0-a9e38f2ad4c3'; + + $payloadJson['app_id'] = $oneSignalAppId; + + $response = ['status' => false, 'message' => '']; + try { + + $result = $this->restClient->post( + $restUrl, + [ + 'headers' => [ + 'Authorization' => 'Basic ODQ2NGEzMTUtM2Y2OS00MzczLWE2MmEtMjNkN2ZiYzg0ZTY5', + 'Content-Type' => 'application/json', + ], + 'body' => json_encode($payloadJson) + ] + ); + + $getResponseBody = $result->getBody()->getContents(); + $getResponseData = $getResponseBody ? json_decode($getResponseBody,1) : []; + + if(isset($getResponseData['errors']) && !empty($getResponseData['errors'])) { + $response['message'] = isset($getResponseData['errors']) ? implode(',', $getResponseData['errors']) : 'Servis Hatası!'; + } else { + $response = [ + 'status' => true, + 'data' => $getResponseData, + ]; + } + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + +} diff --git a/app/Core/Service/PdfContentService.php b/app/Core/Service/PdfContentService.php new file mode 100644 index 0000000..6797ec5 --- /dev/null +++ b/app/Core/Service/PdfContentService.php @@ -0,0 +1,635 @@ +propertyRepository = $propertyRepository; + $this->propertyAdditionalInfoService = $propertyAdditionalInfoService; + $this->propertyContactService = $propertyContactService; + $this->propertyFactService = $propertyFactService; + $this->propertyPhotoService = $propertyPhotoService; + $this->propertyRoomService = $propertyRoomService; + $this->propertyExecutiveService = $propertyExecutiveService; + $this->propertyWebPhotoMappingRepository = $propertyWebPhotoMappingRepository; + $this->propertyWebColorMappingService = $propertyWebColorMappingService; + $this->propertyPlaceService = $propertyPlaceService; + + } + + + public function pdfData($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', + 'propertyRooms.propertyRoomType', 'propertyExecutive.executiveType', + 'propertyLanguageSpoken.languageCode', 'propertyAwardsCertificates.awardsCertificateCategory' + ], + 'firstRow' => true + ]; + + + $getPropertyFields = [ + 'id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country' + ]; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + + //dd($property['property_rooms']); + + if (!$property) { + throw new ApiErrorException('property not found'); + } + + $newExecutiveArray = []; + foreach ($property['property_executive'] as $propertyExecutive) { + + $executiveItem = [ + 'id' => $propertyExecutive['id'], + 'executive_type_name' => $propertyExecutive['executive_type']['name'], + 'executive_type_locale_key' => $propertyExecutive['executive_type']['language_key'], + 'executive_type_icon' => $propertyExecutive['executive_type']['icon'], + 'name_surname' => $propertyExecutive['name_surname'], + 'email' => $propertyExecutive['email'], + 'phone_code' => $propertyExecutive['phone_code'], + 'phone' => $propertyExecutive['phone'], + 'extension' => $propertyExecutive['extension'], + 'mobile_code' => $propertyExecutive['mobile_code'], + 'mobile' => $propertyExecutive['mobile'], + 'fax_code' => $propertyExecutive['fax_code'], + 'fax' => $propertyExecutive['fax'], + 'view_full_phone' => $propertyExecutive['view_full_phone'], + 'view_full_mobile' => $propertyExecutive['view_full_mobile'], + 'view_full_fax' => $propertyExecutive['view_full_fax'], + ]; + + if ($propertyExecutive['status'] == 1) { + $newExecutiveArray[] = $executiveItem; + } + + } + + $getPropertyFact = $this->propertyFactService->getPropertyFact($params); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $getPropertyFact = $this->getPropertyFactForHtml($getPropertyFact['data']); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $responseData = [ + 'name' => $property['name'], + 'property_type' => fillOnUndefined($property['property_type'], 'name'), + 'property_type_language_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_type_key' => fillOnUndefined($property['property_type'], 'language_key'), + 'property_chain' => fillOnUndefined($property['property_chain'], 'name'), + 'official_name' => $property['official_name'], + 'tax_office' => $property['tax_office'], + 'tax_number' => $property['tax_number'], + 'currency_type' => $property['currency_type'], + 'country' => $property['country'], + 'description' => $property['name'], + 'rooms' => $property['property_rooms'], + 'property_brand' => $property['property_brand'], + 'property_contact' => $property['property_contact'], + 'property_facts' => $getPropertyFact['data'], + 'property_executive' => $newExecutiveArray, + ]; + + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if (isset($propertyAdditionalInfo['data'])) { + $responseData['additional_info'] = $propertyAdditionalInfo['data']; + } + + + $responseData['property_language_spoken'] = null; + if ($property['property_language_spoken']) { + foreach ($property['property_language_spoken'] as $language) { + $responseData['property_language_spoken'][] = __($language['language_code']['language_key']); + } + if ($responseData['property_language_spoken']) { + $responseData['property_language_spoken'] = implode(', ', $responseData['property_language_spoken']); + } + } + + + $responseData['property_awards_certificates'] = null; + if ($property['property_awards_certificates']) { + foreach ($property['property_awards_certificates'] as $certificate) { + if ($certificate['status'] == 1) { + $responseData['property_awards_certificates'][] = $certificate['awards_certificate_category']['name'] . (!empty($certificate['date']) ? ' (' . $certificate['date'] . ')' : ''); + } + } + if ($responseData['property_awards_certificates']) { + $responseData['property_awards_certificates'] = implode(', ', $responseData['property_awards_certificates']); + } + } + + $propertyPlaceCriteria = [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '!=', 'value' => 0], + //['field' => 'id', 'condition' => '=', 'value' => 5],//TODO: Del + ], + 'with' => [ + 'propertyPlaceCategory', 'propertyPlaceWorkingHour', + 'propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFact', + 'propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFactTitle', + 'propertyPlaceCategoryFieldValue.placeCategoryField', + 'propertyPlaceCategoryFieldValue.unitValue', + 'propertyPlaceCategoryFieldValue.optionValue' + ], + 'orderBy' => [["field" => "order_number", "value" => "ASC"]], + ]; + $propertyPlaces = $this->propertyPlaceService->select($propertyPlaceCriteria); + $propertyPlaces = $propertyPlaces['data'] ? $propertyPlaces['data'] : []; + + + foreach ($propertyPlaces as $placeKey => $propertyPlace) { + + $propertyPlaceFactGroups = collect($propertyPlace['property_place_fact_mapping'])->groupBy('property_place_fact_title_fact_mapping.place_fact_title.id'); + $propertyPlaceFactGroups = $propertyPlaceFactGroups->toArray(); + + $propertyPlaceFactMapping = []; + foreach ($propertyPlaceFactGroups as $propertyPlaceFactGroupKey => $propertyPlaceFactGroup) { + foreach ($propertyPlaceFactGroup as $propertyPlaceFact) { + $propertyPlaceFactMapping[$propertyPlaceFactGroupKey]['title'] = $propertyPlaceFact['property_place_fact_title_fact_mapping']['place_fact_title']['language_key']; + $propertyPlaceFactMapping[$propertyPlaceFactGroupKey]['fact'][] = $propertyPlaceFact['property_place_fact_title_fact_mapping']['place_fact']['language_key']; + } + } + + unset($propertyPlaces[$placeKey]['property_place_fact_mapping']); + + $propertyPlaces[$placeKey]['property_place_fact_mapping'] = $propertyPlaceFactMapping; + + } + + $responseData['propertyPlaces'] = $propertyPlaces; + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function getPropertyFactForHtml($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $categories = $params; + $responseArray = []; + foreach ($categories as $category) { + $categoryArray = []; + $categoryItem = $category; + foreach ($category['fact_subcategory'] as $subCategory) { + $subCategoryItem = $subCategory; + $facts = collect($subCategory['fact']) + ->where('is_selected', '=', true) + ->map(function ($value) { + return $value; + })->values()->all(); + $subCategoryItem['fact'] = $facts ? $facts : []; + if ($facts) { + $categoryArray[$subCategory['id']] = $subCategoryItem; + } + } + $categoryItem['fact_subcategory'] = $categoryArray; + $responseArray[$category['id']] = $categoryItem; + } + + $response = [ + 'status' => true, + 'data' => $responseArray, + ]; + + } 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); + + } + + public function propertyBaseData($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBrand', 'propertyContact'], + 'firstRow' => true + ]; + + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + + if (!$property) { + throw new ApiErrorException('property not found'); + } + + $response = [ + 'status' => true, + 'data' => $property, + ]; + + } 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); + } + + + public function factSheetData($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + $factSheetData = []; + + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', + 'propertyExecutive.executiveType', 'propertyLanguageSpoken.languageCode', 'propertyAwardsCertificates.awardsCertificateCategory', + 'propertyRooms.propertyRoomType', 'propertyRooms.propertyRoomFactMapping.propertyFact', + 'propertyRooms.propertyRoomPhotoMapping.propertyRoomPhoto', + 'propertyRooms.propertyRoomViewMapping.propertyRoomViewType', + 'propertyFactMapping.fact.propertyFactParent.propertyFactParent', 'propertyBookingEngineToken', + 'propertyPlace.propertyPlaceCategory', 'propertyPlace.propertyPlaceWorkingHour', + 'propertyPlace.propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFact', + 'propertyPlace.propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFactTitle', + 'propertyPlace.propertyPlaceCategoryFieldValue.placeCategoryField', + 'propertyPlace.propertyPlaceCategoryFieldValue.unitValue', + 'propertyPlace.propertyPlaceCategoryFieldValue.optionValue', + 'propertyPlace.propertyPlacePhotoMapping.propertyPlacePhoto', + 'propertyAdditionalInfos.propertyAdditionalInfoKey' + ], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $property = $this->propertyRepository->findByCriteria($propertyRequest, $getPropertyFields); + + if (!$property) { + throw new ApiErrorException('Property not found'); + } + + + //colorCodes + $colorCodes = null; + $colorCodesArray = json_decode($property['property_brand']['color_codes'], 1); + $colorCodes = array_values($colorCodesArray); + + //summary + $summary = null; + $summaryArray = json_decode($property['property_brand']['title'], 1); + $summary = isset($summaryArray[$params['locale']]) ? $summaryArray[$params['locale']] : null; + + + //propertyFact + $propertyFact = null; + $amenities = collect($property['property_fact_mapping'])->where('fact.property_fact_parent.property_fact_parent.id',1)->toArray(); + $propertyFact['amenities']['name'] = __('property_fact-amenities',[],$params['locale']); + foreach ($amenities as $propertyFactMapping) { + + $propertyFact['amenities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['name'] = __($propertyFactMapping['fact']['property_fact_parent']['language_key'],[],$params['locale']); + $propertyFact['amenities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['order_number'] = $propertyFactMapping['fact']['property_fact_parent']['order_number']; + $propertyFact['amenities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['fact'][$propertyFactMapping['fact']['id']] = [ + 'name' => __($propertyFactMapping['fact']['language_key'],[],$params['locale']), + 'icon' => $propertyFactMapping['fact']['icon'], + ]; + $propertyFact['amenities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['fact'] = array_values($propertyFact['amenities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['fact']); + } + $propertyFact['amenities']['fact'] = collect($propertyFact['amenities']['fact'])->sortBy('order_number')->toArray(); + $propertyFact['amenities']['fact'] = array_values($propertyFact['amenities']['fact']); + + $facilities = collect($property['property_fact_mapping'])->where('fact.property_fact_parent.property_fact_parent.id',44)->toArray(); + $propertyFact['facilities']['name'] = __('property_fact-facilities',[],$params['locale']); + foreach ($facilities as $propertyFactMapping) { + + $propertyFact['facilities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['name'] = __($propertyFactMapping['fact']['property_fact_parent']['language_key'],[],$params['locale']); + $propertyFact['facilities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['order_number'] = $propertyFactMapping['fact']['property_fact_parent']['order_number']; + $propertyFact['facilities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['fact'][$propertyFactMapping['fact']['id']] = [ + 'name' => __($propertyFactMapping['fact']['language_key'],[],$params['locale']), + 'icon' => $propertyFactMapping['fact']['icon'], + ]; + $propertyFact['facilities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['fact'] = array_values($propertyFact['facilities']['fact'][$propertyFactMapping['fact']['property_fact_parent']['id']]['fact']); + } + $propertyFact['facilities']['fact'] = collect($propertyFact['facilities']['fact'])->sortBy('order_number')->toArray(); + $propertyFact['facilities']['fact'] = array_values($propertyFact['facilities']['fact']); + + + //propertyPhoto + $propertyPhoto = []; + foreach ($property['property_photos'] as $propertyPhotos) { + $propertyPhoto[] = [ + 'photo_order' => $propertyPhotos['photo_order'], + 'is_default' => $propertyPhotos['is_default'], + 'is_compatible_slider' => $propertyPhotos['is_compatible_with_myweb_slider'], + 'photoUrl' => $propertyPhotos['photoUrl'], + ]; + } + $propertyPhoto = collect($propertyPhoto)->sortBy('photo_order')->toArray(); + $propertyPhoto = array_values($propertyPhoto); + + + //propertyAwardsCertificate + $propertyAwardsCertificate = []; + foreach ($property['property_awards_certificates'] as $propertyAwardsCertificateMapping) { + $propertyAwardsCertificate[] = [ + 'name' => $propertyAwardsCertificateMapping['awards_certificate_category']['name'], + 'type' => $propertyAwardsCertificateMapping['awards_certificate_category']['type'], + ]; + } + + //propertyExecutive + $propertyExecutive = []; + foreach ($property['property_executive'] as $propertyExecutives) { + $propertyExecutive[] = [ + 'name' => $propertyExecutives['name_surname'], + 'type' => __($propertyExecutives['executive_type']['language_key'],[],$params['locale']), + 'email' => $propertyExecutives['email'], + 'phone' => $propertyExecutives['view_full_phone'], + 'mobile' => $propertyExecutives['view_full_mobile'], + 'fax' => $propertyExecutives['view_full_fax'], + ]; + } + + + //propertyAwardsCertificate + $propertyAdditionalInfo = []; + foreach ($property['property_additional_infos'] as $propertyAdditionalInfos) { + $propertyAdditionalInfo[] = [ + 'id' => $propertyAdditionalInfos['additional_info_key_id'], + 'name' => __($propertyAdditionalInfos['property_additional_info_key']['language_key'],[],$params['locale']), + 'key' => $propertyAdditionalInfos['property_additional_info_key']['additional_info_key'], + 'language_key' => $propertyAdditionalInfos['property_additional_info_key']['language_key'], + 'value' => $propertyAdditionalInfos['value'], + ]; + } + + //propertyRoom + $propertyRoom = []; + foreach ($property['property_rooms'] as $propertyRooms) { + + $propertyRoom[$propertyRooms['id']] = [ + 'name' => $propertyRooms['name'], + 'type' => __($propertyRooms['property_room_type']['language_key'],[],$params['locale']), + 'max_occupancy' => $propertyRooms['max_occupancy'], + 'max_adult' => $propertyRooms['max_adult'], + 'max_child' => $propertyRooms['max_child'], + 'max_child_number' => $propertyRooms['max_child_number'], + 'room_size' => $propertyRooms['room_size'], + 'room_size_type' => $propertyRooms['roomSizeTypeFormatted'], + 'room_count' => $propertyRooms['room_count'] + ]; + + //propertyRoomFact + $propertyRoomFact = null; + foreach ($propertyRooms['property_room_fact_mapping'] as $propertyRoomFactMapping) { + $propertyRoomFact[$propertyRoomFactMapping['property_fact']['id']] = [ + 'name' => __($propertyRoomFactMapping['property_fact']['language_key'],[],$params['locale']), + 'icon' => $propertyRoomFactMapping['property_fact']['icon'], + ]; + $propertyRoomFact = array_values($propertyRoomFact); + } + + $propertyRoomFact = collect($propertyRoomFact)->sortBy('order_number')->toArray(); + $propertyRoomFact = array_values($propertyRoomFact); + $propertyRoom[$propertyRooms['id']]['fact'] = $propertyRoomFact; + + //propertyRoomPhoto + $propertyRoomPhoto = null; + foreach ($propertyRooms['property_room_photo_mapping'] as $propertyRoomPhotoMapping) { + $propertyRoomPhoto[] = [ + 'photo_order' => $propertyRoomPhotoMapping['property_room_photo']['photo_order'], + 'is_default' => $propertyRoomPhotoMapping['property_room_photo']['is_default'], + 'photoUrl' => $propertyRoomPhotoMapping['property_room_photo']['photoUrl'], + ]; + } + $propertyRoomPhoto = collect($propertyRoomPhoto)->sortBy('photo_order')->toArray(); + + //dd($propertyRoomPhoto); + $propertyRoomPhoto = array_values($propertyRoomPhoto); + $propertyRoom[$propertyRooms['id']]['photo'] = $propertyRoomPhoto; + + //propertyRoomView + $propertyRoomView = null; + foreach ($propertyRooms['property_room_view_mapping'] as $propertyRoomViewMapping) { + $propertyRoomView[] = [ + 'name' => __($propertyRoomViewMapping['property_room_view_type']['language_key'],[],$params['locale']), + ]; + } + $propertyRoom[$propertyRooms['id']]['view'] = $propertyRoomView; + + } + + $propertyRoom = array_values($propertyRoom); + + + //propertyPlace + $propertyPlace = []; + foreach ($property['property_place'] as $propertyPlaces) { + + + //$description + $description = null; + $descriptionArray = json_decode($propertyPlaces['description'], 1); + $description = isset($descriptionArray[$params['locale']]) ? $descriptionArray[$params['locale']] : null; + + + //propertyPlaceFact + $propertyPlaceFact = null; + foreach ($propertyPlaces['property_place_fact_mapping'] as $propertyPlaceFactMapping) { + $propertyPlaceFact[$propertyPlaceFactMapping['property_place_fact_title_fact_mapping']['place_fact_title']['id']]['name'] = __($propertyPlaceFactMapping['property_place_fact_title_fact_mapping']['place_fact_title']['language_key']); + $propertyPlaceFact[$propertyPlaceFactMapping['property_place_fact_title_fact_mapping']['place_fact_title']['id']]['fact'][$propertyPlaceFactMapping['property_place_fact_title_fact_mapping']['place_fact']['id']] = [ + 'name' => __($propertyPlaceFactMapping['property_place_fact_title_fact_mapping']['place_fact']['language_key'],[],$params['locale']), + 'icon' => $propertyPlaceFactMapping['property_place_fact_title_fact_mapping']['place_fact']['icon'], + ]; + $propertyPlaceFact[$propertyPlaceFactMapping['property_place_fact_title_fact_mapping']['place_fact_title']['id']]['fact'] = array_values($propertyPlaceFact[$propertyPlaceFactMapping['property_place_fact_title_fact_mapping']['place_fact_title']['id']]['fact']); + } + $propertyPlaceFact = collect($propertyPlaceFact)->sortBy('order_number')->toArray(); + $propertyPlaceFact = array_values($propertyPlaceFact); + + + //propertyPlaceFact + $propertyPlaceField = null; + foreach ($propertyPlaces['property_place_category_field_value'] as $propertyPlaceCategoryField) { + $propertyPlaceField[] = [ + 'id' => $propertyPlaceCategoryField['place_category_field']['id'], + 'name' => __($propertyPlaceCategoryField['place_category_field']['language_key'],[],$params['locale']), + 'element' => $propertyPlaceCategoryField['place_category_field']['element'], + 'input_value' => $propertyPlaceCategoryField['input_value'], + 'option_value' => $propertyPlaceCategoryField['option_value'], + 'unit' => isset($propertyPlaceCategoryField['unit_value']) ? __($propertyPlaceCategoryField['unit_value']['language_key'],[],$params['locale']) : null, + 'unit_short_name' => isset($propertyPlaceCategoryField['unit_value']) ? $propertyPlaceCategoryField['unit_value']['short_name'] : null, + ]; + } + + //propertyPlacePhoto + $propertyPlacePhoto= null; + foreach ($propertyPlaces['property_place_photo_mapping'] as $propertyPlacePhotoMapping) { + $propertyPlacePhoto[] = [ + 'photo_order' => $propertyPlacePhotoMapping['property_place_photo']['photo_order'], + 'is_default' => $propertyPlacePhotoMapping['property_place_photo']['is_default'], + 'photoUrl' => $propertyPlacePhotoMapping['property_place_photo']['photoUrl'], + ]; + } + $propertyPlacePhoto = collect($propertyPlacePhoto)->sortBy('photo_order')->toArray(); + $propertyPlacePhoto = array_values($propertyPlacePhoto); + + + + $propertyPlace[$propertyPlaces['id']] = [ + 'name' => $propertyPlaces['name'], + 'category' => __($propertyPlaces['property_place_category']['language_key'],[],$params['locale']), + 'description' => $description, + 'field' => $propertyPlaceField, + 'fact' => $propertyPlaceFact, + 'photo' => $propertyPlacePhoto + ]; + + + } + + $propertyPlace = array_values($propertyPlace); + + + $factSheetData = [ + 'id' => $property['id'], + 'name' => $property['name'], + 'booking_engine_token' => isset($property['property_booking_engine_token']['token']) ? $property['property_booking_engine_token']['token'] : null, + 'property_type' => __(fillOnUndefined($property['property_type'], 'language_key'),[],$params['locale']), + 'property_chain' => __(fillOnUndefined($property['property_chain'], 'name'),[],$params['locale']), + 'property_brand' => [ + 'logoUrl' => $property['property_brand']['logoUrl'], + 'colorCodes' => $colorCodes, + 'summary' => $summary + ], + 'property_contact' => [ + 'country' => $property['country'], + 'address' => $property['property_contact']['address'], + 'zip_code' => $property['property_contact']['zip_code'], + 'latitude' => $property['property_contact']['latitude'], + 'longitude' => $property['property_contact']['longitude'], + 'phone' => $property['property_contact']['view_full_phone'], + 'mobile' => $property['property_contact']['view_full_mobile'], + 'fax' => $property['property_contact']['view_full_fax'], + 'email' => $property['property_contact']['email'], + 'web' => $property['property_contact']['web'], + ], + 'property_executive' => $propertyExecutive, + 'property_awards_certificates' => $propertyAwardsCertificate, + 'property_fact' => $propertyFact, + 'property_photo' => $propertyPhoto, + 'property_room' => $propertyRoom, + 'property_place' => $propertyPlace, + 'additional_info' => $propertyAdditionalInfo + + ]; + + $response = [ + 'status' => true, + 'data' => $factSheetData, + ]; + + } 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); + } + + +} diff --git a/app/Core/Service/PermissionGroupService.php b/app/Core/Service/PermissionGroupService.php new file mode 100644 index 0000000..54b19c1 --- /dev/null +++ b/app/Core/Service/PermissionGroupService.php @@ -0,0 +1,28 @@ +permissionGroupRepository = $permissionGroupRepository; + } + + public function findByCriteria($param) + { + return $this->permissionGroupRepository->findByCriteria($param); + } + + public function findAll() + { + return $this->permissionGroupRepository->all(); + } +} diff --git a/app/Core/Service/PermissionGroupUserMappingService.php b/app/Core/Service/PermissionGroupUserMappingService.php new file mode 100644 index 0000000..1ad733e --- /dev/null +++ b/app/Core/Service/PermissionGroupUserMappingService.php @@ -0,0 +1,49 @@ +permissionGroupUserMappingRepository = $permissionGroupUserMappingRepository; + } + + public function findByCriteria ( array $params ) + { + return $this->permissionGroupUserMappingRepository->findByCriteria($params); + } + + public function updateOrCreate(array $criteria,array $saveData) + { + return $this->permissionGroupUserMappingRepository->updateOrCreate($criteria, $saveData); + } + + public function delete($param) + { + return $this->permissionGroupUserMappingRepository->delete($param); + } + + public function create ( array $param ) + { + return $this->permissionGroupUserMappingRepository->create($param); + } + + public function createAll ( array $param ) + { + return $this->permissionGroupUserMappingRepository->createAll ( $param ); + } + + public function update ( $id, $saveData ) + { + return $this->permissionGroupUserMappingRepository->update($id,$saveData); + + } + +} diff --git a/app/Core/Service/PermissionService.php b/app/Core/Service/PermissionService.php new file mode 100644 index 0000000..3fdbc07 --- /dev/null +++ b/app/Core/Service/PermissionService.php @@ -0,0 +1,181 @@ +permissionRepository = $permissionRepository; + $this->permissionGroupUserMappingRepository = $permissionGroupUserMappingRepository; + $this->permissionGroupMappingRepository = $permissionGroupMappingRepository; + $this->userService = $userService; + } + + public function findByCriteria ( array $params ) + { + return $this->permissionRepository->findByCriteria ( $params ); + } + + public function getPermissionMenuModelWithCriteria ( array $params ) + { + return $this->permissionRepository->permissionMenuModelFindByCriteria ( $params ); + } + + public function getMenuTreeForUser ( $params) + { + try + { + + $user_id = fillOnUndefined($params, 'user_id'); + $property_id = fillOnUndefined($params, 'property_id'); + $locale = fillOnUndefined($params,'locale', 'en'); + + $isAdmin = false; + $groupUserMappingCriteria = + [ + "criteria" => + [ + [ "field" => "user_id", "condition" => "=", "value" => $user_id ], + [ "field" => "property_id", "condition" => "=", "value" => $property_id ], + [ "field" => "status", "condition" => "=", "value" => 1 ] + ], + "with" => [ "permissionGroup" ] + ]; + $groupUserMapping = $this->permissionGroupUserMappingRepository->findByCriteria ( $groupUserMappingCriteria ); + $groupIds = pickItemFromArray ( "permission_group_id", $groupUserMapping ); + + + foreach ($groupUserMapping as $perMapping) + { + if ($perMapping[ "permission_group" ][ "is_admin" ]) + { + $isAdmin = true; + $groupIds = [ ]; + } + } + + $permissionIds = [ ]; + if ($groupIds) + { + $groupMappingCriteria = + [ + "whereIn" => + [ + [ "field" => "permission_group_id", "value" => $groupIds ] + ], + ]; + $groupMapping = $this->permissionGroupMappingRepository->findByCriteria ( $groupMappingCriteria ); + $permissionIds = pickItemFromArray ( "permission_id", $groupMapping ); + } + + $permissionMenuCriteria = + [ + "criteria" => + [ + [ "field" => "parent_id", "condition" => "=", "value" => null ] + ], + "orderBy" => [ [ "field" => "menu_order", "value" => "ASC" ] ], + "withCriteria" => + [ + [ + "method" => "children", + "whereIn" => + [ + [ "field" => "id", "value" => $permissionIds ] + ], + "orderBy" => + [ + [ "field" => "menu_order", "value" => "ASC" ] + ] + ] + ] + ]; + + + if ($permissionIds) + { + $permissionMenuCriteria[ "whereIn" ] = + [ + [ "field" => "id", "condition" => "=", "value" => $permissionIds ] + ]; + } + else + { + $userInfo = $this->userService->findUser($user_id); + if(!$userInfo["isSuperUser"] && !$isAdmin) + { + return []; + } + $permissionMenuCriteria['withCriteria'][] = + [ + "method" => "children", + "orderBy" => + [ + [ "field" => "menu_order", "value" => "ASC" ] + ] + ]; + } + + $permissionMenu = $this->getPermissionMenuModelWithCriteria ( $permissionMenuCriteria ); + $reDesignMenu = [ + 'permission_menu' => $permissionMenu, + 'locale' => $locale + ]; + + return $this->designPermissionMenu($reDesignMenu); + + } catch ( Exception $e ) + { + return [ ]; + } + } + + public function designPermissionMenu($params){ + try{ + $return = []; + $permissionMenu = fillOnUndefined($params , 'permission_menu', []); + $locale = fillOnUndefined($params , 'locale'); + foreach ($permissionMenu as $param){ + $menu = collect($param); + $filtered = $menu->only('id', 'name', 'code', 'children', 'component', 'menu_order','url')->toArray(); + if(!empty($filtered['children'])){ + $reDesignMenu = [ + 'permission_menu' => $filtered['children'], + 'locale' => $locale, + ]; + $filtered['children'] = $this->designPermissionMenu($reDesignMenu); + } + + + $return[] = $filtered ; + } + return $return ; + + }catch ( Exception $e ) + { + return [ ]; + } + } + +} diff --git a/app/Core/Service/ProductParameterMappingService.php b/app/Core/Service/ProductParameterMappingService.php new file mode 100644 index 0000000..fa15b39 --- /dev/null +++ b/app/Core/Service/ProductParameterMappingService.php @@ -0,0 +1,43 @@ +productParameterMappingRepository = $productParameterMappingRepository; + } + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->productParameterMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } +} diff --git a/app/Core/Service/ProductService.php b/app/Core/Service/ProductService.php new file mode 100644 index 0000000..168112f --- /dev/null +++ b/app/Core/Service/ProductService.php @@ -0,0 +1,263 @@ +productRepository = $productRepository; + $this->propertyProductMappingService = $propertyProductMappingService; + $this->productParameterMappingService = $productParameterMappingService; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->productRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyProducts($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $productCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [] + ]; + + $products = $this->select($productCriteria); + + + if ($products['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $productsCollection = collect($products['data']); + + $propertyProductMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $propertyProductMappingData = $this->propertyProductMappingService->select($propertyProductMappingCriteria); + if ($propertyProductMappingData['status'] != 'success') { + throw new ApiErrorException(lang('Property Product data not loaded')); + } + + $propertyProductMappingData = $propertyProductMappingData['data'] ? $propertyProductMappingData['data'] : []; + + $propertyProductMappingDataCollection = collect($propertyProductMappingData); + + $productParameterMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['parameterDetail'] + ]; + + $productParameterMappingData = $this->productParameterMappingService->select($productParameterMappingCriteria); + $productParameterMappingDataCollection = collect($productParameterMappingData['data'] ?? []); + + $propertyProducts = []; + foreach ($products['data'] as $product) { + + $productParameterArray = null; + $productMappingDetail = $propertyProductMappingDataCollection->where('product_id', $product['id'])->first(); + if (isset($productMappingDetail['parameterArray']) && !empty($productMappingDetail['parameterArray'])) { + $productParameterArray = $productMappingDetail['parameterArray']; + } + + $propertyProducts[$product['id']] = $product; + $propertyProducts[$product['id']]['property_has_product'] = count($propertyProductMappingDataCollection->where('product_id', $product['id'])->toArray()) ? true : false; + $propertyProducts[$product['id']]['parameterArray'] = $productParameterArray; + $propertyProducts[$product['id']]['parameters'] = $productParameterMappingDataCollection + ->where('product_id', $product['id']) + ->values() + ->map(function ($item) { + return [ + 'product_parameter_id' => $item['product_parameter_id'] ?? null, + 'is_selected' => $item['is_selected'] ?? null, + 'value' => $item['value'] ?? null, + 'unit' => $item['unit'] ?? null, + 'parameter_detail' => isset($item['parameterDetail']) ? [ + 'id' => $item['parameterDetail']['id'] ?? null, + 'name' => $item['parameterDetail']['name'] ?? null, + ] : (isset($item['parameter_detail']) ? [ + 'id' => $item['parameter_detail']['id'] ?? null, + 'name' => $item['parameter_detail']['name'] ?? null, + ] : null) + ]; + })->all(); + } + + $response = [ + 'status' => true, + 'data' => $propertyProducts, + ]; + + } 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); + } + + public function setDefaultPropertyProducts($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $productCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'whereIn' => [ + ['field' => 'id', 'value' => [1, 4, 5]] + //['field' => 'id', 'value' => [1, 2, 3, 4, 5, 6]] + ], + 'with' => [] + ]; + + $products = $this->select($productCriteria); + if ($products['status'] != 'success') { + throw new ApiErrorException(lang('Products data not loaded')); + } + + + $createParam['property_id'] = fillOnUndefined($params, 'property_id'); + foreach ($products['data'] as $product) { + $createParam['products'][] = [ + 'product_id' => fillOnUndefined($product, 'id') + ]; + + } + + $setProducts = $this->propertyProductMappingService->create($createParam); + if ($setProducts['status'] != 'success') { + throw new ApiErrorException($setProducts['message']); + } + + $response = [ + 'status' => true, + 'data' => $products['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); + } + + public function propertyProductActivate($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $productCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['product_id']], + ], + 'firstRow' => true + ]; + + $productCheck = $this->select($productCriteria); + if ($productCheck['status'] != 'success') { + throw new ApiErrorException(lang('Products data not loaded')); + } + + + $createParam['property_id'] = fillOnUndefined($params, 'property_id'); + + $createParam['products'][] = [ + 'product_id' => fillOnUndefined($params, 'product_id'), + 'expiration_date' => Carbon::now()->addMonth()->toDateString(), + 'amount' => fillOnUndefined($params, 'amount'), + 'currency' => fillOnUndefined($params, 'currency'), + ]; + + $setProducts = $this->propertyProductMappingService->create($createParam); + if ($setProducts['status'] != 'success') { + throw new ApiErrorException($setProducts['message']); + } + + $response = [ + 'status' => true, + 'data' => $setProducts['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); + } + +} diff --git a/app/Core/Service/PropertyAdditionalInfo/PropertyAdditionalInfoService.php b/app/Core/Service/PropertyAdditionalInfo/PropertyAdditionalInfoService.php new file mode 100644 index 0000000..6062f1f --- /dev/null +++ b/app/Core/Service/PropertyAdditionalInfo/PropertyAdditionalInfoService.php @@ -0,0 +1,470 @@ +propertyAdditionalInfoRepository = $additionalInfoRepository; + $this->propertyAdditionalInfoKeyRepository = $additionalInfoKeyRepository; + $this->generalTimezoneRepository = $generalTimezoneRepository; + } + + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyAdditionalInfoRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "additional_info_key_id" => fillOnUndefined($param, "additional_info_key_id"), + "value" => fillOnUndefined($param, "value"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + + ]; + + + /* + $validationResult = $this->propertyExecutiveCreateValidator->validate($insertData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + */ + + + $userCreateResult = $this->propertyAdditionalInfoRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyAdditionalInfoRepository->update($id, $param); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + + + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + /** + * @param array $params property_id and locale code + * + */ + public function getPropertyAdditionalInfo($params) + { + try { + $locale = $params['locale']; + $propertyId = $params['property_id']; + + $getPropertyAdditionalInfoKeyCriteria = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + ]; + $getPropertyAdditionalInfoKey = $this->propertyAdditionalInfoKeyRepository->findByCriteria($getPropertyAdditionalInfoKeyCriteria, ['id', 'additional_info_key']); + $getPropertyAdditionalInfoCriteria = + [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + ]; + $getPropertyAdditionalInfo = $this->propertyAdditionalInfoRepository->findByCriteria($getPropertyAdditionalInfoCriteria); + $getPropertyAdditionalInfoHasInfoKey = collect($getPropertyAdditionalInfo)->keyBy('additional_info_key_id')->toArray(); + + $filteredPropertyAdditionalInfo = []; + foreach ($getPropertyAdditionalInfoKey as $key => $propertyAdditionalInfoKeys) { + $value = null; + $additionalId = null; + + $additionalId = $propertyAdditionalInfoKeys['id']; + if (array_key_exists($additionalId, $getPropertyAdditionalInfoHasInfoKey)) { + $value = $getPropertyAdditionalInfoHasInfoKey[$additionalId]['value']; + } + + $filteredPropertyAdditionalInfo[$additionalId] = + [ + 'id' => $additionalId, + 'name' => $propertyAdditionalInfoKeys['additional_info_key'], + 'value' => $value, + ]; + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $filteredPropertyAdditionalInfo; + return output($response); + } + + /** + * @param array $params + * @param string $locale 'en','tr', etc.. + * @return array + */ + + public function insertPropertyAdditionalInfo($params) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + + try { + $responseData = $params['data']; + $insertPropertyAdditionalInfoData = []; + + foreach ($responseData as $perData) { + $insertPropertyAdditionalInfoData[] = + [ + 'property_id' => $params['property_id'], + 'additional_info_key_id' => $perData['additional_info_key_id'], + 'value' => $perData['value'], + 'created_by' => $params['user_id'], + 'updated_by' => $params['user_id'], + ]; + } + + $response = $this->propertyAdditionalInfoRepository->createAll($insertPropertyAdditionalInfoData); + + if ($response['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => '']; + + + } catch (ApiErrorException $e) { + $response = ['status' => 0, 'statusCode' => 500, 'message' => $e->getMessage(), 'data' => null]; + } catch (Exception $e) { + + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + + return output($response); + + } + + public function updatePropertyAdditionalInfo($params) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + + try { + $responseData = $params['data']; + $updatePropertyAdditionalInfoData = []; + $responseDataCount = count($responseData); + $countOfUpdatedData = 0; + //TODO : update'i gerçekleştirilmeyen datanında bildirilmesi gerekiyor. + foreach ($responseData as $perData) { + $id = $perData['id']; + + $updatePropertyAdditionalInfoData = + [ + 'property_id' => $params['property_id'], + 'additional_info_key_id' => $perData['additional_info_key_id'], + 'value' => $perData['value'], + 'updated_by' => $params['user_id'], + ]; + + $update = $this->propertyAdditionalInfoRepository->update($id, $updatePropertyAdditionalInfoData); + if ($update['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + if ($update['status'] === "success" && !empty($update['data'])) { + $countOfUpdatedData++; + } + } + + + if ($countOfUpdatedData != $responseDataCount) { + throw new ApiErrorException(lang('Your update did not take place!')); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => '']; + + + } catch (ApiErrorException $e) { + $response = ['status' => 0, 'statusCode' => 500, 'message' => $e->getMessage(), 'data' => null]; + } catch (Exception $e) { + + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + + return output($response); + } + + public function updateOrCreatePropertyAdditionalInfo($params) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + + try { + + + // Get All Keys + $getPropertyAdditionalKeyCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $getPropertyAdditionalKeyResponse = $this->propertyAdditionalInfoKeyRepository->findByCriteria($getPropertyAdditionalKeyCriteria, ['id', 'additional_info_key']); + $getPropertyAdditionalKeyResponse = $getPropertyAdditionalKeyResponse ? $getPropertyAdditionalKeyResponse : []; + $keyArray = collect($getPropertyAdditionalKeyResponse)->keyBy('additional_info_key')->all(); + + // get all property additional info + $propertyAdditionalInfoRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + + $propertyAdditionalInfoResponse = $this->select($propertyAdditionalInfoRequest); + if ($propertyAdditionalInfoResponse['status'] != 'success') { + throw new ApiErrorException(lang('Additional Data Not Loaded')); + } + $propertyAdditionalInfoCollection = collect($propertyAdditionalInfoResponse['data']); + foreach ($params['additional_info'] as $key => $value) { + + if (!isset($keyArray[$key])) { + continue; + } + + if (!isset($keyArray[$key])) { + throw new ApiErrorException($key . ' not found'); + } + + $value = $value != null ? $value : ""; + + + $additionalInfoCheck = $propertyAdditionalInfoCollection + ->where('additional_info_key_id', '=', $keyArray[$key]['id']) + ->first(); + /* + $kvkkKeys = ['kvkk_mersis_no', 'kvkk_data_controller', 'kvkk_contact_person']; + if(in_array($key, $kvkkKeys)){ + if($value === "" || $value === NULL){ + continue; + } + } + */ + if ($additionalInfoCheck) { + $updateData = [ + 'value' => $value, + 'updated_by' => $params['user_id'], + 'status' => 1, + ]; + $updateStatus = $this->update($additionalInfoCheck['id'], $updateData); + + if ($updateStatus['status'] != 'success') { + throw new Exception($updateStatus['message']); + } + + } else { + $createData = [ + 'property_id' => $params['property_id'], + 'additional_info_key_id' => $keyArray[$key]['id'], + 'value' => $value, + 'updated_by' => $params['user_id'], + 'created_by' => $params['user_id'], + ]; + $createStatus = $this->create($createData); + if ($createStatus['status'] != 'success') { + throw new Exception($createStatus['message']); + } + } + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => '']; + + + } catch (ApiErrorException $e) { + $response = ['status' => 0, 'statusCode' => 500, 'message' => $e->getMessage(), 'data' => null]; + } catch (Exception $e) { + + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + + return output($response); + } + + public function getPropertyAdditionalInfo2KeyBy($params) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + + $getPropertyAdditionalKeyCriteria = [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $getPropertyAdditionalKeyResponse = $this->propertyAdditionalInfoKeyRepository->findByCriteria($getPropertyAdditionalKeyCriteria, ['id', 'additional_info_key']); + $getPropertyAdditionalKeyResponse = $getPropertyAdditionalKeyResponse ? $getPropertyAdditionalKeyResponse : []; + + $getPropertyAdditionalInfoCriteria = [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $getPropertyAdditionalInfoResponse = $this->propertyAdditionalInfoRepository->findByCriteria($getPropertyAdditionalInfoCriteria, ['id', 'additional_info_key_id', 'value']); + $getPropertyAdditionalInfoResponse = $getPropertyAdditionalInfoResponse ? $getPropertyAdditionalInfoResponse : []; + $getPropertyAdditionalInfoResponse = collect($getPropertyAdditionalInfoResponse)->keyBy('additional_info_key_id')->all(); + + $newMap = []; + foreach ($getPropertyAdditionalKeyResponse as $keyItem) { + $newMap[$keyItem['additional_info_key']] = isset($getPropertyAdditionalInfoResponse[$keyItem['id']]) ? $getPropertyAdditionalInfoResponse[$keyItem['id']]['value'] : null; + + //property_timezone + if ($keyItem['additional_info_key'] == 'property_timezone') { + $generalTimezoneRepositoryCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $getPropertyAdditionalInfoResponse[$keyItem['id']]['value']], + ], + 'firstRow' => true + ]; + $propertyTimeZoneDetail = $this->generalTimezoneRepository->findByCriteria($generalTimezoneRepositoryCriteria, ['location', 'action_type', 'hour', 'minute']); + $newMap['property_timezone_detail'] = $propertyTimeZoneDetail ?? null; + } + } + + $response = ['status' => true, 'data' => $newMap]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function deletePropertyAdditionalInfos($params) + { + + $keyIdCriteria = [ + 'whereIn' => + [ + ['field' => 'additional_info_key', 'value' => $params['key_array']], + ], + ]; + $keyData = $this->propertyAdditionalInfoKeyRepository->findByCriteria($keyIdCriteria); + $keyIds = collect($keyData)->keyBy('id')->keys(); + + $propertyExecutiveDeleteRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'whereIn' => [ + ['field' => 'additional_info_key_id', 'value' => $keyIds], + ] + ]; + + $this->propertyAdditionalInfoRepository->delete($propertyExecutiveDeleteRequest); + + } + +} diff --git a/app/Core/Service/PropertyAddonService.php b/app/Core/Service/PropertyAddonService.php new file mode 100644 index 0000000..0fe3178 --- /dev/null +++ b/app/Core/Service/PropertyAddonService.php @@ -0,0 +1,205 @@ +propertyAddonRepository = $propertyAddonRepository; + $this->propertyChannelAddonRepository = $propertyChannelAddonRepository; + $this->propertyAddonValidator = $propertyAddonValidator; + } + + public function selectPropertyChannelAddon($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyChannelAddonRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function createPropertyChannelAddon($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyAddonValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = [ + "title" => fillOnUndefined($param, "title"), + 'description' => fillOnUndefined($param, 'description'), + "property_id" => fillOnUndefined($param, "property_id"), + "channel_id" => fillOnUndefined($param, "channel_id"), + "property_addon_id" => fillOnUndefined($param, "property_addon_id"), + "amount" => fillOnUndefined($param, "amount"), + "type" => fillOnUndefined($param, "type", 'PRP'), + "min_stay" => fillOnUndefined($param, "min_stay"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $insertDataResult = $this->propertyChannelAddonRepository->create($insertData); + if ($insertDataResult['status'] != 'success') { + throw new Exception($insertDataResult['message']); + } + + $insertDataResult = $insertDataResult["data"]; + + $response = [ + 'status' => true, + 'data' => $insertDataResult, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + + public function updatePropertyChannelAddon($id, $params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyAddonValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $updateParamAvailable = ['title','description','amount','type','min_stay','status','updated_by']; + foreach ($params as $key => $value) { + if(!in_array($key, $updateParamAvailable)) { + unset($params[$key]); + } + } + + $result = $this->propertyChannelAddonRepository->update($id, $params); + if ($result['status'] != 'success') { + throw new Exception($result['message']); + } + + $result = $result["data"]; + + $response = [ + 'status' => true, + 'data' => $result, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + + public function deletePropertyChannelAddon($deleteThisId) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $deleteThisId = is_array($deleteThisId) ? $deleteThisId : [$deleteThisId]; + $deleteThisArray = $this->propertyChannelAddonRepository->destroy($deleteThisId); + + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function selectPropertyAddon($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyAddonRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + +} diff --git a/app/Core/Service/PropertyAwardsCertificateService.php b/app/Core/Service/PropertyAwardsCertificateService.php new file mode 100644 index 0000000..e72faeb --- /dev/null +++ b/app/Core/Service/PropertyAwardsCertificateService.php @@ -0,0 +1,531 @@ +propertyAwardsCertificateRepository = $propertyAwardsCertificateRepository; + $this->awardsCertificateCategoryRepository = $awardsCertificateCategoryRepository; + $this->propertyAwardsCertificateCreateValidator = $propertyAwardsCertificateCreateValidator ; + $this->propertyAwardsCertificateUpdateValidator = $propertyAwardsCertificateUpdateValidator; + $this->propertyAwardCertificateUploadValidator = $propertyAwardCertificateUploadValidator; + $this->propertyRepository = $propertyRepository ; + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyAwardsCertificateCreateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $filePath = null ; + $fileType = null ; + + if($params['file']){ + + // upload new image ... + $uploadFile = $this->uploadCertificate($params); + if ($uploadFile['status'] != 'success') { + throw new Exception($uploadFile['message']); + } + + $filePath = $uploadFile['data']['file_path'] ; + $fileType = $uploadFile['data']['file_type'] ; + + } + + $insertData = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'category_id' => fillOnUndefined($params, 'category_id'), + 'language_code' => fillOnUndefined($params, 'language_code'), + 'name' => fillOnUndefined($params, 'name'), + 'date' => fillOnUndefined($params, 'date'), + 'url' => fillOnUndefined($params, 'url'), + 'file_path' => $filePath, + 'file_type' => $fileType, + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $createResult = $this->propertyAwardsCertificateRepository->create($insertData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $createResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($params= [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyAwardsCertificateRepository->findByCriteria($params, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyAwardsCertificateUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $id = fillOnUndefined($params, 'award_certificate_id') ; + $filePath = null ; + $fileType = null ; + $deleteThisImage = null ; + + $requestCriteria = [ + 'criteria' => [ + // ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['award_certificate_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => 1, + ]; + $columns = ['id', 'property_id', 'category_id', 'name', 'date', 'url', 'file_path', 'file_type', 'status']; + $awardCertificate = $this->propertyAwardsCertificateRepository->findByCriteria($requestCriteria,$columns); + if (!$awardCertificate) { + throw new Exception('api-unknown_error'); + } + $fileUpdateData = []; + + if($params['file']){ + + // upload new image ... + $uploadFile = $this->uploadCertificate($params); + if ($uploadFile['status'] != 'success') { + throw new Exception($uploadFile['message']); + } + + $filePath = $uploadFile['data']['file_path'] ; + $fileType = $uploadFile['data']['file_type'] ; + + // set delete old image+++++++ + if($awardCertificate['file_path']){ + $urlPath = '/property-photos/' . $awardCertificate['property_id'] . "/awards-certificates/"; + $deleteThisImage = Config::get('app.fileSystemDriver') . $urlPath . $awardCertificate['file_path']; + } + $fileUpdateData = [ + 'file_path' => $filePath, + 'file_type' => $fileType, + ]; + } + + $updateData = [ + 'category_id' => fillOnUndefined($params, 'category_id'), + 'language_code' => fillOnUndefined($params, 'language_code'), + 'name' => fillOnUndefined($params, 'name'), + 'date' => fillOnUndefined($params, 'date'), + 'url' => fillOnUndefined($params, 'url'), + "updated_by" => fillOnUndefined($params, "user_id"), + "updated_at" => time(), + ]; + $updateData = array_merge($updateData, $fileUpdateData) ; + + $updateResult = $this->propertyAwardsCertificateRepository->update($id, $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + + if($deleteThisImage) { + if (File::exists($deleteThisImage)) { + File::delete($deleteThisImage); + } + } + + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getCategoryList($params= [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $requestCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'firstRow' => 1 + ]; + $columns = ['id', 'name', 'country']; + $property = $this->propertyRepository->findByCriteria($requestCriteria, $columns); + + $requestCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'whereOr' => [ + ['field' => 'country_code', 'condition' => '=', 'value' => $property['country']], + ['field' => 'country_code', 'condition' => '=', 'value' => null], + ], + 'orderBy' => [ + ["field" => "name", "value" => "ASC"], + ] + + ]; + $columns = ['id', 'name', 'language_key', 'country_code', 'logo', 'type', 'status']; + + $data = $this->awardsCertificateCategoryRepository->findByCriteria($requestCriteria,$columns); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function listAwardsCertificates($params= [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $requestCriteria = [ + 'criteria' => [ + // ['field' => 'status', 'condition' => '=', 'value' => 1] + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'with' => ['awardsCertificateCategory'] + ]; + $columns = ['id', 'property_id', 'category_id', 'name', 'language_code', 'date', 'url', 'file_path', 'file_type', 'status']; + $data = $this->propertyAwardsCertificateRepository->findByCriteria($requestCriteria,$columns); + + $responseData = collect($data)->map(function ($value){ + $return = $value ; + unset($return['awards_certificate_category']) ; + $image = null ; + + if($value['file_path']){ + $urlPath = '/property-photos/' . $value['property_id'] . "/awards-certificates/"; + $image = Config::get('app.imageUrl') . $urlPath . $value['file_path']; + } + + $return['file_path'] = $image ? $image : null ; + + $category = [ + 'category_name' => $value['awards_certificate_category']['name'], + 'category_language_key' => $value['awards_certificate_category']['language_key'], + 'category_country_code' => $value['awards_certificate_category']['country_code'], + 'category_logo' => $value['awards_certificate_category']['logo'], + 'category_type' => $value['awards_certificate_category']['type'], + 'category_status' => $value['awards_certificate_category']['status'], + ]; + return array_merge($return, $category) ; + })->values()->all() ; + + array_multisort( + array_column($responseData, 'category_name'), SORT_ASC, + array_column($responseData, 'id'), SORT_DESC, + $responseData + ); + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function uploadCertificate($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try + { + $fileParam = [] ; + $validateParams = [ + 'file' => $params['file'] + ]; + + $validationResult = $this->propertyAwardCertificateUploadValidator->validate($validateParams); + if ($validationResult->errors()->first()) { + throw new ApiErrorException($validationResult->errors()->all()); + } + + $filePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . "/awards-certificates/"; + if (!File::exists($filePath)) { + File::makeDirectory($filePath, 0777, true); + } + $uploadedFile = $params['file']; + $fileExt = $uploadedFile->getClientOriginalExtension(); + $fileName = time() . '_'. generateRandomString(10) . '.' . $fileExt; + $quality = 80 ; + + if(strtolower($fileExt) == 'pdf'){ + $move = $params['file']->move($filePath, $fileName); + if ($move) { + $fileParam = + [ + 'file_path' => $fileName, + 'file_type' => 'PDF', + ]; + } + }else{ + + $uploadedOriginalImage = Image::make($uploadedFile); + $imageWidth = $uploadedOriginalImage->width() ; + $imageHeight = $uploadedOriginalImage->height(); + + if ($imageHeight > $imageWidth) { + + if($imageHeight > 768){ + + $imageHRatio = $imageHeight / 768 ; + $imageResizedOriginalWidth = intval($imageWidth / $imageHRatio); + $image = $uploadedOriginalImage->fit($imageResizedOriginalWidth, 768)->save($filePath . $fileName , $quality) ; + }else{ + $image = $uploadedOriginalImage->save($filePath . $fileName , $quality) ; + } + + } else { + + if($imageWidth > 1024) { + $imageWRatio = $imageWidth / 1024 ; + $imageResizedOriginalHeight = intval($imageHeight / $imageWRatio); + $image = $uploadedOriginalImage->fit(1024, $imageResizedOriginalHeight)->save($filePath . $fileName , $quality) ; + }else{ + $image = $uploadedOriginalImage->save($filePath . $fileName , $quality) ; + } + } + + $fileParam = + [ + 'file_path' => $fileName, + 'file_type' => 'IMG', + ]; + $uploadedOriginalImage->destroy(); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $fileParam]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + public function deletePhotoAwardsCertificates($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $requestCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['award_certificate_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => 1, + ]; + $columns = ['id', 'property_id', 'category_id', 'name', 'date', 'url', 'file_path', 'file_type', 'status']; + $awardCertificate = $this->propertyAwardsCertificateRepository->findByCriteria($requestCriteria,$columns); + if (!$awardCertificate) { + throw new Exception('api-unknown_error'); + } + + $urlPath = '/property-photos/' . $awardCertificate['property_id'] . "/awards-certificates/"; + $deleteThisImage = Config::get('app.fileSystemDriver') . $urlPath . $awardCertificate['file_path']; + + if($deleteThisImage) { + if (File::exists($deleteThisImage)) { + File::delete($deleteThisImage); + } + } + + $id = $awardCertificate['id'] ; + $updateData = [ + 'file_path' => null, + 'file_type' => null, + "updated_by" => fillOnUndefined($params, "user_id"), + "updated_at" => time(), + ]; + + $updateResult = $this->propertyAwardsCertificateRepository->update($id, $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateStatus($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $updateId = fillOnUndefined($param, "award_certificate_id") ; + $placeRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => $updateId], + ] + ]; + $propertyWebData = $this->propertyAwardsCertificateRepository->findByCriteria($placeRequest, ['id', 'name']); + + if (!$propertyWebData) { + throw new Exception('api-unknown_error'); + } + + $updateData = + [ + "status" => fillOnUndefined($param, "set_status", 0), + "updated_by" => fillOnUndefined($param, "user_id"), + "updated_at" => time(), + ]; + + $updateResult = $this->propertyAwardsCertificateRepository->update($updateId, $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + +} diff --git a/app/Core/Service/PropertyBookingEngineSearchService.php b/app/Core/Service/PropertyBookingEngineSearchService.php new file mode 100644 index 0000000..f8d6d41 --- /dev/null +++ b/app/Core/Service/PropertyBookingEngineSearchService.php @@ -0,0 +1,163 @@ +propertyBookingEngineSearchRepository = $propertyBookingEngineSearchRepository; + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $insertData = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'search_key' => fillOnUndefined($params, 'search_key'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + 'checkin_date' => fillOnUndefined($params, 'checkin_date'), + 'checkout_date' => fillOnUndefined($params, 'checkout_date'), + 'pax' => fillOnUndefined($params, 'pax'), + 'rooms' => fillOnUndefined($params, 'rooms'), + 'booking_code' => fillOnUndefined($params, 'booking_code'), + 'amount' => fillOnUndefined($params, 'best_amount'), + 'currency_code' => fillOnUndefined($params, 'currency_code'), + 'ip_address' => fillOnUndefined($params, 'ip_address'), + 'country_code' => fillOnUndefined($params, 'country_code'), + 'language_code' => fillOnUndefined($params, 'language_code', 'en'), + 'detail' => fillOnUndefined($params, 'detail'), + 'referrer' => fillOnUndefined($params, 'referrer'), + 'status' => fillOnUndefined($params, 'status', 3), + ]; + + $createResult = $this->propertyBookingEngineSearchRepository->create($insertData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $createResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyBookingEngineSearchRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyBookingEngineSearchRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function delete($ids = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteResult = $this->propertyBookingEngineSearchRepository->destroy($ids); + + if ($deleteResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult['data'] // affected row count + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + +} diff --git a/app/Core/Service/PropertyBookingEngineService.php b/app/Core/Service/PropertyBookingEngineService.php new file mode 100644 index 0000000..679665b --- /dev/null +++ b/app/Core/Service/PropertyBookingEngineService.php @@ -0,0 +1,288 @@ +propertyBookingEngineRepository = $propertyBookingEngineRepository; + $this->propertyChannelRepository = $propertyChannelRepository; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + $this->contractUploadValidator = $contractUploadValidator; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyBookingEngineRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + + + + public function createBookingEngineToken($params = []){ + + $response = ['status' => "success", 'message' => '', 'data' => null]; + + try { + if($this->propertyChannelService->checkChannelCategory($params["channel_id"],3)) { + + $data = [ + "property_id" => $params['property_id'], + "channel_id" => $params['channel_id'], + "token" => getGuid(), + "status" => 1, + "created_by" => $params['created_by'], + "updated_by" => $params['updated_by'], + "created_at" => time(), + "updated_at" => time(), + ]; + + $create = $this->propertyBookingEngineRepository->create($data); + if ($create['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + log:info("createBookingEngineToken",[$response]); + return output($response); + + } + + public function reactivateBookingEngineToken($params = []){ + + log:info("reactivateBookingEngineToken",[]); + $response = ['status' => "success", 'message' => '', 'data' => null]; + + try { + + if($this->propertyChannelService->checkChannelCategory($params["channel_id"],3)) { + $updateCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ] + ]; + + $updateData = [ + "status" => 1, + 'updated_by' => $params["updated_by"], + 'updated_at' => time(), + ]; + + $update = $this->propertyBookingEngineRepository->updateWhere($updateCriteria, $updateData); + if ($update['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deactivateBookingEngineToken($params = []){ + + log:info("reactivateBookingEngineToken",[]); + $response = ['status' => "success", 'message' => '', 'data' => null]; + + try { + + if($this->propertyChannelService->checkChannelCategory($params["channel_id"],3)) { + $updateCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ] + ]; + + $updateData = [ + "status" => 0, + 'updated_by' => $params["updated_by"], + 'updated_at' => time(), + ]; + + $update = $this->propertyBookingEngineRepository->updateWhere($updateCriteria, $updateData); + if ($update['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function bookEngineDashboard($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel', 'property.propertyBookingEngineGroupMapping.propertyGroup.propertyGroupMapping.property.propertyBookingEngineToken', 'propertyWeb'], + 'firstRow' => 1 + ] ; + $propertyBookingEngine = $this->select($propertyRequest, ['id', 'property_id', 'channel_id', 'token']); + if ($propertyBookingEngine['status'] != 'success') { + throw new ApiErrorException($propertyBookingEngine['message']); + } + + $propertyBookingEngine = $propertyBookingEngine['data'] ; + + $channelMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ] ; + $propertyChannelMapping = $this->propertyChannelMappingRepository->findByCriteria($channelMappingRequest, ['id', 'property_id', 'channel_id']); + + $response = [ + 'is_booking_engine_active' => isset($propertyBookingEngine['token'] ) && $propertyChannelMapping , + 'booking_engine_url' => isset($propertyBookingEngine['token'] ) ? Config::get('app.bookingEngineUrl').'/'.$propertyBookingEngine['token'] : null, + 'token' => isset($propertyBookingEngine['token']) ? $propertyBookingEngine['token'] : null, + + ]; + + + + + + $response = [ + 'status' => true, + 'data' => $response, + ]; + + } 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); + + } + + public function contractFileUpload($params = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $validationResult = $this->contractUploadValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $uploadResponse = [] ; + if($params['contract_file']){ + + $public_dir = Config::get('app.fileSystemDriver'); + $filePath = $public_dir . '/property-files/'.$params['property_id'].'/be'; + $filePathResponse = $params['property_id'].'/be/'; + + $contractFile = $params['contract_file']; + $newFileName = $params['property_id'] . '_'.$params['channel_id'].'_contract.pdf'; + + $propertyFilesUrl = Config::get('app.propertyFilesUrl'); + + $createFile = $contractFile->move($filePath, $newFileName); + if($createFile){ + $uploadResponse['contract_file_url'] = $propertyFilesUrl.$filePathResponse.$newFileName; + $uploadResponse['contract_file_path'] = $filePathResponse.$newFileName; + } + } + + $response = ['status' => 1, 'data' => $uploadResponse]; + + } 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); + + } + +} diff --git a/app/Core/Service/PropertyBookingPaymentTypeService.php b/app/Core/Service/PropertyBookingPaymentTypeService.php new file mode 100644 index 0000000..3552f76 --- /dev/null +++ b/app/Core/Service/PropertyBookingPaymentTypeService.php @@ -0,0 +1,88 @@ +request = $request; + $this->propertyBookingPaymentTypeRepository = $propertyBookingPaymentTypeRepository; + + } + + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->propertyBookingPaymentTypeRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyBookingPaymentTypeList(){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + $propertyBookingPaymentTypeListRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ] + ]; + + $propertyBookingPaymentTypeList = $this->select($propertyBookingPaymentTypeListRequest, ['id', 'name', 'language_key', 'code']); + + if(isset($propertyBookingPaymentTypeList['data'])){ + $return['booking_payment_types'] = $propertyBookingPaymentTypeList['data'] ; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyBrandService.php b/app/Core/Service/PropertyBrandService.php new file mode 100644 index 0000000..6dab603 --- /dev/null +++ b/app/Core/Service/PropertyBrandService.php @@ -0,0 +1,500 @@ +propertyBrandRepository = $propertyBrandRepository; + $this->propertyPhotoRepository = $propertyPhotoRepository; + $this->propertyWebPhotoMappingRepository = $propertyWebPhotoMappingRepository; + $this->propertyBrandAddValidator = $propertyBrandAddValidator; + $this->propertyBrandPhotoValidator = $propertyBrandPhotoValidator; + $this->languageService = $languageService; + } + + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyBrandRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyBrandRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updatePropertyBrand($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyBrandAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $photoArray = []; + if ($params['photo']) { + $photoArray = $this->uploadBrandPhoto($params); + if ($photoArray['status'] != 'success') { + throw new ApiErrorException($photoArray['message']); + } + $photoArray = $photoArray['data']; + } + + $brandCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'firstRow' => 1 + + ]; + + $oldBrandData = $this->propertyBrandRepository->findByCriteria($brandCriteria); + + $checkPropertyBrand = [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + + ]; + + $brandColor = json_decode($params['color_codes'], true); + $colorCodes = trim($brandColor[0]['color_code']) === '' && trim($brandColor[1]['color_code']) === '' ? null : json_encode($brandColor, true); + + + $titles = []; + $titleArray = json_decode(fillOnUndefined($params, "title", []), 1); + + if (!empty($oldBrandData['title'])) { + $brandLanguages = (array)json_decode($oldBrandData['title'], 1); + + $arrDiffs = array_diff_key($brandLanguages, $titles); + + foreach ($arrDiffs as $key => $arrDiff) { + $titles[$key] = $arrDiff; + } + } + + foreach ($titleArray as $key => $arr) { + $titles[$arr['language_code']] = $arr['description']; + } + + + $insertData = + [ + "property_id" => fillOnUndefined($params, "property_id"), + "title" => json_encode($titles), + "color_codes" => $colorCodes, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + if ($photoArray) { + $insertData['logo_path'] = $photoArray['path']; + $insertData['logo_name'] = $photoArray['photo']; + $insertData['logo_file_ext'] = $photoArray['extension']; + } + + $propertyBrandResponse = $this->propertyBrandRepository->updateOrCreate($checkPropertyBrand, $insertData); + + + if ($propertyBrandResponse['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + if ($oldBrandData && $params['photo'] && $photoArray) { + $urlPath = '/property-photos/' . $oldBrandData['property_id'] . "/logo/"; + $filePath = Config::get('app.fileSystemDriver') . $urlPath; + $oldImage250px = $filePath . $oldBrandData['logo_name'] . '_250x250' . '.' . $oldBrandData['logo_file_ext']; + $oldImage750px = $filePath . $oldBrandData['logo_name'] . '_750x750' . '.' . $oldBrandData['logo_file_ext']; + if (File::exists($oldImage750px)) { + File::delete($oldImage750px); + } + if (File::exists($oldImage250px)) { + File::delete($oldImage250px); + } + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyBrandResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + public function uploadBrandPhoto($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $fileParam = []; + $validateParams = [ + 'photo' => $params['photo'] + ]; + + $validationResult = $this->propertyBrandPhotoValidator->validate($validateParams); + if ($validationResult->errors()->first()) { + throw new ApiErrorException($validationResult->errors()->all()); + } + + + $filePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . "/logo/"; + + if (!File::exists($filePath)) { + File::makeDirectory($filePath, 0777, true); + } + + + $uploadedFile = $params['photo']; + $imageResize = Image::make($uploadedFile); + $imageWidth = $imageResize->width(); + $imageHeight = $imageResize->height(); + if ($imageWidth < 200 && $imageHeight < 150) { + throw new ApiErrorException('image size error'); + } + + $fileExt = $uploadedFile->getClientOriginalExtension(); + $urlPath = '/property-photos/' . $params['property_id'] . "/logo/"; + $filePath = Config::get('app.fileSystemDriver') . $urlPath; + $fileName = time(); + + if ($imageWidth >= 750 || $imageHeight >= 750) { + if ($imageWidth >= $imageHeight) { + $imageResize->resize(750, null, function ($constraint) { + $constraint->aspectRatio(); + })->save($filePath . $fileName . '_750x750' . '.' . $fileExt); + + $imageResize->resize(250, null, function ($constraint) { + $constraint->aspectRatio(); + })->save($filePath . $fileName . '_250x250' . '.' . $fileExt); + } else { + $imageResize->resize(null, 750, function ($constraint) { + $constraint->aspectRatio(); + })->save($filePath . $fileName . '_750x750' . '.' . $fileExt); + $imageResize->resize(null, 250, function ($constraint) { + $constraint->aspectRatio(); + })->save($filePath . $fileName . '_250x250' . '.' . $fileExt); + + } + } else { + if ($imageWidth >= $imageHeight) { + $imageResize->save($filePath . $fileName . '_750x750' . '.' . $fileExt); + $imageResize->resize(250, null, function ($constraint) { + $constraint->aspectRatio(); + })->save($filePath . $fileName . '_250x250' . '.' . $fileExt); + } else { + $imageResize->save($filePath . $fileName . '_750x750' . '.' . $fileExt); + $imageResize->resize(null, 250, function ($constraint) { + $constraint->aspectRatio(); + })->save($filePath . $fileName . '_250x250' . '.' . $fileExt); + } + } + $imageResize->destroy(); + $fileParam = + [ + 'photo' => $fileName, + 'path' => $urlPath, + 'extension' => $fileExt, + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $fileParam]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + public function getPropertyBrand($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $requestBrand = [ + + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + + ], + 'firstRow' => 1 + ]; + + $getPropertyBrand = $this->select($requestBrand); + if ($getPropertyBrand['status'] != "success") { + throw new ApiErrorException($getPropertyBrand['message']); + } + + $getApplicationLanguages = $this->languageService->getApplicationLanguages(); + if ($getApplicationLanguages['status'] != 'success') { + throw new ApiErrorException($getApplicationLanguages['message']); + } + $applicationLanguages = $getApplicationLanguages['data']; + $responseLangTitle = []; + + $brandLanguages = []; + if (!empty($getPropertyBrand['data']['title'])) { + $brandLanguages = (array)json_decode($getPropertyBrand['data']['title'], 1); + } + + if (!empty($getPropertyBrand['data'])) { + $brandColors = $getPropertyBrand['data']['color_codes']; + $getPropertyBrand = $getPropertyBrand['data']; + $getPropertyBrand['color_codes'] = json_decode($brandColors, true); + $getPropertyBrand['name'] = $getPropertyBrand['logo_name']; + $getPropertyBrand['logo_name'] = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . "/logo/" . $getPropertyBrand['logo_name'] . '_250x250.' . $getPropertyBrand['logo_file_ext']; + $brandTitle = fillOnUndefinedAndEmpty($getPropertyBrand, 'title', "[]"); + + $titleLangContents = json_decode($brandTitle, 1); + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangTitle[] = [ + 'language_code' => $langKey, + 'description' => isset($titleLangContents[$langKey]) ? $titleLangContents[$langKey] : null + ]; + } + $appLanguages = []; + foreach ($applicationLanguages as $applicationLanguage) { + $appLanguages[] = $applicationLanguage['code']; + } + + foreach ($appLanguages as $key => $appLanguage) { + if (in_array($appLanguage, array_keys($brandLanguages))) { + + $getPropertyBrand['title'] = $responseLangTitle; + } + } + + $getPropertyBrand['title'] = $responseLangTitle; + + } else { + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangTitle[] = [ + 'language_code' => $langKey, + 'description' => null + ]; + } + $getPropertyBrand = null; + $getPropertyBrand['title'] = $responseLangTitle; + $getPropertyBrand['color_codes'] = json_decode('[{"color_number":1,"color_code":"#000"},{"color_number":2,"color_code":"#000"}]'); + } + + + //Cover Photo + $coverPhoto = null; + $propertyWebPhotoMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'is_cover', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyPhoto'], + 'orderBy' => [["field" => "updated_at", "value" => "ASC"]], + 'firstRow' => true + ]; + $propertyWebMappingPhotos = $this->propertyWebPhotoMappingRepository->findByCriteria($propertyWebPhotoMappingRequest); + $propertyWebMappingPhotos = $propertyWebMappingPhotos ? $propertyWebMappingPhotos : []; + + if (!empty($propertyWebMappingPhotos)) { + $coverPhoto = $propertyWebMappingPhotos['property_photo']['photoUrl']['default']; + } else { + + $propertyDefaultPhotoRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'is_default', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + $propertyDefaultPhoto = $this->propertyPhotoRepository->findByCriteria($propertyDefaultPhotoRequest); + $propertyDefaultPhoto = $propertyDefaultPhoto ? $propertyDefaultPhoto : []; + + + if (!empty($propertyDefaultPhoto)) { + $coverPhoto = $propertyDefaultPhoto['photoUrl']['default']; + } + } + + $getPropertyBrand['cover_photo'] = empty($coverPhoto) ? 'https://api.extranetwork.com/assets/img/main-image.png' : $coverPhoto; + //Cover Photo + + $response = [ + 'status' => true, + 'data' => $getPropertyBrand, + ]; + + } 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); + } + + public function clearBrandLogo($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $requestBrand = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + + ], + 'firstRow' => 1 + ]; + + $getPropertyBrand = $this->select($requestBrand); + + if ($getPropertyBrand['status'] != "success") { + throw new ApiErrorException($getPropertyBrand['message']); + } + + if (empty($getPropertyBrand['data'])) { + throw new ApiErrorException("Property Not Found"); + } + + $brandId = $getPropertyBrand['data']['id']; + + $brandUpdateData = [ + "logo_path" => null, + "logo_name" => null, + "logo_file_ext" => null, + "updated_by" => fillOnUndefined($params, 'user_id'), + "updated_at" => time(), + ]; + + $updatedBrand = $this->update($brandId, $brandUpdateData); + + if (!empty($updatedBrand['data'])) { + $brandLogoPath = Config::get('app.fileSystemDriver') . $getPropertyBrand['data']['logo_path']; + $brandLogoName = $getPropertyBrand['data']['logo_name']; + $brandLogoExtension = $getPropertyBrand['data']['logo_file_ext']; + + $filename1 = $brandLogoPath . $brandLogoName . '_250x250' . '.' . $brandLogoExtension; + $filename2 = $brandLogoPath . $brandLogoName . '_750x750' . '.' . $brandLogoExtension; + + if (File::exists($filename1) && File::exists($filename2)) { + File::delete([$filename1, $filename2]); + } + } + + $response = [ + 'status' => true, + 'data' => $updatedBrand, + ]; + + } 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); + + } + + +} diff --git a/app/Core/Service/PropertyCancellationPolicyService.php b/app/Core/Service/PropertyCancellationPolicyService.php new file mode 100644 index 0000000..8a22af9 --- /dev/null +++ b/app/Core/Service/PropertyCancellationPolicyService.php @@ -0,0 +1,572 @@ +request = $request; + $this->propertyCancellationPolicyRepository = $propertyCancellationPolicyRepository; + $this->propertyRoomRepository = $propertyRoomRepository; + $this->propertyChannelRoomRateCancellationPolicyMappingRepository = $propertyChannelRoomRateCancellationPolicyMappingRepository ; + $this->updateRoomRateChannelCancellationPolicyValidator = $updateRoomRateChannelCancellationPolicyValidator ; + $this->propertyCancellationPolicyAddValidator = $propertyCancellationPolicyAddValidator ; + $this->propertyCancellationPolicyUpdateValidator = $propertyCancellationPolicyUpdateValidator ; + + } + + + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->propertyCancellationPolicyRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "name" => fillOnUndefined($param, "name"), + 'before_arrival' => fillOnUndefined($param, "before_arrival"), + 'is_nonrefundable' => fillOnUndefined($param, "is_nonrefundable"), + 'is_free_cancellation' => fillOnUndefined($param, "is_free_cancellation"), + 'type' => fillOnUndefined($param, "type"), + 'value' => fillOnUndefined($param, "value"), + 'is_affected_price' => fillOnUndefined($param, "is_affected_price"), + 'affect_price_action_type' => fillOnUndefined($param, "affect_price_action_type"), + 'affect_price_type' => fillOnUndefined($param, "affect_price_type"), + 'affect_price_value' => fillOnUndefined($param, "affect_price_value"), + 'is_date_range' => fillOnUndefined($param, "is_date_range"), + 'start_date' => fillOnUndefined($param, "start_date"), + 'finish_date' => fillOnUndefined($param, "finish_date"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $propertyCreateResult = $this->propertyCancellationPolicyRepository->create($propertyData); + + if ($propertyCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $data = Arr::except($propertyCreateResult["data"],['status','created_by','updated_by','created_at','updated_at']); + $response = [ + 'status' => true, + 'data' => $data + ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyCancellationPolicyRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $data = Arr::except($updateData,['status','created_by','updated_by','created_at','updated_at']); + $response = [ + 'status' => true, + 'data' => $data + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyCancellationPolicyList($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $propertyId = fillOnUndefined($params, 'property_id'); + $propertyCancellationPolicyRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + + ], + ]; + $propertyCancellationPolicy = $this->propertyCancellationPolicyRepository->findByCriteria($propertyCancellationPolicyRequest, ['id', 'property_id', 'name', 'before_arrival', 'is_nonrefundable', 'is_free_cancellation', 'type','value', 'is_affected_price', 'affect_price_action_type', 'affect_price_type', 'affect_price_value', 'is_date_range', 'start_date', 'finish_date', 'status']); + + $propertyCancellationPolicy = $propertyCancellationPolicy ? $propertyCancellationPolicy : [] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyCancellationPolicy]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function createPropertyCancellationPolicy($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + DB::beginTransaction(); + + $validationResult = $this->propertyCancellationPolicyAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + if($params['is_nonrefundable']){ + $type = 'PER'; + $value = 100 ; + }elseif ($params['is_free_cancellation']){ + $type = 'PER'; + $value = 0 ; + }else{ + $type = $params['type'] ; + $value = $params['value'] ; + } + $createData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => isset($params['name']) && $params['name'] ? $params['name'] : null, + 'before_arrival' => $params['before_arrival'], + 'is_nonrefundable' => $params['is_nonrefundable'], + 'is_free_cancellation' => $params['is_free_cancellation'], + 'type' => $type, + 'value' => $value, + 'is_affected_price' => $params['affects_price'], + 'affect_price_action_type' => $params['affects_price_action_type'], + 'affect_price_type' => $params['affects_price_type'], + 'affect_price_value' => $params['affects_price_value'], + 'is_date_range' => $params['is_date_range'], + 'start_date' => fillOnUndefined($params, 'start_date'), + 'finish_date' => fillOnUndefined($params, 'finish_date'), + 'user_id' => fillOnUndefined($params, 'user_id'), + 'status' => 1, + ]; + + + $createResult = $this->create($createData); + + if($createResult['status'] != 'success'){ + throw new ApiErrorException($createResult['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResult['data']]; + + DB::commit(); + } catch (ApiErrorException $e) { + db::rollBack(); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updatePropertyCancellationPolicy($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $validationResult = $this->propertyCancellationPolicyUpdateValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + if($params['is_nonrefundable']){ + $type = 'PER'; + $value = 100 ; + }elseif ($params['is_free_cancellation']){ + $type = 'PER'; + $value = 0 ; + }else{ + $type = $params['type'] ; + $value = $params['value'] ; + } + + $cancellationPolicyUpdateData = + [ + 'name' => isset($params['name']) && $params['name'] ? $params['name'] : null, + 'before_arrival' => $params['before_arrival'], + 'is_nonrefundable' => $params['is_nonrefundable'], + 'is_free_cancellation' => $params['is_free_cancellation'], + 'type' => $type, + 'value' => $value, + 'is_affected_price' => $params['affects_price'], + 'affect_price_action_type' => $params['affects_price_action_type'], + 'affect_price_type' => $params['affects_price_type'], + 'affect_price_value' => $params['affects_price_value'], + 'is_date_range' => $params['is_date_range'], + 'start_date' => fillOnUndefined($params, 'start_date'), + 'finish_date' => fillOnUndefined($params, 'finish_date'), + 'updated_by' => fillOnUndefined($params, 'user_id'), + 'updated_at' => time() + ]; + + $cancellationPolicyId = fillOnUndefined($params, 'cancellation_policy_id'); + + $cancellationPolicyResult = $this->update($cancellationPolicyId, $cancellationPolicyUpdateData); + + if ($cancellationPolicyResult['status'] != 'success') { + throw new ApiErrorException(lang('Property Cancellation Policy data is not updated.')); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $cancellationPolicyResult['data']]; + + } catch(ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + + public function getRoomRateChannelCancellationPolicy($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyRoomType', 'propertyRoomRateMapping.propertyRoomRateChannel.propertyRoomRateChannelCancellationPolicy', 'propertyRoomRateMapping.propertyRoomRate'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $roomData = $this->propertyRoomRepository->findByCriteria($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'exclude_occupancy', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number']); + + $propertyCancellationPolicyRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $propertyCancellationPolicy = $this->propertyCancellationPolicyRepository->findByCriteria($propertyCancellationPolicyRequest, ['id', 'property_id', 'name', 'before_arrival', 'is_nonrefundable', 'is_free_cancellation', 'type','value', 'is_affected_price', 'affect_price_action_type', 'affect_price_type', 'affect_price_value', 'is_date_range', 'start_date', 'finish_date', 'status']); + + $return = []; + + foreach ($roomData as $room){ + + if($room['property_room_rate_mapping']){ + $item = $room; + $item['exclude_occupancy'] = json_decode($room['exclude_occupancy']); + $roomRateMappings = $room['property_room_rate_mapping'] ; + $mapping = []; + $addedMapping = 0 ; + foreach ($roomRateMappings as $roomRateMapping){ + $propertyRoomRateChannel = null; + if(isset($roomRateMapping['property_room_rate_channel'])){ + $propertyRoomRateChannel = collect($roomRateMapping['property_room_rate_channel']) + ->where('channel_id', '=', $params['channel_id']) + ->where('room_rate_mapping_id', '=', $roomRateMapping['id']) + ->first(); + } + + $mappingStatus = false ; + $propertyCancellationPolicyArray = [] ; + if($propertyRoomRateChannel){ + if ($propertyRoomRateChannel['status'] == 1) { + $mappingStatus = true; + foreach ($propertyCancellationPolicy as $cancellationPolicy) { + + $propertyRoomRateChannelCancellationPolicy = [] ; + if (isset($propertyRoomRateChannel['property_room_rate_channel_cancellation_policy'])) { + $propertyRoomRateChannelCancellationPolicy = collect($propertyRoomRateChannel['property_room_rate_channel_cancellation_policy']) + ->where('cancellation_policy_id', '=', $cancellationPolicy['id']) + ->where('room_rate_channel_mapping_id', '=', $propertyRoomRateChannel['id']) + ->values()->toArray(); + } + + $propertyCancellationPolicyArray[] = [ + 'id' => $cancellationPolicy['id'], + 'property_id' => $cancellationPolicy['property_id'], + 'name' => $cancellationPolicy['name'], + 'before_arrival' => $cancellationPolicy['before_arrival'], + 'is_nonrefundable' => $cancellationPolicy['is_nonrefundable'], + 'is_free_cancellation' => $cancellationPolicy['is_free_cancellation'], + 'type' => $cancellationPolicy['type'], + 'value' => $cancellationPolicy['value'], + 'is_affected_price' => $cancellationPolicy['is_affected_price'], + 'affect_price_action_type' => $cancellationPolicy['affect_price_action_type'], + 'affect_price_type' => $cancellationPolicy['affect_price_type'], + 'affect_price_value' => $cancellationPolicy['affect_price_value'], + 'is_date_range' => $cancellationPolicy['is_date_range'], + 'start_date' => $cancellationPolicy['start_date'], + 'finish_date' => $cancellationPolicy['finish_date'], + 'is_selected' => $propertyRoomRateChannelCancellationPolicy ? true : false , + ]; + } + + } + } + $roomRateMapping['name'] = $roomRateMapping['property_room_rate']['name']; + $roomRateMapping['is_selected'] = $mappingStatus; + $roomRateMapping['has_date'] = $propertyRoomRateChannel['has_date'] == 0 ? false : true; + $roomRateMapping['end_date'] = $propertyRoomRateChannel['end_date']; + $roomRateMapping['start_date'] = $propertyRoomRateChannel['start_date']; + $roomRateMapping['room_rate_channel_mapping_id'] = $propertyRoomRateChannel['id']; + $roomRateMapping['room_rate_channel_cancellation_policy'] = $propertyCancellationPolicyArray; + + unset($roomRateMapping['property_room_rate']); + unset($roomRateMapping['property_room_rate_channel']); + if($mappingStatus){ + $addedMapping++; + $mapping[] = $roomRateMapping; + } + } + $item['property_room_rate_mapping'] = $mapping; + if($addedMapping > 0){ + $return[] = $item ; + } + } + + } + + $collection = collect($return); + $sorted = $collection->sortBy('id')->values()->all(); + + $response = [ + 'status' => true, + 'data' => $sorted, + ]; + + } 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); + } + + public function updateRoomRateChannelCancellationPolicy($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + $validationResult = $this->updateRoomRateChannelCancellationPolicyValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyCancellationPolicy'] + ]; + + $propertyChannelRoomRateCancellationPolicyMapping = $this->propertyChannelRoomRateCancellationPolicyMappingRepository->findByCriteria($criteria); + $oldMappingData = collect($propertyChannelRoomRateCancellationPolicyMapping); + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'id', 'condition' => '=', 'value' => $params['cancellation_policy_id']], + ], + 'firstRow' => 1 + ]; + + $mappedCancellationPolicy = $this->propertyCancellationPolicyRepository->findByCriteria($criteria); + if(!$mappedCancellationPolicy) { + throw new ApiErrorException('This Cancellation Policy not found'); + } + if ($params['is_selected'] == true) { + + $oldDataMappingCollect = collect($oldMappingData)->map(function ($value){ + return [ + 'id' => $value['id'] , + 'property_id' => $value['property_id'], + 'cancellation_policy_id' => $value['cancellation_policy_id'], + 'room_rate_channel_mapping_id' => $value['room_rate_channel_mapping_id'], + 'name' => $value['property_cancellation_policy']['name'] , + 'before_arrival' => $value['property_cancellation_policy']['before_arrival'], + 'is_nonrefundable' => $value['property_cancellation_policy']['is_nonrefundable'], + 'is_free_cancellation' => $value['property_cancellation_policy']['is_free_cancellation'], + 'type' => $value['property_cancellation_policy']['value'], + 'value' => $value['property_cancellation_policy']['type'], + 'is_affected_price' => $value['property_cancellation_policy']['is_affected_price'], + 'affect_price_action_type' => $value['property_cancellation_policy']['affect_price_action_type'], + 'affect_price_type' => $value['property_cancellation_policy']['affect_price_type'], + 'affect_price_value' => $value['property_cancellation_policy']['affect_price_value'], + ] ; + }); + + $checkHasMapping = $oldDataMappingCollect->where('cancellation_policy_id', '=', $params['cancellation_policy_id']) + ->where('room_rate_channel_mapping_id' ,'=', $params['room_rate_channel_mapping_id']) + ->where('property_id', '=', $params['property_id']) + ->first() ; + + $checkHasDay = $oldDataMappingCollect + ->where('before_arrival', '=', $mappedCancellationPolicy['before_arrival']) + ->where('start_date', '=', $mappedCancellationPolicy['start_date']) + ->where('finish_date', '=', $mappedCancellationPolicy['finish_date']) + ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id']) + ->where('property_id', '=', $params['property_id']) + ->first() ; + + if($checkHasMapping || $checkHasDay) { + throw new ApiErrorException('This mapping added before'); + }else{ + + $insertData = [] ; + $insertData[] = [ + 'property_id' => $params['property_id'], + 'cancellation_policy_id' => $params['cancellation_policy_id'], + 'room_rate_channel_mapping_id' => $params['room_rate_channel_mapping_id'], + 'status' => 1, + 'created_by' => fillOnUndefined($params, 'user_id'), + 'updated_by' => fillOnUndefined($params, 'user_id'), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + if($insertData){ + $createResult = $this->propertyChannelRoomRateCancellationPolicyMappingRepository->insert($insertData); + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + } + + + if($params['is_selected'] == false){ + $deleteThis = collect($propertyChannelRoomRateCancellationPolicyMapping) + ->where('cancellation_policy_id', '=', $params['cancellation_policy_id']) + ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id']) + ->where('property_id', '=', $params['property_id']) + ->keyBy('id')->keys()->toArray(); + + if($deleteThis){ + $deleteThisArray = $this->propertyChannelRoomRateCancellationPolicyMappingRepository->destroy($deleteThis); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + } + + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + db::rollBack(); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyChainService.php b/app/Core/Service/PropertyChainService.php new file mode 100644 index 0000000..41ceab3 --- /dev/null +++ b/app/Core/Service/PropertyChainService.php @@ -0,0 +1,95 @@ +propertyChainRepository = $propertyChainRepository; + + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyChainRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyChains($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'orderBy' => [ + ['field' => 'priority','value' => 'ASC'] + ] + ]; + + $searchResult = $this->select($searchCriteria, ['id', 'name', 'loyalty' ]); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_chains' =>$return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + + + +} diff --git a/app/Core/Service/PropertyChannelBookingPaymentSetupService.php b/app/Core/Service/PropertyChannelBookingPaymentSetupService.php new file mode 100644 index 0000000..bf8b392 --- /dev/null +++ b/app/Core/Service/PropertyChannelBookingPaymentSetupService.php @@ -0,0 +1,397 @@ +request = $request; + $this->propertyChannelBookingPaymentSetupRepository = $propertyChannelBookingPaymentSetupRepository; + $this->propertyBookingPaymentTypeService = $propertyBookingPaymentTypeService; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyChannelBookingPaymentSetupAddValidator = $propertyChannelBookingPaymentSetupAddValidator; + $this->propertyChannelTaxRepository = $propertyChannelTaxRepository; + + } + + + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->propertyChannelBookingPaymentSetupRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyCreateResult = $this->propertyChannelBookingPaymentSetupRepository->createAll($param); + if ($propertyCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $propertyCreateResult["data"] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyChannelBookingPaymentSetupRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getChannelBookingPaymentSetup($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $propertyBookingPaymentTypeList = $this->propertyBookingPaymentTypeService->getPropertyBookingPaymentTypeList(); + + $propertyChannelBookingPaymentSetupRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']] + ] + ]; + + $propertyChannelBookingPaymentSetup = $this->select($propertyChannelBookingPaymentSetupRequest, ['id','property_id', 'channel_id','cancellation_policy', 'property_channel_mapping_id', 'payment_type_id', 'is_affected_price', 'action_type', 'value_type', 'value','is_get_payment_data', 'is_selected']); + + if($propertyChannelBookingPaymentSetup['status'] != 'success'){ + throw new ApiErrorException('Property Channel Booking Payment Setup data not found'); + } + + $dataList = []; + $dataList['channel_id'] = $params['channel_id']; + foreach ($propertyBookingPaymentTypeList['data']['booking_payment_types'] as $propertyBookingPaymentType){ + + if($propertyBookingPaymentType['code'] == 'CHN') { + //continue; + } + + $setup = collect($propertyChannelBookingPaymentSetup['data']) + ->where('payment_type_id', '=', $propertyBookingPaymentType['id'])->first(); + + $paymentCancellationPolicy = []; + if(!empty($setup['cancellationPolicyArray'])) { + $paymentCancellationPolicy = $setup['cancellationPolicyArray']; + } + + $dataList['payments'][] = [ + "payment_type_id" => $propertyBookingPaymentType['id'], + "name" => $propertyBookingPaymentType['name'], + "code" => $propertyBookingPaymentType['code'], + "language_key" => $propertyBookingPaymentType['language_key'], + "is_affected_price" => isset($setup['is_affected_price']) ? $setup['is_affected_price'] : null, + "action_type" => isset($setup['action_type']) ? $setup['action_type'] : null, + "value_type" => isset($setup['value_type']) ? $setup['value_type'] : null, + "value" => isset($setup['value']) ? $setup['value'] : null, + "is_get_payment_data" => isset($setup['is_get_payment_data']) ? $setup['is_get_payment_data'] : null, + "is_selected" => $setup['is_selected'] ? true : false, + "cancellation_policy" => $paymentCancellationPolicy + ]; + } + + $propertyChannelMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']] + ], + 'firstRow' => true + ]; + + $propertyCurrencyCode = $this->propertyChannelMappingService->select($propertyChannelMappingRequest, ['currency_code']); + + if($propertyCurrencyCode['status'] != 'success'){ + throw new ApiErrorException($propertyCurrencyCode['message']); + } + + + $getChannelRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['parentChannel'], + 'firstRow' => 1 + ]; + $getChannelResponse = $this->propertyChannelService->select($getChannelRequest, ['id', 'name','default_currency', 'currency_code']) ; + if($getChannelResponse['status'] != 'success'){ + throw new ApiErrorException($getChannelResponse['message']); + } + $channelData['channel'] = $getChannelResponse['data'] ; + + + $dataList['currency_code'] = isset($propertyCurrencyCode['data']) && !empty($propertyCurrencyCode['data']['currency_code']) + ? $propertyCurrencyCode['data']['currency_code'] : $channelData['channel']['default_currency']; + + + + $getChannelMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ], + 'firstRow' => 1 + ]; + $getChannelMappingResponse = $this->propertyChannelMappingService->select($getChannelMappingRequest) ; + + if($getChannelMappingResponse['status'] != 'success'){ + throw new ApiErrorException($getChannelMappingResponse['message']); + } + + + $channelTaxCriteria = ["criteria" => [["field" => "status", "condition" => "=", "value" => 1]]]; + $channelTaxes = $this->propertyChannelTaxRepository->findByCriteria($channelTaxCriteria, ['id', 'name', 'language_key', 'code']); + $channelTaxes = $channelTaxes ? $channelTaxes : []; + + $channelTaxList = []; + foreach ($channelTaxes as $channelTax) { + $channelTaxList[] = [ + "id" => $channelTax['id'], + "name" => $channelTax['name'], + "language_key" => $channelTax['language_key'], + "code" => $channelTax['code'], + "is_selected" => $channelTax['id'] == $getChannelMappingResponse['data']['channel_tax_id'] ? true : false, + ]; + } + + $dataList['channel_tax'] = $channelTaxList; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $dataList]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + + return output($response); + } + + public function addChannelBookingPaymentSetup($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $validationResult = $this->propertyChannelBookingPaymentSetupAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $criteria = + [ + "criteria" => + [ + ["field" => "property_id", "condition" => "=", "value" => $params['property_id']], + ["field" => "channel_id", "condition" => "=", "value" => $params['channel_id']], + ] + ]; + + $oldMappingList = $this->propertyChannelBookingPaymentSetupRepository->findByCriteria($criteria); + + $criteria = + [ + "criteria" => + [ + ["field" => "property_id", "condition" => "=", "value" => $params['property_id']], + ["field" => "channel_id", "condition" => "=", "value" => $params['channel_id']], + ], + "firstRow" => 1 + ]; + + $channelMapping = $this->propertyChannelMappingService->select($criteria); + + if($channelMapping['status'] != 'success'){ + throw new ApiErrorException($channelMapping['message']) ; + } + $channelMapping = $channelMapping['data'] ; + + if(empty($oldMappingList)){ + $setupData = []; + foreach ($params['payments'] as $channelSetup) { + $setupData[] = + [ + "property_id" => $params['property_id'], + "channel_id" => $params['channel_id'], + "property_channel_mapping_id" => $channelMapping['id'], + "payment_type_id" => fillOnUndefinedAndEmpty($channelSetup, 'payment_type_id', null), + "cancellation_policy" => !empty($channelSetup['cancellation_policy']) ? json_encode($channelSetup['cancellation_policy']) : null, + "is_affected_price" => fillOnUndefinedAndEmpty($channelSetup, 'is_affected_price', 0), + "is_get_payment_data" => fillOnUndefinedAndEmpty($channelSetup, 'is_get_payment_data', 0), + "action_type" => fillOnUndefinedAndEmpty($channelSetup, 'action_type', null), + "value_type" => fillOnUndefinedAndEmpty($channelSetup, 'value_type', null), + "value" => fillOnUndefinedAndEmpty($channelSetup, 'value', null), + "is_selected" => $channelSetup['is_selected'], + "status" => 1, + "created_by" => $params['user_id'], + "updated_by" => $params['user_id'], + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $createStatus = $this->create($setupData); + + if($createStatus['status'] != 'success'){ + throw new ApiErrorException($createStatus['message']); + } + + }else{ + + foreach ($params['payments'] as $channelSetup) { + + $updateData = collect($oldMappingList)->firstWhere('payment_type_id',$channelSetup['payment_type_id']); + if(!empty($updateData)){ + $setupData = + [ + "property_id" => $params['property_id'], + "channel_id" => $params['channel_id'], + "payment_type_id" => fillOnUndefinedAndEmpty($channelSetup, 'payment_type_id', null), + "cancellation_policy" => !empty($channelSetup['cancellation_policy']) ? json_encode($channelSetup['cancellation_policy']) : null, + "action_type" => fillOnUndefinedAndEmpty($channelSetup, 'action_type', null), + "value_type" => fillOnUndefinedAndEmpty($channelSetup, 'value_type', null), + "value" => fillOnUndefinedAndEmpty($channelSetup, 'value', null), + "is_affected_price" => fillOnUndefinedAndEmpty($channelSetup, 'is_affected_price', 0), + "is_get_payment_data" => fillOnUndefinedAndEmpty($channelSetup, 'is_get_payment_data', 0), + "is_selected" => $channelSetup['is_selected'], + "status" => 1, + "updated_by" => $params['user_id'], + "updated_at" => time() + ]; + + $updateStatus = $this->update($updateData['id'], $setupData); + + if($updateStatus['status'] != 'success'){ + throw new ApiErrorException($updateStatus['message']); + } + } + } + + } + + + $getChannelMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ], + 'firstRow' => 1 + ]; + $getChannelMappingResponse = $this->propertyChannelMappingService->select($getChannelMappingRequest); + $getChannelMappingResponse = $getChannelMappingResponse['status'] ? $getChannelMappingResponse['data'] : []; + + $channelTaxCriteria = ["criteria" => [["field" => "status", "condition" => "=", "value" => 1]]]; + $channelTaxes = $this->propertyChannelTaxRepository->findByCriteria($channelTaxCriteria, ['id', 'name', 'language_key', 'code']); + $channelTaxes = $channelTaxes ? $channelTaxes : []; + + if(isset($params['channel_tax']) || is_null($params['channel_tax'])) { + $this->propertyChannelMappingService->update($getChannelMappingResponse['id'], ['channel_tax_id' => $params['channel_tax']]); + } + + $response = ['status' => 1 , 'message' => '', 'data' => []]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + +} diff --git a/app/Core/Service/PropertyChannelCategoryService.php b/app/Core/Service/PropertyChannelCategoryService.php new file mode 100644 index 0000000..13a8448 --- /dev/null +++ b/app/Core/Service/PropertyChannelCategoryService.php @@ -0,0 +1,122 @@ +propertyChannelCategoryRepository = $propertyChannelCategoryRepository; + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "name" => fillOnUndefined($param, "name"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->propertyChannelCategoryRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyChannelCategoryRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyChannelCategories($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + ]; + + $data = $this->propertyChannelCategoryRepository->findByCriteria($criteria, ['id', 'name', 'language_key']); + + + $response = [ + 'status' => true, + 'data' => $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); + } + + + + +} diff --git a/app/Core/Service/PropertyChannelContactService.php b/app/Core/Service/PropertyChannelContactService.php new file mode 100644 index 0000000..048cd92 --- /dev/null +++ b/app/Core/Service/PropertyChannelContactService.php @@ -0,0 +1,256 @@ +propertyChannelContactRepository = $propertyChannelContactRepository; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = + [ + 'property_channel_mapping_id' => fillOnUndefined($params, 'property_channel_mapping_id'), + 'name' => fillOnUndefined($params, 'name'), + 'email' => fillOnUndefined($params, 'email'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + + ]; + + $userCreateResult = $this->propertyChannelContactRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('This e-mail address already in list'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyChannelContactRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyChannelContactRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function add($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = + [ + 'property_channel_mapping_id' => fillOnUndefined($params, 'property_channel_mapping_id'), + 'name' => fillOnUndefined($params, 'name'), + 'email' => fillOnUndefined($params, 'email'), + "status" => fillOnUndefined($params, "status", 1), + "user_id" => fillOnUndefined($params, "user_id"), + ]; + + + $userCreateResult = $this->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new ApiErrorException($userCreateResult['message']); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getContact($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + if(!isset($params['id'])){ + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_channel_mapping_id', 'condition' => '=', 'value' => $params['property_channel_mapping_id']] + ], + 'orderBy' => [ + ['field' => 'name', 'value' => 'ASC'] + ] + ]; + + $contactData = $this->propertyChannelContactRepository->findByCriteria($criteria); + $contactData = $contactData ? $contactData : [] ; + } + else{ + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['id']], + ['field' => 'property_channel_mapping_id', 'condition' => '=', 'value' => $params['property_channel_mapping_id']] + ], + 'firstRow' => true + ]; + + $contactData = $this->propertyChannelContactRepository->findByCriteria($criteria); + if (!$contactData) { + throw new ApiErrorException('Unvalid Contact'); + } + } + + + $response = [ + 'status' => true, + 'data' => $contactData, + ]; + + + } 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); + } + + public function delete($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + + $request = [ + 'criteria' => [ + ['field' => 'property_channel_mapping_id', 'condition' => '=', 'value' => $params['property_channel_mapping_id']], + ['field' => 'id', 'condition' => '=', 'value' => $params['id']], + ] + ]; + + + $response = $this->propertyChannelContactRepository->delete($request); + if(!$response){ + throw new ApiErrorException('No record to delete'); + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $response['data']]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + +} diff --git a/app/Core/Service/PropertyChannelCouponService.php b/app/Core/Service/PropertyChannelCouponService.php new file mode 100644 index 0000000..c728ee1 --- /dev/null +++ b/app/Core/Service/PropertyChannelCouponService.php @@ -0,0 +1,179 @@ +propertyChannelCouponRepository = $propertyChannelCouponRepository; + $this->propertyChannelCouponValidator = $propertyChannelCouponValidator; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyChannelCouponRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyChannelCouponValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = [ + "title" => fillOnUndefined($param, "title"), + "property_id" => fillOnUndefined($param, "property_id"), + "channel_id" => fillOnUndefined($param, "channel_id"), + "code" => fillOnUndefined($param, "code"), + "start_date" => fillOnUndefined($param, "start_date"), + "end_date" => fillOnUndefined($param, "end_date"), + "reservation_start_date" => fillOnUndefined($param, "reservation_start_date"), + "reservation_end_date" => fillOnUndefined($param, "reservation_end_date"), + 'is_notify' => fillOnUndefined($param, 'is_notify'), + 'email' => fillOnUndefined($param, 'email'), + "status" => fillOnUndefined($param, "status", 1), + "type" => fillOnUndefined($param, "type"), + "value" => fillOnUndefined($param, "value"), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $insertDataResult = $this->propertyChannelCouponRepository->create($insertData); + if ($insertDataResult['status'] != 'success') { + throw new Exception($insertDataResult['message']); + } + + $insertDataResult = $insertDataResult["data"]; + + $response = [ + 'status' => true, + 'data' => $insertDataResult, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + + public function update($id, $params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /*$validationResult = $this->propertyAddonValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + + $updateParamAvailable = ['title', 'code', 'start_date', 'end_date', 'reservation_start_date', 'reservation_end_date', 'checked', 'used', 'type', 'value', 'is_notify', 'email' , 'status']; + foreach ($params as $key => $value) { + if (!in_array($key, $updateParamAvailable)) { + unset($params[$key]); + } + } + + $result = $this->propertyChannelCouponRepository->update($id, $params); + if ($result['status'] != 'success') { + throw new Exception($result['message']); + } + + $result = $result["data"]; + + $response = [ + 'status' => true, + 'data' => $result, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + + public function delete($deleteThisId) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $deleteThisId = is_array($deleteThisId) ? $deleteThisId : [$deleteThisId]; + $deleteThisArray = $this->propertyChannelCouponRepository->destroy($deleteThisId); + + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyChannelGroupChannelMappingService.php b/app/Core/Service/PropertyChannelGroupChannelMappingService.php new file mode 100644 index 0000000..6f01f1c --- /dev/null +++ b/app/Core/Service/PropertyChannelGroupChannelMappingService.php @@ -0,0 +1,131 @@ +propertyChannelGroupChannelMappingRepository = $propertyChannelGroupChannelMappingRepository; + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /* Todo: validator + $validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + */ + + $insertData = + [ + 'channel_group_id' => fillOnUndefined($params, 'channel_group_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "created_by", 0), + "updated_by" => fillOnUndefined($params, "updated_by", 0), + "created_at" => time(), + "updated_at" => time(), + ]; + + $insertResponse = $this->propertyChannelGroupChannelMappingRepository->create($insertData); + + if ($insertResponse['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $insertResponse["data"] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyChannelGroupChannelMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyChannelGroupChannelMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + +} diff --git a/app/Core/Service/PropertyChannelGroupService.php b/app/Core/Service/PropertyChannelGroupService.php new file mode 100644 index 0000000..0222a66 --- /dev/null +++ b/app/Core/Service/PropertyChannelGroupService.php @@ -0,0 +1,278 @@ +propertyChannelGroupRepository = $propertyChannelGroupRepository; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + /* Todo: validator + $validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + */ + + $insertData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "created_by", 0), + "updated_by" => fillOnUndefined($params, "updated_by", 0), + "created_at" => time(), + "updated_at" => time(), + ]; + + $insertResponse = $this->propertyChannelGroupRepository->create($insertData); + + if ($insertResponse['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $insertResponse["data"] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyChannelGroupRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyChannelGroupRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyChannelGroup($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + if(fillOnUndefined($params, 'property_id', null) == null){ + throw new ApiErrorException(lang('property_id is required')); + } + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + ]; + + if(isset($params['id'])){ + $criteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['channelGroupActiveChannels'], + 'firstRow' => true + ]; + } + + + $groups = $this->propertyChannelGroupRepository->findByCriteria($criteria, ['id', 'name','status']); + $groups = $groups ? $groups : [] ; + + if(isset($params['id'])){ + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + + $propertyChannel = $this->propertyChannelService->getPropertyChannels($requestParams); + if($propertyChannel['status'] != 'success'){ + throw new ApiErrorException($propertyChannel['message']); + } + + + $propertyChannelCollection = collect($propertyChannel['data']); + $filterPropertyChannels = $propertyChannelCollection->where('is_selected', true); + $filterPropertyChannels = $filterPropertyChannels->where('is_pending', false); + $selectedPropertyChannels = $filterPropertyChannels->all(); + + $groupChannelCollection = collect($groups["channel_group_active_channels"]); + + + foreach ($selectedPropertyChannels as $k=>$v){ + + $filtergroupChannel = $groupChannelCollection->where('channel_id', $v['id']); + $isChannelMatched = $filtergroupChannel->all(); + + $selectedPropertyChannels[$k]["in_group"] = false; + if(count($isChannelMatched)){ + $selectedPropertyChannels[$k]["in_group"] = true; + } + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $v['id']], + ['field' => 'property_availability_type_id', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $availabilityTypeCheck = $this->propertyChannelMappingRepository->findByCriteria($criteria); + if(!count($availabilityTypeCheck)){ + unset($selectedPropertyChannels[$k]); + } + + } + + $groups["channel_group_active_channels"] = $selectedPropertyChannels; + + } + + + $response = [ + 'status' => true, + 'data' => $groups, + ]; + + } 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); + } + + public function getPropertyChannelGroupActiveChannels($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + if(fillOnUndefined($params, 'property_id', null) == null){ + throw new ApiErrorException(lang('property_id is required')); + } + + $criteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['channel_group_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['channelGroupActiveChannels','channelGroupActiveChannels.channel'], + 'firstRow' => true + ]; + + + $group = $this->propertyChannelGroupRepository->findByCriteria($criteria, ['id', 'name','status']); + $group = $group ? $group : [] ; + + $response = [ + 'status' => true, + 'data' => $group, + ]; + + } 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); + } + + + + + +} diff --git a/app/Core/Service/PropertyChannelMappingService.php b/app/Core/Service/PropertyChannelMappingService.php new file mode 100644 index 0000000..639d0b4 --- /dev/null +++ b/app/Core/Service/PropertyChannelMappingService.php @@ -0,0 +1,1214 @@ +request = $request; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + $this->propertyChannelMappingAddValidator = $propertyChannelMappingAddValidator; + $this->propertyChannelMappingRemoveValidator = $propertyChannelMappingRemoveValidator; + $this->propertyChannelMappingUpdateValidator = $propertyChannelMappingUpdateValidator; + $this->propertyChannelSetupValidator = $propertyChannelSetupValidator ; + $this->propertyBookingTypeRepository = $propertyBookingTypeRepository; + $this->propertyBookingPaymentTypeRepository = $propertyBookingPaymentTypeRepository; + $this->propertyAvailabilityTypeRepository = $propertyAvailabilityTypeRepository ; + $this->propertyRoomPricingTypeRepository = $propertyRoomPricingTypeRepository ; + $this->propertyRoomRepository = $propertyRoomRepository ; + $this->propertyRoomRateChannelMappingRepository = $propertyRoomRateChannelMappingRepository ; + $this->propertyChannelSetupAddValidator = $propertyChannelSetupAddValidator ; + $this->propertyChannelSetupUpdateValidator = $propertyChannelSetupUpdateValidator ; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService ; + $this->propertyBookingEngineService = $propertyBookingEngineService; + $this->propertyChannelSetupWithMissingParametersAddValidator = $propertyChannelSetupWithMissingParametersAddValidator; + $this->propertyRoomService = $propertyRoomService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomRatePriceRepository = $propertyRoomRatePriceRepository; + $this->propertyChannelTaxRepository = $propertyChannelTaxRepository; + + } + + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->propertyChannelMappingRepository->findByCriteria($param, $column); + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyChannelMappingData = + [ + "channel_id" => fillOnUndefined($param, "user_id"), + "property_id" => fillOnUndefined($param, "property_id"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "created_by"), + + ]; + + + $propertyCreateResult = $this->propertyChannelMappingRepository->create($propertyChannelMappingData); + + if ($propertyCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $propertyCreateResult["data"]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyChannelMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function addPropertyChannelMapping($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $validationResult = $this->propertyChannelMappingAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $criteria = + [ + "criteria" => + [ + ["field" => "property_id", "condition" => "=", "value" => $params['property_id']] + ] + ]; + $oldMappingList = $this->propertyChannelMappingRepository->findByCriteria($criteria, ['id', 'property_id', 'channel_id', 'status']); + + + foreach ($params['channels'] as $addChannel) { + + $checkData = collect($oldMappingList) + ->where('property_id' , '=', $params['property_id']) + ->where('channel_id', '=', $addChannel) + ->first(); + + if(!$checkData){ + $addPropertyChannelMappingData = [ + "property_id" => $params['property_id'], + "channel_id" => $addChannel, + "status" => 1, + "created_by" => $params['user_id'], + "updated_by" => $params['user_id'], + ]; + $createStatus = $this->propertyChannelMappingRepository->create($addPropertyChannelMappingData); + if($createStatus['status'] != 'success'){ + throw new Exception('api-unknown_error'); + } + }else{ + + $updatePropertyChannelMappingData = [ + "status" => 1, + "updated_by" => $params['user_id'] + ]; + $updateResult = $this->propertyChannelMappingRepository->update($checkData['id'], $updatePropertyChannelMappingData) ; + if($updateResult['status'] != 'success'){ + throw new Exception('api-unknown_error'); + } + + } + } + + $response = ['status' => 1 , 'message' => '', 'data' => []]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyChannelMapping($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $getPropertyCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel.propertyChannelCategory', 'property'] + + ]; + + $getChannels = $this->propertyChannelMappingRepository->findByCriteria($getPropertyCriteria, ['id', 'property_id', 'channel_id']) ; + + $response = ['status' => 1 , 'message' => '', 'data' => ['property_channel_mapping' => $getChannels]]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function removePropertyChannelMapping($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $validationResult = $this->propertyChannelMappingRemoveValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + foreach ($params['channels'] as $removeChannel) { + + + $updatePropertyCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id'] ], + ['field' => 'channel_id', 'condition' => '=', 'value' => $removeChannel], + ] + + ]; + + $updatePropertyChannelMappingData = [ + "status" => 0, + "updated_by" => $params['user_id'], + "updated_at" => time() + ]; + + $updateResult = $this->propertyChannelMappingRepository->updateWhere($updatePropertyCriteria, $updatePropertyChannelMappingData) ; + if($updateResult['status'] != 'success'){ + throw new Exception('api-unknown_error'); + } + } + + $response = ['status' => 1 , 'message' => '', 'data' => []]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updatePropertyChannelMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try + { + $validationResult = $this->propertyChannelMappingUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $addPropertyChannelMapping = []; + $addChannelList = collect($params['property_channel']) + ->where('is_selected' ,'=', true); + $addChannelIds = $addChannelList->keyBy("id")->keys()->toArray(); + + $removeChannelList = collect($params['property_channel']) + ->where('is_selected' ,'=', false); + + $removeChannelIds = $removeChannelList->keyBy("id")->keys()->toArray(); + + $checkPropertyMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + "whereIn" => [ + ["field" => "channel_id", "value" => $addChannelIds] + ] + ]; + $checkPropertyMappingResponse = $this->propertyChannelMappingRepository->findByCriteria($checkPropertyMappingRequest,['id', 'property_id', 'channel_id']); + + $checkPropertyMappingResponse = $checkPropertyMappingResponse ? $checkPropertyMappingResponse : [] ; + + + $checkPropertyMappingCollect = collect($checkPropertyMappingResponse); + + foreach ($addChannelList->toArray() as $key => $param) + { + $checkPropertyMapping = $checkPropertyMappingCollect->where('property_id', '=', $params['property_id']) + ->where('channel_id', '=', $param['id']) + ->first() ; + if(!$checkPropertyMapping){ + $addPropertyChannelMapping[] = + [ + 'property_id' => $params['property_id'], + 'channel_id' => fillOnUndefined($param, 'id'), + 'created_by' => $params['user_id'], + 'updated_by' => $params['user_id'], + ]; + }else{ + $updatePropertyChannelMapping = + [ + 'status' => 1, + 'updated_by' => $params['user_id'], + ]; + + $response = $this->propertyChannelMappingRepository->update($checkPropertyMapping['id'], $updatePropertyChannelMapping); + if ($response['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + $response = $this->propertyChannelMappingRepository->insert($addPropertyChannelMapping); + if ($response['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + if($removeChannelIds){ + + $findCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + "whereIn" => + [ + ["field" => "channel_id", "value" => $removeChannelIds] + ] + ]; + $deletePropertyChannelMapping = $this->propertyChannelMappingRepository->findByCriteria($findCriteria); + $deleteThisIds = []; + foreach ($deletePropertyChannelMapping as $deleteThisItem){ + + $updatePropertyChannelMapping = + [ + 'status' => 0, + 'updated_by' => $params['user_id'], + ]; + $response = $this->propertyChannelMappingRepository->update($deleteThisItem['id'], $updatePropertyChannelMapping); + if ($response['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + + if ($response['status'] != 'success') { + throw new ApiErrorException(lang('Data is not added')) ; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + public function addPropertyChannelSetup($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + DB::beginTransaction(); + + $criteria = + [ + "criteria" => + [ + ["field" => "property_id", "condition" => "=", "value" => $params['property_id']], + ["field" => "channel_id", "condition" => "=", "value" => $params['channel_id']], + ], + 'firstRow' => true + ]; + $oldMappingList = $this->propertyChannelMappingRepository->findByCriteria($criteria); + + $roomCriteria = [ + "criteria" => + [ + ["field" => "property_id", "condition" => "=", "value" => $params['property_id']] + ], + 'with' => ['propertyRoomRateMapping.propertyRoomRate'] + ]; + + $rooms = $this->propertyRoomRepository->findByCriteria($roomCriteria) ; + $rooms = $rooms ? $rooms : [] ; + + $roomRateMappingIds = [] ; + foreach ($rooms as $room) { + foreach ($room['property_room_rate_mapping'] as $item) { + if(!in_array($params['channel_id'],[5]) && $item['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + $roomRateMappingIds[] = $item['id'] ; + } + } + + if(!$oldMappingList){ + + $addPropertyChannelMappingData = [ + "property_id" => $params['property_id'], + "channel_id" => $params['channel_id'], + "currency_code" => $params['currency_code'], + "property_booking_type_id" => $params['booking_type_id'], + "property_availability_type_id" => $params['availability_type_id'], + "property_room_pricing_type_id" => $params['room_pricing_type_id'], + "connected_channel_id" => fillOnUndefined($params,'connected_channel_id'), + "connected_channel_action" => fillOnUndefined($params,'connected_channel_action'), + "contract_file" => null, + 'token' => getGuid(), + "status" => 1, + "created_by" => $params['user_id'], + "updated_by" => $params['user_id'], + ]; + + + if($params['contract_file'] != null) { + + $contractFileUploadParams = [ + 'property_id' => $params['property_id'], + 'contract_file' => $params['contract_file'], + 'channel_id' => $params['channel_id'], + ]; + + $contractFileUpload = $this->propertyBookingEngineService->contractFileUpload($contractFileUploadParams); + if ($contractFileUpload['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $addPropertyChannelMappingData["contract_file"] = $contractFileUpload["data"]["contract_file_path"]; + + } + + + + $validationResult = $this->propertyChannelSetupAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $createStatus = $this->propertyChannelMappingRepository->create($addPropertyChannelMappingData); + + if($createStatus['status'] != 'success'){ + throw new Exception('api-unknown_error'); + } + + $roomRateChannelMappingData = [] ; + + foreach ($roomRateMappingIds as $roomRateMappingId) { + $roomRateChannelMappingData[] = [ + "property_id" => $params['property_id'], + "channel_id" => $params['channel_id'], + "room_rate_mapping_id" => $roomRateMappingId, + "has_date" => 0, + "status" => 1, + "created_by" => $params['user_id'], + "updated_by" => $params['user_id'], + "created_at" => time(), + "updated_at" => time(), + ]; + } + + if($roomRateChannelMappingData){ + $insertRoomRateChannelMappingResult = $this->propertyRoomRateChannelMappingRepository->insert($roomRateChannelMappingData); + if ($insertRoomRateChannelMappingResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $channelTokenParam = [ + "property_id" => $params['property_id'], + "channel_id" => $params['channel_id'], + "created_by" => $params['user_id'], + "updated_by" => $params['user_id'], + ]; + + $createBookingEngineToken = $this->propertyBookingEngineService->createBookingEngineToken($channelTokenParam); + if ($createBookingEngineToken['status'] != 'success') { + throw new ApiErrorException($createBookingEngineToken['message']); + } + + + }else{ + + $updatePropertyChannelMappingData = [ + "currency_code" => $params['currency_code'], + "property_booking_type_id" => $params['booking_type_id'], + "property_availability_type_id" => $params['availability_type_id'], + "property_room_pricing_type_id" => $params['room_pricing_type_id'], + "connected_channel_id" => fillOnUndefined($params,'connected_channel_id'), + "connected_channel_action" => fillOnUndefined($params,'connected_channel_action'), + "updated_by" => $params['user_id'], + "status" => 1, + "updated_at" => time(), + ]; + + $validationResult = $this->propertyChannelSetupUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + if($params['contract_file'] != null){ + $contractFileUploadParams = [ + 'property_id' => $params['property_id'], + 'channel_id' => $params['channel_id'], + 'contract_file' => $params['contract_file'], + ]; + + $contractFileUpload = $this->propertyBookingEngineService->contractFileUpload($contractFileUploadParams); + if ($contractFileUpload['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $updatePropertyChannelMappingData["contract_file"] = $contractFileUpload["data"]["contract_file_path"]; + } + + + + $updateStatus = $this->update($oldMappingList['id'], $updatePropertyChannelMappingData); + + if($updateStatus['status'] != 'success'){ + throw new ApiErrorException($updateStatus['message']); + } + + if ($oldMappingList['property_availability_type_id'] != $params['availability_type_id']) { + $dropParams = [ + 'property_id' => $params['property_id'], + 'channel_id' => $params['channel_id'], + ]; + $deleteOldAvailability = $this->propertyRoomAvailabilityService->dropPropertyChannelGuaranteedAndLimitedAvailability($dropParams); + if ($deleteOldAvailability['status'] != 'success') { + throw new ApiErrorException($deleteOldAvailability['message']); + } + } + + $channelTokenParam = [ + "property_id" => $params['property_id'], + "channel_id" => $params['channel_id'], + "updated_by" => $params['user_id'], + ]; + + $reactivateBookingEngineToken = $this->propertyBookingEngineService->reactivateBookingEngineToken($channelTokenParam); + if ($reactivateBookingEngineToken['status'] != 'success') { + throw new ApiErrorException($reactivateBookingEngineToken['message']); + } + } + + //Booking Engine Channel - Channel Manager Replication + if($params['channel_id'] == 1 && !empty($params['connected_channel_id']) && $params['connected_channel_id'] == 5) { + + + //Delete Old Prices + $oldRoomRatesCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()], + ] + ]; + + $oldRoomRates = $this->propertyRoomRatePriceRepository->findbyCriteria($oldRoomRatesCriteria, ['id']); + if(!empty($oldRoomRates)) { + $oldRoomRatePriceIds = collect($oldRoomRates)->pluck('id')->toArray(); + $deleteRoomRatePrices = $this->propertyRoomRatePriceRepository->destroy($oldRoomRatePriceIds); + if($deleteRoomRatePrices['status'] != 'success') { + throw new ApiErrorException($deleteRoomRatePrices['message']); + } + } + //Delete Old Prices + + $getPropertyRoomInventoryParam = [ + 'property_id' => $params['property_id'], + 'channel_id' => 5, + 'start_date' => Carbon::now()->toDateString(), + 'end_date' => Carbon::now()->addYear()->subDay()->toDateString(), + ]; + + $getPropertyRoomInventory = $this->propertyRoomService->getPropertyRoomInventory($getPropertyRoomInventoryParam); + if($getPropertyRoomInventory['status'] != 'success'){ + throw new ApiErrorException($getPropertyRoomInventory['message']); + } + + $getPropertyRoomInventory = $getPropertyRoomInventory['data']; + + $roomRateReplicationParam = [ + 'property_id' => $params['property_id'], + 'channel_id' => 1, + 'user_id' => $params['user_id'], + 'availability' => [], + 'rates' => [] + ]; + + foreach ($getPropertyRoomInventory as $roomInventory) { + foreach ($roomInventory['property_room_rate_mapping'] as $propertyRoomRate) { + if(isset($propertyRoomRate['prices'][1])) { + foreach ($propertyRoomRate['prices'][1]['price'] as $date => $price) { + $roomRateKey = '1|'.$roomInventory['id'].'|'.$propertyRoomRate['id'].'|'.$date; + //availability_type_id|property_room_id|room_rate_mapping_id|2022-06-22 + if(!is_null($price['value'])) { + $roomRateReplicationParam['rates'][$roomRateKey] = [ + 'setup_type_id' => '1', + 'room_id' => $roomInventory['id'], + 'room_rate_mapping_id' => $propertyRoomRate['id'], + 'date' => $date, + 'amount' => fillOnUndefined($price,'value', ''), + ]; + } + } + } + } + } + + $roomRateReplication = $this->propertyRoomRatePriceService->roomRateUpdate($roomRateReplicationParam); + if ($roomRateReplication['status'] != 'success') { + throw new ApiErrorException($roomRateReplication['message']); + } + + } + + //Booking Engine Channel - Channel Manager Replication + + $response = ['status' => 1 , 'message' => '', 'data' => []]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPropertyChannelSetup($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $bookingTypeCriteria = + [ + "criteria" => + [ + ["field" => "status", "condition" => "=", "value" => 1], + ], + + ]; + $bookingTypes = $this->propertyBookingTypeRepository->findByCriteria($bookingTypeCriteria, ['id', 'name', 'language_key', 'code', 'icon']); + $bookingTypes = $bookingTypes ? $bookingTypes : []; + + + $availabilityTypeCriteria = + [ + "criteria" => + [ + ["field" => "status", "condition" => "=", "value" => 1], + ], + + ]; + $availabilityTypes = $this->propertyAvailabilityTypeRepository->findByCriteria($availabilityTypeCriteria, ['id', 'name', 'language_key', 'code', 'icon']); + $availabilityTypes = $availabilityTypes ? $availabilityTypes : []; + + + $criteria = + [ + "criteria" => + [ + ["field" => "status", "condition" => "=", "value" => 1] + ] + ]; + $propertyRoomPricingTypes = $this->propertyRoomPricingTypeRepository->findByCriteria($criteria); + $propertyRoomPricingTypes = $propertyRoomPricingTypes ? $propertyRoomPricingTypes : []; + + + + $getChannelMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ], + 'firstRow' => 1 + ]; + $getChannelMappingResponse = $this->select($getChannelMappingRequest) ; + + if($getChannelMappingResponse['status'] != 'success'){ + throw new ApiErrorException($getChannelMappingResponse['message']); + } + + $propertyBookingTypeId = NULL; + $propertyAvailabilityTypeId = NULL; + $propertyRoomPricingTypeId = NULL; + $propertyContractFile = NULL; + if(!empty($getChannelMappingResponse['data'])){ + $propertyBookingTypeId = $getChannelMappingResponse['data']['property_booking_type_id']; + $propertyAvailabilityTypeId = $getChannelMappingResponse['data']['property_availability_type_id']; + $propertyRoomPricingTypeId = $getChannelMappingResponse['data']['property_room_pricing_type_id']; + + if(!empty($getChannelMappingResponse['data']['contract_file'])){ + $propertyContractFile = Config::get('app.propertyFilesUrl').$propertyContractFile.$getChannelMappingResponse['data']['contract_file']; + } + } + + $bookingList = [] ; + $propertyRoomPricingTypeList = [] ; + $availabilityList = [] ; + $bulkUpdateOptions = []; + + foreach ($bookingTypes as $bookingType) { + $bookingList[] = [ + "booking_type_id" => $bookingType['id'], + "name" => $bookingType['name'], + "language_key" => $bookingType['language_key'], + "icon" => $bookingType['icon'], + "is_selected" => $bookingType['id'] === $propertyBookingTypeId ? true : false, + ]; + } + + + + foreach ($availabilityTypes as $availabilityType) { + $availabilityList[] = [ + "availability_type_id" => $availabilityType['id'], + "name" => $availabilityType['name'], + "language_key" => $availabilityType['language_key'], + "icon" => $availabilityType['icon'], + "is_selected" => $availabilityType['id'] === $propertyAvailabilityTypeId ? true : false, + ]; + + } + + $roomAvailability = collect($availabilityList)->where('availability_type_id',1)->first(); + $limitedAvailability = collect($availabilityList)->where('availability_type_id',2)->first(); + $guaranteedAvailability = collect($availabilityList)->where('availability_type_id',3)->first(); + + foreach ($availabilityList as $index => $availabilityListItem){ + if($propertyAvailabilityTypeId === 1){ + + $bulkUpdateOptions[] = [ + "name" => $roomAvailability['name'], + "language_key" => $roomAvailability['language_key'], + "type" => "availability", + "availability_type_id" => $roomAvailability['availability_type_id'] + ]; + + $bulkUpdateOptions[] = [ + "name" => $roomAvailability['name'], + "language_key" => $roomAvailability['language_key'], + "type" => "rate", + "availability_type_id" => $roomAvailability['availability_type_id'] + ]; + + $bulkUpdateOptions[] = [ + "name" => $roomAvailability['name'], + "language_key" => $roomAvailability['language_key'], + "type" => "room_stop_sell", + "availability_type_id" => $roomAvailability['availability_type_id'] + ]; + + $bulkUpdateOptions[] = [ + "name" => $roomAvailability['name'], + "language_key" => $roomAvailability['language_key'], + "type" => "rate_stop_sell", + "availability_type_id" => $roomAvailability['availability_type_id'] + ]; + + $bulkUpdateOptions[] = [ + "name" => $roomAvailability['name'], + "language_key" => $roomAvailability['language_key'], + "type" => "min_stay", + "availability_type_id" => $roomAvailability['availability_type_id'] + ]; + + + $bulkUpdateOptions[] = [ + "name" => $roomAvailability['name'], + "language_key" => 'enw-input-quick_pricing', + "type" => "quick_pricing", + "availability_type_id" => $roomAvailability['availability_type_id'] + ]; + + break; + }else if($propertyAvailabilityTypeId === 2){ + + $bulkUpdateOptions[] = [ + "name" => $roomAvailability['name'], + "language_key" => $roomAvailability['language_key'], + "type" => "availability", + "availability_type_id" => $roomAvailability['availability_type_id'] + ]; + + $bulkUpdateOptions[] = [ + "name" => $limitedAvailability['name'], + "language_key" => $limitedAvailability['language_key'], + "type" => "availability", + "availability_type_id" => $limitedAvailability['availability_type_id'] + ]; + + $bulkUpdateOptions[] = [ + "name" => $limitedAvailability['name'], + "language_key" => $limitedAvailability['language_key'], + "type" => "rate", + "availability_type_id" => $limitedAvailability['availability_type_id'] + ]; + break; + }else if($propertyAvailabilityTypeId === 3){ + + $bulkUpdateOptions[] = [ + "name" => $roomAvailability['name'], + "language_key" => $roomAvailability['language_key'], + "type" => "availability", + "availability_type_id" => $roomAvailability['availability_type_id'] + ]; + + $bulkUpdateOptions[] = [ + "name" => $roomAvailability['name'], + "language_key" => $roomAvailability['language_key'], + "type" => "rate", + "availability_type_id" => $roomAvailability['availability_type_id'] + ]; + + $bulkUpdateOptions[] = [ + "name" => $guaranteedAvailability['name'], + "language_key" => $guaranteedAvailability['language_key'], + "type" => "availability", + "availability_type_id" => $guaranteedAvailability['availability_type_id'] + ]; + + $bulkUpdateOptions[] = [ + "name" => $guaranteedAvailability['name'], + "language_key" => $guaranteedAvailability['language_key'], + "type" => "rate", + "availability_type_id" => $guaranteedAvailability['availability_type_id'] + ]; + break; + } + + } + + foreach ($propertyRoomPricingTypes as $propertyRoomPricingType) { + $propertyRoomPricingTypeList[] = [ + "room_pricing_type_id" => $propertyRoomPricingType['id'], + "name" => $propertyRoomPricingType['name'], + "language_key" => $propertyRoomPricingType['language_key'], + "icon" => $propertyRoomPricingType['icon'], + "is_selected" => $propertyRoomPricingType['id'] === $propertyRoomPricingTypeId ? true : false, + ]; + } + + $responseData = [ + 'property_id' => $params['property_id'], + 'channel_id' => $params['channel_id'], + 'booking' => $bookingList, + 'room_price_type' => $propertyRoomPricingTypeList, + 'availability' => $availabilityList, + 'bulk_update_options' => $bulkUpdateOptions, + 'contract_file_url' => $propertyContractFile + ]; + + $response = ['status' => 1 , 'message' => '', 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function checkPropertyChannelMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $getMapping = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $data = $this->propertyChannelMappingRepository->findByCriteria($getMapping); + if(!$data){ + throw new ApiErrorException('api-unknown_error'); + } + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function addPropertyChannelSetupWithMissingParameters($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $criteria = + [ + "criteria" => + [ + ["field" => "property_id", "condition" => "=", "value" => $params['property_id']], + ["field" => "channel_id", "condition" => "=", "value" => $params['channel_id']], + ], + 'firstRow' => true + ]; + $oldMappingList = $this->propertyChannelMappingRepository->findByCriteria($criteria); + + if($oldMappingList){ + + //UPDATE + $updatePropertyChannelMappingData = [ + "updated_by" => $params['user_id'], + "status" => self::STATUS_PENDING, + "updated_at" => time(), + ]; + + $createdOrUpdatedResponse = $this->update($oldMappingList['id'], $updatePropertyChannelMappingData); + if($createdOrUpdatedResponse['status'] != 'success'){ + throw new ApiErrorException($createdOrUpdatedResponse['message']); + } + + }else{ + + //CREATE + $addPropertyChannelMappingData = [ + "property_id" => $params['property_id'], + "channel_id" => $params['channel_id'], + "currency_code" => null, + "property_booking_type_id" => null, + "property_availability_type_id" => null, + "property_room_pricing_type_id" => null, + "status" => self::STATUS_PENDING, + "created_by" => $params['user_id'], + "updated_by" => $params['user_id'], + ]; + + $validationResult = $this->propertyChannelSetupWithMissingParametersAddValidator->validate($addPropertyChannelMappingData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $createdOrUpdatedResponse = $this->propertyChannelMappingRepository->create($addPropertyChannelMappingData); + if($createdOrUpdatedResponse['status'] != 'success'){ + throw new Exception('api-unknown_error'); + } + + } + + if(!empty($createdOrUpdatedResponse['data'])){ + $response['data'] = [ + "property_id" => $createdOrUpdatedResponse['data']['property_id'], + "channel_id" => $createdOrUpdatedResponse['data']['channel_id'], + "currency_code" => $createdOrUpdatedResponse['data']['currency_code'], + "property_booking_type_id" => $createdOrUpdatedResponse['data']['property_booking_type_id'], + "property_availability_type_id" => $createdOrUpdatedResponse['data']['property_availability_type_id'], + "property_room_pricing_type_id" => $createdOrUpdatedResponse['data']['property_room_pricing_type_id'], + "status" => $createdOrUpdatedResponse['data']['status'] + ]; + } + + $response = ['status' => 1 , 'message' => '', 'data' => $response['data'] ]; + + }catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function getPropertyChildChannel($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $getPropertyCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + + ]; + $getChannels = $this->propertyChannelMappingRepository->findByCriteria($getPropertyCriteria) ; + + $childDatas = collect($getChannels)->where('channel.channel_category_id', 4) + ->where('channel.parent_id', '!=', null)->toArray(); + + $responseData = []; + + if(!empty($childDatas)){ + + foreach ($childDatas as $childData){ + $responseData[] = [ + 'channel_mapping_id' => $childData['id'], + 'property_id' => $childData['property_id'], + 'channel_id' => $childData['channel_id'], + 'status' => $childData['status'], + 'channel_name' => $childData['channel']['name'], + 'channel_logo' => $childData['channel']['logo'] + + ]; + } + + } + + $response = ['status' => 1 , 'message' => '', 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function getPropertyChannelMappingForChannelGroup($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $getChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_availability_type_id', 'condition' => '=', 'value' => 1], + ], + 'whereIn' => [ + ['field' => 'channel_id', 'value' => $params['channel_ids']] + ] + ]; + + $getChannels = $this->propertyChannelMappingRepository->findByCriteria($getChannelMappingCriteria) ; + if(!$getChannels){ + throw new ApiErrorException('api-unknown_error'); + } + $response = ['status' => 1 , 'message' => '', 'data' => $getChannels]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getOnlyPropertyChannelMapping($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $getChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ], + 'firstRow' => 1 + ]; + + $getChannels = $this->propertyChannelMappingRepository->findByCriteria($getChannelMappingCriteria) ; + if(!$getChannels){ + throw new ApiErrorException('api-unknown_error'); + } + $response = ['status' => 1 , 'message' => '', 'data' => $getChannels]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyChannelService.php b/app/Core/Service/PropertyChannelService.php new file mode 100644 index 0000000..6f879d8 --- /dev/null +++ b/app/Core/Service/PropertyChannelService.php @@ -0,0 +1,367 @@ +propertyChannelRepository = $propertyChannelRepository; + $this->propertyChannelAddValidator = $propertyChannelAddValidator; + $this->propertyChannelUpdateValidator = $propertyChannelUpdateValidator; + $this->propertyChannelDeleteValidator = $propertyChannelDeleteValidator; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyChannelAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + 'parent_id' => fillOnUndefined($params, 'parent_id'), + 'name' => fillOnUndefined($params, 'name'), + 'official_name' => fillOnUndefined($params, 'official_name'), + 'description' => fillOnUndefined($params, 'description'), + 'channel_category_id' => fillOnUndefined($params, 'channel_category_id'), + 'country_code' => fillOnUndefined($params, 'country_code'), + 'currency_code' => fillOnUndefined($params, 'currency_code'), + 'logo' => fillOnUndefined($params, 'logo'), + 'address' => fillOnUndefined($params, 'address'), + 'zip_code' => fillOnUndefined($params, 'zip_code'), + 'email' => fillOnUndefined($params, 'email'), + 'phone' => fillOnUndefined($params, 'phone'), + 'phone2' => fillOnUndefined($params, 'phone2'), + 'fax' => fillOnUndefined($params, 'fax'), + 'score' => fillOnUndefined($params, 'score'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "created_by", 0), + "updated_by" => fillOnUndefined($params, "updated_by", 0) + + ]; + + $userCreateResult = $this->propertyChannelRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyChannelRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyChannelRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function addPropertyChannel($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = + [ + 'parent_id' => fillOnUndefined($params, 'parent_id'), + 'name' => fillOnUndefined($params, 'name'), + 'official_name' => fillOnUndefined($params, 'official_name'), + 'description' => fillOnUndefined($params, 'description'), + 'channel_category_id' => fillOnUndefined($params, 'channel_category_id'), + 'country_code' => fillOnUndefined($params, 'country_code'), + 'currency_code' => json_encode(fillOnUndefined($params, 'currency_code', [])), + 'logo' => fillOnUndefined($params, 'logo'), + 'address' => fillOnUndefined($params, 'address'), + 'zip_code' => fillOnUndefined($params, 'zip_code'), + 'email' => fillOnUndefined($params, 'email'), + 'phone' => fillOnUndefined($params, 'phone'), + 'phone2' => fillOnUndefined($params, 'phone2'), + 'fax' => fillOnUndefined($params, 'fax'), + 'score' => fillOnUndefined($params, 'score'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + + $userCreateResult = $this->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new ApiErrorException($userCreateResult['message']); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyChannels($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $mappingData = [] ; + + if(isset($params['property_id'])){ + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + + $mappingData = $this->propertyChannelMappingRepository->findByCriteria($criteria, ['id', 'property_id', 'channel_id','connected_channel_id', 'status']); + $mappingData = $mappingData ? $mappingData : [] ; + $mappingData = collect($mappingData)->keyBy('channel_id')->all() ; + } + + $mappingDataPending = [] ; + + if(isset($params['property_id'])){ + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + + $mappingDataPending = $this->propertyChannelMappingRepository->findByCriteria($criteria, ['id', 'property_id', 'channel_id','connected_channel_id', 'status']); + $mappingDataPending = $mappingDataPending ? $mappingDataPending : [] ; + $mappingDataPending = collect($mappingDataPending)->keyBy('channel_id')->all() ; + } + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1 ] + ], + 'with' => ['propertyChannelCategory', 'parentChannel'], + 'orderBy' => [ + ['field' => 'name', 'value' => 'ASC'] + ] + ]; + + + if(isset($params['parent_id'])){ + $params['parent_id'] = $params['parent_id'] == "null" ? null : $params['parent_id']; + $criteria['criteria'][] = ['field' => 'parent_id', 'condition' => '=', 'value' => $params['parent_id'] ] ; + } + if(isset($params['type'])){ + $criteria['criteria'][] = ['field' => 'type', 'condition' => '=', 'value' => $params['type'] ] ; + } + if(isset($params['channel_category_id'])){ + $criteria['criteria'][] = ['field' => 'channel_category_id', 'condition' => '=', 'value' => $params['channel_category_id'] ] ; + } + if(isset($params['country_code'])){ + $criteria['criteria'][] = ['field' => 'country_code', 'condition' => '=', 'value' => $params['country_code'] ] ; + } + + + $data = $this->propertyChannelRepository->findByCriteria($criteria,['id', 'parent_id', 'name', 'official_name', 'description', 'channel_category_id', 'country_code', 'restriction','currency_code', 'logo', 'address', 'zip_code', 'email', 'phone', 'phone2', 'fax', 'score', 'status']); + + + $data = collect($data)->map(function ($channels) use ($params, $mappingData, $mappingDataPending){ + + if($params['property_id']){ + $channels['is_selected'] = isset($mappingData[$channels['id']]) ; + $channels['is_pending'] = isset($mappingDataPending[$channels['id']]) && $mappingDataPending[$channels['id']]['status'] == 2 ? true : false ; + $channels['connected_channel_id'] = isset($mappingData[$channels['id']]) ? $mappingData[$channels['id']]['connected_channel_id'] : null; + } + + $channels["currency_code"] = json_decode($channels["currency_code"], 1); + + $channels['has_children'] = in_array($channels['channel_category_id'], [3,4]) ? true : false; + + return $channels; + }); + + $data = $data->keyBy('id')->toArray(); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function updatePropertyChannel($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyChannelUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $updateData = + [ + 'parent_id' => fillOnUndefined($params, 'parent_id'), + 'name' => fillOnUndefined($params, 'name'), + 'official_name' => fillOnUndefined($params, 'official_name'), + 'description' => fillOnUndefined($params, 'description'), + 'channel_category_id' => fillOnUndefined($params, 'channel_category_id'), + 'country_code' => fillOnUndefined($params, 'country_code'), + 'currency_code' => json_encode(fillOnUndefined($params, 'currency_code', [])), + 'logo' => fillOnUndefined($params, 'logo'), + 'address' => fillOnUndefined($params, 'address'), + 'zip_code' => fillOnUndefined($params, 'zip_code'), + 'email' => fillOnUndefined($params, 'email'), + 'phone' => fillOnUndefined($params, 'phone'), + 'phone2' => fillOnUndefined($params, 'phone2'), + 'fax' => fillOnUndefined($params, 'fax'), + 'score' => fillOnUndefined($params, 'score'), + "updated_by" => fillOnUndefined($params, "user_id", 0) + ]; + + $updateResult = $this->update($params['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException($updateResult['message']); + } + $userData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function checkChannelCategory($channel_id=0, $category_id=0){ + + $response = false; + + $channel = $this->propertyChannelRepository->find($channel_id); + if(isset($channel["channel_category_id"]) && $channel["channel_category_id"] == $category_id){ + $response = true; + } + + return $response; + } + + +} diff --git a/app/Core/Service/PropertyCompetitorGroupService.php b/app/Core/Service/PropertyCompetitorGroupService.php new file mode 100644 index 0000000..e5f6809 --- /dev/null +++ b/app/Core/Service/PropertyCompetitorGroupService.php @@ -0,0 +1,371 @@ +propertyCompetitorGroupRepository = $propertyCompetitorGroupRepository; + $this->propertyCompetitorGroupMappingRepository = $propertyCompetitorGroupMappingRepository; + } + + public function createPropertyCompetitorGroup($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = [ + "property_id" => fillOnUndefined($param, "property_id"), + "name" => fillOnUndefined($param, "name"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $insertDataResult = $this->propertyCompetitorGroupRepository->create($insertData); + if ($insertDataResult['status'] != 'success') { + throw new Exception($insertDataResult['message']); + } + + $insertDataResult = $insertDataResult["data"]; + + $response = [ + 'status' => true, + 'data' => $insertDataResult, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + + public function selectPropertyCompetitorGroup($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyCompetitorGroupRepository->findByCriteria($param, $column); + + $propertyList = []; + foreach ($data as $property) { + $propertyList[] = [ + 'id' => $property['id'], + 'property_id' => $property['property_id'], + 'name' => $property['name'], + 'status' => $property['status'], + ]; + } + + $response = [ + 'status' => true, + 'data' => $propertyList, + ]; + + } 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); + } + + public function syncPropertyCompetitorGroup($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $id = fillOnUndefined($param, "id"); + $propertyId = fillOnUndefined($param, "property_id"); + $name = fillOnUndefined($param, "name"); + $status = fillOnUndefined($param, "status", 1); + $userId = fillOnUndefined($param, "user_id"); + + if ($id) { + // Güncelleme öncesi: kayıt ve yetki kontrolü + $existingRow = $this->propertyCompetitorGroupRepository->findByCriteria([ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $id], + ], + 'firstRow' => 1 + ], ['id','property_id']); + + if (!$existingRow) { + throw new ApiErrorException('Record not found'); + } + + // Eğer dışarıdan property_id gönderildiyse, kayıtla eşleşmesini zorunlu tutalım + if (!is_null($propertyId) && (int)$propertyId !== (int)$existingRow['property_id']) { + // Bu durum, kullanıcının farklı bir property_id ile kayıt güncellemeye çalıştığını gösterir + throw new ApiErrorException('Permission denied for this property'); + } + + // Güvenlik: update sırasında property_id değiştirilmesin + $updateData = [ + "name" => $name, + "status" => $status, + "updated_by" => $userId, + "updated_at" => time(), + ]; + + // Repository update signature: update($id, $data) + $updateResult = $this->propertyCompetitorGroupRepository->update($id, $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception($updateResult['message']); + } + $data = $updateResult["data"]; + } else { + // Ekleme + $insertData = [ + "property_id" => $propertyId, + "name" => $name, + "status" => $status, + "created_by" => $userId, + "updated_by" => $userId, + "created_at" => time(), + "updated_at" => time(), + ]; + + $insertResult = $this->propertyCompetitorGroupRepository->create($insertData); + if ($insertResult['status'] != 'success') { + throw new Exception($insertResult['message']); + } + $data = $insertResult["data"]; + } + + $response = [ + 'status' => true, + 'data' => $data, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + + public function deletePropertyCompetitorGroup($deleteThisId) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $deleteThisId = is_array($deleteThisId) ? $deleteThisId : [$deleteThisId]; + $deleteThisArray = $this->propertyCompetitorGroupRepository->destroy($deleteThisId); + + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + /** + * Mapping GET + * GET isteklerinde meta alanlar (created_by, updated_by, created_at, updated_at) gizlenir. + */ + public function getPropertyCompetitorGroupMapping($param = [], $columns = [ + 'id', + 'competitor_group_id', + 'property_id', + 'competitor_property_source', + 'competitor_property_key', + 'status', + ]) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $propertyId = fillOnUndefined($param, 'property_id'); + $competitorGroupId = fillOnUndefined($param, 'competitor_group_id'); + + if (!$propertyId || !$competitorGroupId) { + throw new ApiErrorException('property_id and competitor_group_id are required'); + } + + $selectParams = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'competitor_group_id', 'condition' => '=', 'value' => $competitorGroupId], + ] + ]; + + $data = $this->propertyCompetitorGroupMappingRepository->findByCriteria($selectParams, $columns); + + $response = [ + 'status' => true, + 'data' => $data, + ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + + /** + * Mapping SYNC + * Tam senkron: önce mevcutları sil, sonra yeni gelenleri ekle. + * Dönüşte ilgili kayıtları (meta alanlar dahil) array olarak döndürür. + */ + public function syncPropertyCompetitorGroupMapping($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $propertyId = fillOnUndefined($param, 'property_id'); + $competitorGroupId = fillOnUndefined($param, 'competitor_group_id'); + $competitors = fillOnUndefined($param, 'competitor', []); + $userId = fillOnUndefined($param, 'user_id'); + + if (!$propertyId || !$competitorGroupId) { + throw new ApiErrorException('property_id and competitor_group_id are required'); + } + + // YETKİ KONTROLÜ: competitor_group_id gerçekten bu property_id'ye ait mi? + // Kullanıcı, farklı bir gruba (başka property'ye ait) yazmaya çalışırsa engelle. + $groupRow = $this->propertyCompetitorGroupRepository->findByCriteria([ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $competitorGroupId], + ], + 'firstRow' => 1 + ], ['id', 'property_id']); + + if (!$groupRow) { + throw new ApiErrorException('Competitor group not found'); + } + + if ((int)$groupRow['property_id'] !== (int)$propertyId) { + // İstenen davranış: kullanıcı farklı bir grup ID gönderirse ve bu grup bu property'e ait değilse izin verme + throw new ApiErrorException('Permission denied for this competitor group'); + } + + // 1) Eski kayıtları temizle (tam eşitleme) — yalnızca ilgili property_id + competitor_group_id çiftini hedefle + // Not: Repository delete() metodunda filtre anahtarı 'criteria' olmalı; 'where' kullanılırsa tüm kayıtlar silinebilir. + $deleteWhere = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'competitor_group_id', 'condition' => '=', 'value' => $competitorGroupId], + ] + ]; + $deleteResult = $this->propertyCompetitorGroupMappingRepository->delete($deleteWhere); + if (($deleteResult['status'] ?? 'success') !== 'success') { + throw new Exception($deleteResult['message'] ?? 'delete_error'); + } + + // 2) Yeni kayıtları ekle + $now = time(); + $rows = []; + foreach ($competitors as $c) { + $source = $c['source'] ?? 'extranetwork'; + $key = $c['key'] ?? null; + if (!$key) { // key zorunlu + continue; + } + $rows[] = [ + 'competitor_group_id' => $competitorGroupId, + 'property_id' => $propertyId, + 'competitor_property_source' => $source ?: 'extranetwork', + 'competitor_property_key' => (string)$key, + 'status' => 1, + 'created_by' => $userId, + 'updated_by' => $userId, + 'created_at' => $now, + 'updated_at' => $now, + ]; + } + + if (!empty($rows)) { + $insertResult = $this->propertyCompetitorGroupMappingRepository->insert($rows); + if (($insertResult['status'] ?? '') !== 'success') { + throw new Exception($insertResult['message'] ?? 'insert_error'); + } + } + + // 3) Güncel kayıtları geri döndür + $selectParams = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'competitor_group_id', 'condition' => '=', 'value' => $competitorGroupId], + ] + ]; + + $columns = [ + 'id', + 'competitor_group_id', + 'property_id', + 'competitor_property_source', + 'competitor_property_key', + 'status', + 'created_by', + 'updated_by', + 'created_at', + 'updated_at', + ]; + + $rowsResult = $this->propertyCompetitorGroupMappingRepository->findByCriteria($selectParams, $columns); + + $response = [ + 'status' => true, + 'data' => $rowsResult, + ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyConfigService.php b/app/Core/Service/PropertyConfigService.php new file mode 100644 index 0000000..c98bba5 --- /dev/null +++ b/app/Core/Service/PropertyConfigService.php @@ -0,0 +1,475 @@ +propertyConfigRepository = $propertyConfigRepository; + $this->propertyConfigCreateValidator = $propertyConfigCreateValidator; + $this->siteConfigRepository = $siteConfigRepository; + $this->propertyFactService = $propertyFactService; + $this->rateKey = 'profile_rate_value'; + $this->siteConfigKey = 'profile_rate_mapping'; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "config_key" => fillOnUndefined($param, "config_key"), + "config_value_json" => fillOnUndefined($param, "config_value_json"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + + $validationResult = $this->propertyConfigCreateValidator->validate($insertData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $userCreateResult = $this->propertyConfigRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyConfigRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyConfigRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyConfigRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyConfig($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + $configRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + + ]; + + if ($params['config_keys']) { + + $configRequest['whereIn'] = [ + ["field" => "config_key", "value" => $params['config_keys']] + ]; + } + $configResponse = $this->select($configRequest, ['property_id', 'config_key', 'config_value_json']); + + if ($configResponse['status'] != 'success') { + throw new ApiErrorException(lang('Executive Data Not Loaded')); + } + $configCollection = collect($configResponse['data']); + + foreach ($configCollection as $config) { + $configItem['property_id'] = $config['property_id']; + $configItem['config_key'] = $config['config_key']; + $configItem['config_value_json'] = $config['config_value_json']; + $return[] = $configItem; + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['property_config' => $return]]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function propertyConfigUpdate($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + $requestKeys = collect($params['config_keys'])->keyBy("config_key")->keys()->toArray(); + + + $configRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + "whereIn" => [ + ["field" => "config_key", "value" => $requestKeys] + ] + ]; + + $configResponse = $this->select($configRequest); + + $configCollection = collect($configResponse['data']); + + foreach ($params['config_keys'] as $config_key) { + + $configKeyCheck = $configCollection->where('config_key', '=', $config_key['config_key']) + ->first(); + + if ($configKeyCheck) { + $updateData = [ + 'config_value_json' => $config_key['config_value_json'], + 'updated_by' => $params['user_id'], + 'status' => 1, + ]; + $updateStatus = $this->update($configKeyCheck['id'], $updateData); + + if ($updateStatus['status'] != 'success') { + throw new Exception($updateStatus['message']); + } + } else { + $createData = [ + 'property_id' => $params['property_id'], + 'config_key' => $config_key['config_key'], + 'config_value_json' => $config_key['config_value_json'], + 'updated_by' => $params['user_id'], + 'created_by' => $params['user_id'], + ]; + $createStatus = $this->create($createData); + if ($createStatus['status'] != 'success') { + throw new Exception($createStatus['message']); + } + } + } + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'config_keys' => $requestKeys + + ]; + + $propertyConfig = $this->getPropertyConfig($requestParams); + + if ($propertyConfig['status'] != 'success') { + throw new ApiErrorException($propertyConfig['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyConfig['data']]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function checkRateForId($params) + { + + $response = $params; + $response['property_rate_for'] = $response['property_rate_for'] == 'Property.Fact.SubCategoryFacts' ? 'Property.Fact.SubCategoryFacts' . '.' . $response['sub_category_id'] : $response['property_rate_for']; + return $response; + } + + public function rateProperty($rateParams) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $rateParams = $this->checkRateForId($rateParams); + $selectProperty = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $rateParams['property_id']], + ['field' => 'config_key', 'condition' => '=', 'value' => $this->rateKey], + ], + 'firstRow' => 1 + + ]; + $propertyRates = $this->select($selectProperty); + + if ($propertyRates['status'] != "success") { + throw new Exception($propertyRates['message']); + } + + if ($propertyRates['data'] == null) { + $configValue[] = $rateParams['property_rate_for']; + $configValueJson = json_encode($configValue); + $updateParams = [ + 'property_id' => $rateParams['property_id'], + 'config_key' => $this->rateKey, + 'config_value_json' => $configValueJson, + 'status' => 1, + 'created_by' => $rateParams['user_id'], + 'updated_by' => $rateParams['user_id'], + 'created_at' => time(), + 'updated_at' => time() + ]; + $updateStatus = $this->create($updateParams); + if ($updateStatus['status'] != 'success') { + throw new Exception($updateStatus['message']); + } + } else { + $propertyRates = $propertyRates['data'] ? $propertyRates['data'] : []; + $configValue = json_decode($propertyRates['config_value_json'], 1); + $configValue = is_array($configValue) ? $configValue : []; + if (!in_array($rateParams['property_rate_for'], $configValue)) { + array_push($configValue, $rateParams['property_rate_for']); + $configValueJson = json_encode($configValue); + $updateParams = [ + 'config_value_json' => $configValueJson, + 'updated_by' => $rateParams['user_id'], + 'updated_at' => time() + ]; + $updateStatus = $this->update($propertyRates['id'], $updateParams); + if ($updateStatus['status'] != 'success') { + throw new Exception($updateStatus['message']); + } + } + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $configValue]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function propertyDashBoard($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + + $siteConfigRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'config_key', 'condition' => '=', 'value' => $this->siteConfigKey], + ], + 'firstRow' => 1 + ]; + + $siteConfig = $this->siteConfigRepository->findByCriteria($siteConfigRequest); + + $siteConfig = $siteConfig ? $siteConfig : []; + $rateConfig = fillOnUndefined($siteConfig, 'config_value_json', null); + $rateConfig = json_decode($rateConfig, 1); + $dashboardSections = is_array($rateConfig) && isset($rateConfig['dashboard_section']) ? $rateConfig['dashboard_section'] : []; + + + foreach ($dashboardSections as $key => $value) { + $return[$key]['title'] = $value['title']; + $return[$key]['icon'] = $value['icon']; + $return[$key]['description'] = $value['description']; + if (isset($value['menus'])) { + $return[$key]['menus'] = $value['menus']; + } + } + + $amenityMenu = $this->propertyFactService->getSubCategoryMenus(['parent_id' => 1, 'sub_category_id' => null]); + if ($amenityMenu['status'] != 'success') { + throw new ApiErrorException($amenityMenu['message']); + } + + $facilityMenu = $this->propertyFactService->getSubCategoryMenus(['parent_id' => 44, 'sub_category_id' => null]); + if ($facilityMenu['status'] != 'success') { + throw new ApiErrorException($facilityMenu['message']); + } + + + $return['amenities']['menus'] = $amenityMenu['data']['menu']; + $return['facility']['menus'] = $facilityMenu['data']['menu']; + + $wizardParams = [ + 'menu_array' => $return, + 'property_id' => $params['property_id'] + ]; + + + $return = $this->checkWizard($wizardParams); + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['dashboard' => $return]]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + + public function checkWizard($params) + { + + $selectProperty = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'config_key', 'condition' => '=', 'value' => $this->rateKey], + ], + 'firstRow' => 1 + ]; + $propertyRates = $this->propertyConfigRepository->findByCriteria($selectProperty); + $propertyRates = $propertyRates ? $propertyRates : []; + $rates = fillOnUndefined($propertyRates, 'config_value_json', '[]'); + $rates = json_decode($rates, 1); + $return = []; + foreach ($params['menu_array'] as $key => $value) { + $return[$key] = $value; + $menus = []; + $completed = 0; + if (isset($value['menus'])) { + foreach ($value['menus'] as $menu) { + $menu['wizard_completed'] = array_search($menu['alias'], $rates) > -1; + $completed = array_search($menu['alias'], $rates) > -1 ? $completed + 1 : $completed; + $menus[] = $menu; + } + $return[$key]['menus'] = $menus; + $return[$key]['total_module'] = count($menus); + $return[$key]['completed_module'] = $completed; + } + } + return $return; + } +} diff --git a/app/Core/Service/PropertyContactService.php b/app/Core/Service/PropertyContactService.php new file mode 100644 index 0000000..a5a4932 --- /dev/null +++ b/app/Core/Service/PropertyContactService.php @@ -0,0 +1,330 @@ +propertyContactRepository = $propertyContactRepository; + $this->propertyContactCreateValidator = $propertyContactCreateValidator; + $this->propertyContactUpdateValidator = $propertyContactUpdateValidator; + $this->propertyLocationUpdateValidator = $propertyLocationUpdateValidator; + + } + + /** + * @param array $param + * + * @return array + */ + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $propertyContactData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "phone_code" => fillOnUndefined($param, "phone_code"), + "phone" => fillOnUndefined($param, "phone"), + "mobile_code" => fillOnUndefined($param, "mobile_code"), + "mobile" => fillOnUndefined($param, "mobile"), + "mobile2_code" => fillOnUndefined($param, "mobile_code"), + "mobile2" => fillOnUndefined($param, "mobile2"), + "fax_code" => fillOnUndefined($param, "fax_code"), + "fax" => fillOnUndefined($param, "fax"), + "email" => fillOnUndefined($param, "email"), + "web" => fillOnUndefined($param, "web"), + "social_media_addresses" => fillOnUndefined($param, "social_media_addresses"), + "zip_code" => fillOnUndefined($param, "zip_code"), + "address" => fillOnUndefined($param, "address"), + "latitude" => fillOnUndefined($param, "latitude"), + "longitude" => fillOnUndefined($param, "longitude"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + // todo: destination service will added for destination_id check + $validationResult = $this->propertyContactCreateValidator->validate($propertyContactData); + + //todo: validation section will be updated + Validator::make($propertyContactData, + ['property_id' => 'required|integer|unique:property_contact,property_id'], + ['property_id.property_contact' => '"property_id format must be unique"']); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $propertyContactCreateResult = $this->propertyContactRepository->create($propertyContactData); + + if ($propertyContactCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $contactData = $propertyContactCreateResult["data"]; + + $response = [ + 'status' => true, + 'data' => $contactData + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + } + + /** + * @param array $param + * @param array $column + * + * @return array + */ + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyContentRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + + public function update($id, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + if(!isset($param['address'])){ + + $validationResult = $this->propertyContactUpdateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + } + + if(isset($param['address']) || isset($param['latitude']) || isset($param['longitude'])){ + $validationResult = $this->propertyLocationUpdateValidator->validate($param); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + } + + $updateResult = $this->propertyContactRepository->update($id,$param); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + /** + * property contact data list + * + * @param $params + * + * @return array + */ + public function propertyContact($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $contactRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $contactData = $this->propertyContactRepository->findByCriteria($contactRequest); + + $contactData = $contactData ? $contactData : [] ; + + $data = []; + foreach ($contactData as $contact) { + $data = Arr::except($contact,['id','status','created_by','updated_by','created_at','updated_at']); + $data["social_media_addresses"] = json_decode($data["social_media_addresses"], 1) ; + } + + if(!isset($data["social_media_addresses"])){ + $data["social_media_addresses"] = ['facebook'=>'', 'twitter'=>'', 'instagram'=>''] ; + } + + $response = [ + 'status' => 1, + 'message' => '', + 'data' => ['property_contact' => $data] , + ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + + /** + * contact update or create + * + * @param $params + * + * @return array + */ + public function propertyContactUpdateOrCreate($params) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $contactCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $contactData = $this->propertyContactRepository->findByCriteria($contactCriteria); + if (!$contactData) { + $addData = [ + 'property_id' => $params['property_id'], + 'status' => 1, + 'created_by' => $params[ 'user_id' ], + 'updated_by' => $params[ 'user_id' ], + ]; + $saveData = array_merge($addData,$params['contact']); + + if(isset($saveData['social_media_addresses'])){ + $saveData['social_media_addresses'] = json_encode($this->clearSocialMediaAddresses($saveData['social_media_addresses'])); + } + + $createResult = $this->create($saveData); + if($createResult['status'] != 'success'){ + throw new ApiErrorException(lang('Not saved')); + } + $data = Arr::except($createResult['data'],['id','status','created_by','updated_by','created_at','updated_at']); + $data["social_media_addresses"] = json_decode($data["social_media_addresses"], 1) ; + $response = [ + 'status' => true, + 'data' => $data, + ]; + }else { + $saveData = $params['contact']; + if(isset($saveData['social_media_addresses'])){ + $saveData['social_media_addresses'] = json_encode($this->clearSocialMediaAddresses($saveData['social_media_addresses'])); + } + + $saveData['updated_by'] = $params['user_id']; + $updateResult = $this->update($contactData[0]['id'], $saveData) ; + + if($updateResult['status'] != 'success'){ + throw new ApiErrorException(lang('Not updated')); + } + $data = Arr::except($updateResult['data'],['id','status','created_by','updated_by','created_at','updated_at']); + $data["social_media_addresses"] = json_decode($data["social_media_addresses"], 1) ; + $response = [ + 'status' => true, + 'data' => ['property_contact' => $data] , + ]; + } + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return output($response); + } + + public function clearSocialMediaAddresses($params){ + $newAddressArray = []; + foreach ($params as $key => $value) { + $addressToArray = explode('/', $value) ; + $address = end($addressToArray); + if(!$address){ + array_pop($addressToArray); + $address = end($addressToArray); + } + $address = str_replace('@','',$address); + $newAddressArray[$key] = $address ; + } + return $newAddressArray; + } +} diff --git a/app/Core/Service/PropertyContentService.php b/app/Core/Service/PropertyContentService.php new file mode 100644 index 0000000..8d78ca5 --- /dev/null +++ b/app/Core/Service/PropertyContentService.php @@ -0,0 +1,291 @@ +languageService = $languageService; + $this->propertyContentCreateValidator = $propertyContentCreateValidator; + $this->propertyContentCategoryRepository = $propertyContentCategoryRepository; + $this->propertyContentRepository = $propertyContentRepository; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "content_category_id" => fillOnUndefined($param, "content_category_id"), + "content" => fillOnUndefined($param, "content"), + "locale" => fillOnUndefined($param, "locale"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $validationResult = $this->propertyContentCreateValidator->validate($insertData); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $userCreateResult = $this->propertyContentRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response['status'] = 1; + $response['data'] = $userData; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyContentRepository->findByCriteria($param, $column); + + if (!$data) { + throw new ApiErrorException(lang('User data is not found.')) ; + } + + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyContentRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + + public function updateOrCreate($criteria = [], $saveData =[]){ + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyContentRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + + } + + public function propertyContent($params) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $contentRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'content_category_id', 'condition' => '=', 'value' => $params['category_id']], + ['field' => 'language_code', 'condition' => '=', 'value' => $params['language_code']], + ] + ]; + + $contentData = $this->propertyContentRepository->findByCriteria($contentRequest, ['id', 'property_id', 'content_category_id', 'language_code', 'content']); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['property_content' => $contentData]]; + + } catch(ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + + public function propertyContentUpdate($params) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $contentCollection = collect(fillOnUndefined($params, 'contents')) ; + $updates = $contentCollection->where('content_id', '!=' , null)->toArray(); + + // get all property contents + $propertyContentRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $propertyContentResponse = $this->select($propertyContentRequest); + + if($propertyContentResponse['status'] != 'success'){ + throw new ApiErrorException(lang('Executive Data Not Loaded')); + } + $propertyContentCollection = collect($propertyContentResponse['data']); + foreach ($contentCollection as $content){ + + $contentCheck = $propertyContentCollection + ->where('property_id', '=', $params['property_id']) + ->where('content_category_id', '=', $content['category_id']) + ->where('locale', '=', $params['locale']) + ->first(); + + if($contentCheck){ + $saveData = [ + 'content' => $content['value'], + 'updated_by' => $params['user_id'], + ]; + $updateStatus = $this->update($contentCheck['id'], $saveData) ; + + if($updateStatus['status'] != 'success'){ + throw new Exception(lang('Not updated')); + } + + }else{ + + $saveData = [ + 'property_id' => $params['property_id'], + 'content_category_id' => $content['category_id'], + 'content' => $content['value'], + 'locale' => $params['locale'], + 'status' => 1, + 'created_by' => $params['user_id'], + 'updated_by' => $params['user_id'], + + ]; + $saveStatus = $this->create($saveData) ; + if($saveStatus['status'] != 'success'){ + throw new ApiErrorException(lang('Not saved')); + } + + } + + } + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + + ]; + $propertyContent = $this->propertyContent($requestParams); + + if($propertyContent['status'] != 'success'){ + throw new ApiErrorException($propertyContent['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyContent['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } +} diff --git a/app/Core/Service/PropertyExecutiveService.php b/app/Core/Service/PropertyExecutiveService.php new file mode 100644 index 0000000..34696e5 --- /dev/null +++ b/app/Core/Service/PropertyExecutiveService.php @@ -0,0 +1,625 @@ +propertyExecutiveRepository = $propertyExecutiveRepository; + $this->propertyExecutiveTypeService = $propertyExecutiveTypeService; + $this->propertyExecutiveCreateValidator = $propertyExecutiveCreateValidator; + $this->propertyExecutiveUpdateValidator = $propertyExecutiveUpdateValidator; + $this->offerContactMappingRepository = $offerContactMappingRepository; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "executive_type_id" => fillOnUndefined($param, "executive_type_id"), + "name_surname" => fillOnUndefined($param, "name_surname"), + "email" => fillOnUndefined($param, "email"), + "phone_code" => fillOnUndefined($param, "phone_code"), + "phone" => fillOnUndefined($param, "phone"), + 'extension' => fillOnUndefined($param, 'extension'), + "mobile_code" => fillOnUndefined($param, "mobile_code"), + "mobile" => fillOnUndefined($param, "mobile"), + "fax_code" => fillOnUndefined($param, "fax_code"), + "fax" => fillOnUndefined($param, "fax"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + + $executiveTypeRequest = [ + 'criteria' => [ + ['field' => 'name_surname', 'condition' => '=', 'value' => $insertData['name_surname']], + ['field' => 'property_id', 'condition' => '=', 'value' => $insertData['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'firstRow' => true + ]; + $propertyExecutiveExist = $this->propertyExecutiveRepository->findByCriteria($executiveTypeRequest); + if(!empty($propertyExecutiveExist)){ + throw new ApiErrorException(lang('Registered user in the system')); + } + + $validationResult = $this->propertyExecutiveCreateValidator->validate($insertData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $userCreateResult = $this->propertyExecutiveRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyExecutiveRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyExecutiveUpdateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $updateResult = $this->propertyExecutiveRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData =[]){ + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyExecutiveRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function getPropertyExecutive($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + // get all executive types + $executiveTypeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + ]; + $executiveTypeResponse = $this->propertyExecutiveTypeService->select($executiveTypeRequest); + if($executiveTypeResponse['status'] != 'success'){ + throw new ApiErrorException(lang('Executive Data Not Loaded')); + } + + $executiveTypeCollection = collect($executiveTypeResponse['data']); + + // get all property executive types + $propertyExecutiveTypeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + + $propertyExecutiveTypeResponse = $this->select($propertyExecutiveTypeRequest); + + if($propertyExecutiveTypeResponse['status'] != 'success'){ + throw new ApiErrorException(lang('Executive Data Not Loaded')); + } + $propertyExecutiveTypeCollection = collect($propertyExecutiveTypeResponse['data']); + + foreach ($executiveTypeCollection as $executiveType){ + + + // get property_executive data + $propertyExecutiveItem = []; + $executiveTypeCheck = $propertyExecutiveTypeCollection->where('executive_type_id', '=', $executiveType['id']) + ->toArray(); + foreach ($executiveTypeCheck as $propertyExecutive){ + $propertyExecutiveItem['name_surname'] = $propertyExecutive['name_surname'] ; + $propertyExecutiveItem['email'] = $propertyExecutive['email'] ; + $propertyExecutiveItem['phone_code'] = $propertyExecutive['phone_code'] ; + $propertyExecutiveItem['phone'] = $propertyExecutive['phone'] ; + $propertyExecutiveItem['mobile_code'] = $propertyExecutive['mobile_code'] ; + $propertyExecutiveItem['mobile'] = $propertyExecutive['mobile'] ; + $propertyExecutiveItem['fax_code'] = $propertyExecutive['fax_code'] ; + $propertyExecutiveItem['fax'] = $propertyExecutive['fax'] ; + $propertyExecutiveItem['view_full_phone'] = $propertyExecutive['view_full_phone'] ; + $propertyExecutiveItem['view_full_mobile'] = $propertyExecutive['view_full_mobile'] ; + $propertyExecutiveItem['view_full_fax'] = $propertyExecutive['view_full_fax'] ; + + } + + + $executiveTypeItem['executive_type_id'] = $executiveType['id'] ; + $executiveTypeItem['name'] = $executiveType['name']; + $executiveTypeItem['data'] = $propertyExecutiveItem ; + $return[] = $executiveTypeItem ; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['executive_type' => $return]]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function listPropertyExecutive($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + // get all executive types + $executiveTypeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + + ]; + + + $executiveTypeResponse = $this->propertyExecutiveTypeService->select($executiveTypeRequest); + if($executiveTypeResponse['status'] != 'success'){ + throw new ApiErrorException(lang('Executive Data Not Loaded')); + } + + $executiveTypeCollection = collect($executiveTypeResponse['data']); + + // get all property executive types + $propertyExecutiveTypeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'orderBy' => [['field' => 'updated_at', 'value' => 'DESC']], + 'with' => ['executiveType'] + ]; + + $propertyExecutiveResponse = $this->select($propertyExecutiveTypeRequest); + + if($propertyExecutiveResponse['status'] != 'success'){ + throw new ApiErrorException(lang('Executive Data Not Loaded')); + } + + $propertyExecutiveResponse = $propertyExecutiveResponse['data'] ; + + $return['executive_type'] = []; + foreach ($executiveTypeCollection as $executiveType){ + + + + // Get Locale data + + $executiveTypeItem['executive_type_id'] = $executiveType['id'] ; + $executiveTypeItem['name'] = $executiveType['name']; + $executiveTypeItem['language_key'] = $executiveType['language_key']; + $return['executive_type'][] = $executiveTypeItem ; + } + + $return['executives'] = [] ; + foreach ($propertyExecutiveResponse as $executive){ + $executiveItem = [ + 'id' => $executive['id'], + 'property_id' => $executive['property_id'], + 'executive_type_id' => $executive['executive_type_id'], + 'executive_type_name' => $executive['executive_type']['name'], + 'name_surname' => $executive['name_surname'], + 'email' => $executive['email'], + 'phone_code' => $executive['phone_code'], + 'phone' => $executive['phone'], + 'extension' => $executive['extension'], + 'mobile_code' => $executive['mobile_code'], + 'mobile' => $executive['mobile'], + 'fax_code' => $executive['fax_code'], + 'fax' => $executive['fax'], + 'view_full_phone' => $executive['view_full_phone'], + 'view_full_mobile' => $executive['view_full_mobile'], + 'view_full_fax' => $executive['view_full_fax'], + ]; + $return['executives'][] = $executiveItem ; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return ]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function propertyExecutiveAdd($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + + foreach ($params['executive_type'] as $executive_type){ + + $createData = [ + 'property_id' => $params['property_id'], + 'executive_type_id' => $executive_type['executive_type_id'], + 'name_surname' => fillOnUndefined($executive_type, 'name_surname'), + 'email' => fillOnUndefined($executive_type, 'email'), + 'phone_code' => fillOnUndefined($executive_type, 'phone_code'), + 'phone' => fillOnUndefined($executive_type, 'phone'), + 'extension' => fillOnUndefined($executive_type, 'extension'), + 'mobile_code' => fillOnUndefined($executive_type, 'mobile_code'), + 'mobile' => fillOnUndefined($executive_type, 'mobile'), + 'fax_code' => fillOnUndefined($executive_type, 'fax_code'), + 'fax' => fillOnUndefined($executive_type, 'fax'), + 'updated_by' => $params['user_id'], + 'created_by' => $params['user_id'], + ]; + $createStatus = $this->create($createData); + + if ($createStatus['status'] != 'success') { + throw new Exception($createStatus['message']); + } + + } + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + + ]; + + $propertyExecutive = $this->listPropertyExecutive($requestParams); + + if($propertyExecutive['status'] != 'success'){ + throw new ApiErrorException($propertyExecutive['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyExecutive['data']]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function propertyExecutiveUpdate($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + foreach ($params['executive_type'] as $executive_type){ + $updateData = [ + 'executive_type_id' => fillOnUndefined($executive_type, 'executive_type_id'), + 'name_surname' => fillOnUndefined($executive_type, 'name_surname'), + 'email' => fillOnUndefined($executive_type, 'email') , + 'phone_code' => fillOnUndefined($executive_type, 'phone_code'), + 'phone' => fillOnUndefined($executive_type, 'phone'), + 'extension' => fillOnUndefined($executive_type, 'extension'), + 'mobile_code' => fillOnUndefined($executive_type, 'mobile_code'), + 'mobile' => fillOnUndefined($executive_type, 'mobile') , + 'fax_code' => fillOnUndefined($executive_type, 'fax_code'), + 'fax' => fillOnUndefined($executive_type, 'fax'), + 'updated_by' => $params['user_id'], + 'status' => 1, + ]; + + $propertyExecutiveExistRequest = [ + 'criteria' => [ + ['field' => 'name_surname', 'condition' => '=', 'value' => $executive_type['name_surname']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'id', 'condition' => '!=', 'value' => $executive_type['id'] ], + ['field' => 'status', 'condition' => '=', 'value' => 1] + + ], + 'firstRow' => true + ]; + + $propertyExecutiveExist = $this->propertyExecutiveRepository->findByCriteria($propertyExecutiveExistRequest); + if (isset($propertyExecutiveExist['id'])) { + throw new ApiErrorException(lang('Registered user in the system')); + } + + + $updateStatus = $this->update($executive_type['id'], $updateData) ; + + if($updateStatus['status'] != 'success'){ + throw new Exception($updateStatus['message']); + } + } + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + + ]; + + $propertyExecutive = $this->listPropertyExecutive($requestParams); + + if($propertyExecutive['status'] != 'success'){ + throw new ApiErrorException($propertyExecutive['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyExecutive['data']]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function propertyExecutiveDelete($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + + if(!isset($params['property_executive_id']) || !is_numeric($params['property_executive_id']) ){ + throw new ApiErrorException('property_executive_id field require'); + } + $propertyExecutiveDeleteRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'id', 'condition' => '=', 'value' => $params['property_executive_id']], + ] + ]; + + + $propertyExecutiveTypeResponse = $this->propertyExecutiveRepository->delete($propertyExecutiveDeleteRequest); + if(!$propertyExecutiveTypeResponse){ + throw new ApiErrorException('No record to delete'); + + } + + + + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + + ]; + + $propertyExecutive = $this->getPropertyExecutive($requestParams); + + if($propertyExecutive['status'] != 'success'){ + throw new ApiErrorException($propertyExecutive['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyExecutive['data']]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function propertyExecutivePassive($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $updateParams = [ + "status" => 0, + "updated_by" => $params['user_id'], + "updated_at" => time(), + ]; + + $updateResult = $this->propertyExecutiveRepository->update($params['property_executive_id'], $updateParams); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $updateParams = [ + "status" => 0, + "updated_by" => $params['user_id'], + "updated_at" => time(), + ]; + + $updateCriteria = [ + 'criteria' => [ + ['field' => 'property_executive_id', 'condition' => '=', 'value' => $params['property_executive_id']], + ] + ]; + + + + $offerUpdateResult = $this->offerContactMappingRepository->findByCriteria($updateCriteria , ['id']); + $updateIds = collect($offerUpdateResult)->keyBy('id')->keys()->toArray(); + $offerUpdateResult = $this->offerContactMappingRepository->updateWhereIn($updateIds , $updateParams); + + if ($offerUpdateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $propertyExecutive = $this->listPropertyExecutive($requestParams); + + if($propertyExecutive['status'] != 'success'){ + throw new ApiErrorException($propertyExecutive['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyExecutive['data']]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + +} diff --git a/app/Core/Service/PropertyExecutiveTypeService.php b/app/Core/Service/PropertyExecutiveTypeService.php new file mode 100644 index 0000000..31836d6 --- /dev/null +++ b/app/Core/Service/PropertyExecutiveTypeService.php @@ -0,0 +1,151 @@ +propertyExecutiveTypeRepository = $propertyExecutiveTypeRepository; + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "name" => fillOnUndefined($param, "name"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + + $userCreateResult = $this->propertyExecutiveTypeRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyExecutiveTypeRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyExecutiveTypeRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData =[]){ + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyExecutiveTypeRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + +} diff --git a/app/Core/Service/PropertyFactMappingService.php b/app/Core/Service/PropertyFactMappingService.php new file mode 100644 index 0000000..96ffce2 --- /dev/null +++ b/app/Core/Service/PropertyFactMappingService.php @@ -0,0 +1,338 @@ +languageService = $languageService; + $this->propertyFactMappingRepository = $propertyFactMappingRepository; + $this->applicationCacheService = $applicationCacheService; + $this->propertyFactCheckboxValidator = $propertyFactCheckboxValidator; + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + + $userCreateResult = $this->propertyFactMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]->toArray(); + + $response['status'] = 1; + $response['data'] = $userData; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyFactMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function deleteById($param){ + + $userCreateResult = $this->propertyFactMappingRepository->delete(); + + } + + public function removePropertyFactMapping($param = []) + { + + //Todo: validations + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + //Todo: cache datası yeni bir fonksiyon yapılıp herkes oradan geçekecek + + $cacheInfo = $this->applicationCacheService->getApplicationCache([]); + if($cacheInfo['status'] != 'success'){ + throw new ApiErrorException(lang('Cache Required')); + } + $userData = $cacheInfo['data'] ; + + + $factIds = []; + foreach ($param["facts"] as $fact){ + $factIds[] = $fact["id"]; + } + + + $findCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $userData['property_id']], + ], + "whereIn"=> + [ + ["field"=>"fact_id","value"=>$factIds] + ] + ]; + + $factMappingDatas = $this->propertyFactMappingRepository->findByCriteria($findCriteria); + if (!$factMappingDatas) { + throw new ApiErrorException(lang('Fact cannot loaded')); + } + + + // Todo: gönderilen idler ile veritabanında bulunan idler karşılaştırılacak. Uyumsuz data varsa hiç bir şey silinmeden error throw edilecek + + $deleteRowIds = []; + foreach ($factMappingDatas as $factMappingData){ + $ids = []; + $ids[] = $factMappingData["id"]; + $delete = $this->propertyFactMappingRepository->deleteById($ids); + if (!$delete) { + throw new ApiErrorException(lang('cannot delete fact')); + } + + } + $response['status'] = 1; + $response['data'] = []; + + + + } + catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function addPropertyFactMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try + { + $propertyId = $params['property_id']; + $userId = $params['user_id']; + $propertyFactData = $params['data']; + + $addPropertyFactMapping = []; + foreach ($propertyFactData as $key => $param) + { + $addPropertyFactMapping[] = + [ + 'property_id' => $propertyId, + 'fact_id' => fillOnUndefined($param, 'id'), + 'created_by' => $userId, + 'updated_by' => $userId, + ]; + } + + //TODO : $addPropertyFactMapping için validation işlemleri yapılacaktır.(property_id ve fact id inin unique doğrulamasına bakılması unutulmamalı ) + $response = $this->propertyFactMappingRepository->createAll($addPropertyFactMapping); + + if ($response['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + public function updatePropertyFactMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try + { + + $validationResult = $this->propertyFactCheckboxValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $addPropertyFactMapping = []; + $addFactList = collect($params['property_fact']) + ->where('is_selected' ,'=', true); + $addFactIds = $addFactList->keyBy("id")->keys()->toArray(); + + $removeFactList = collect($params['property_fact']) + ->where('is_selected' ,'=', false); + + $removeFactIds = $removeFactList->keyBy("id")->keys()->toArray(); + + $checkPropertyMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + "whereIn" => [ + ["field" => "fact_id", "value" => $addFactIds] + ] + ]; + $checkPropertyMappingResponse = $this->select($checkPropertyMappingRequest,['id', 'property_id', 'fact_id']); + + if($checkPropertyMappingResponse['status'] != 'success'){ + throw new ApiErrorException(lang('Mapping data not loaded')); + } + $checkPropertyMappingCollect = collect($checkPropertyMappingResponse['data']); + + foreach ($addFactList->toArray() as $key => $param) + { + + $isFactMappingBefore = $checkPropertyMappingCollect->where('property_id', '=', $params['property_id']) + ->where('fact_id', '=', $param['id']) + ->first(); + + $description = [] ; + foreach (fillOnUndefined($param, "description", []) as $title) { + $description[$title['language_code']] = !empty($title['description']) ? $title['description'] : null; + } + + if(empty($isFactMappingBefore)){ + + $addPropertyFactMapping[] = + [ + 'property_id' => $params['property_id'], + 'fact_id' => fillOnUndefined($param, 'id'), + 'description' => !empty($description) ? json_encode($description) : null, + 'created_by' => $params['user_id'], + 'updated_by' => $params['user_id'], + ]; + + } else { + $this->propertyFactMappingRepository->update($isFactMappingBefore['id'], ['description' => !empty($description) ? json_encode($description) : null]); + } + } + + $response = $this->propertyFactMappingRepository->createAll($addPropertyFactMapping); + + + if($removeFactIds){ + + $findCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + "whereIn" => + [ + ["field" => "fact_id", "value" => $removeFactIds] + ] + ]; + + $deletePropertyFactMapping = $this->propertyFactMappingRepository->findByCriteria($findCriteria); + + $deleteThisIds = []; + foreach ($deletePropertyFactMapping as $deleteThisItem){ + $deleteThisIds[] = $deleteThisItem['id']; + } + + if($deleteThisIds){ + $this->propertyFactMappingRepository->deleteById($deleteThisIds); + + } + } + + if ($response['status'] != 'success') { + throw new ApiErrorException(lang('Data is not added')) ; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + } diff --git a/app/Core/Service/PropertyFactService.php b/app/Core/Service/PropertyFactService.php new file mode 100644 index 0000000..70b384f --- /dev/null +++ b/app/Core/Service/PropertyFactService.php @@ -0,0 +1,758 @@ +applicationCacheService = $applicationCacheService; + $this->propertyFactMappingService = $propertyFactMappingService; + $this->languageService = $languageService; + $this->propertyFactRepository = $propertyFactRepository; + $this->propertyFactMappingRepository = $propertyFactMappingRepository; + $this->propertyFactSearchValidator = $propertyFactSearchValidator; + $this->propertyRoomFactMappingService = $propertyRoomFactMappingService; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "Fact_category_id" => fillOnUndefined($param, "Fact_category_id"), + "Fact" => fillOnUndefined($param, "Fact"), + "locale" => fillOnUndefined($param, "locale"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + + $userCreateResult = $this->propertyFactRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]->toArray(); + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyFactRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyFact($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $requestData = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [] + ]; + $fectData = $this->select($requestData); + + + if ($fectData['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $factCollection = collect($fectData['data']); + + $propertyFactMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + + $propertyFactMappingData = $this->propertyFactMappingService->select($propertyFactMappingRequest); + if ($propertyFactMappingData['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $propertyFactMappingData = $propertyFactMappingData['data'] ? $propertyFactMappingData['data'] : []; + $propertyFactMapping = []; + foreach ($propertyFactMappingData as $propertyFactItem) { + $propertyFactMapping[$propertyFactItem['fact_id']] = $propertyFactItem; + } + + $return = []; + + $mainFacts = $factCollection->where('parent_id', '=', null) + ->toArray(); + $responseFacts = []; + + foreach ($mainFacts as $fact) { + + + $mainFactItem['id'] = $fact['id']; + $mainFactItem['name'] = $fact['name']; + $mainFactItem['language_key'] = $fact['language_key']; + $mainFactItem['title_language_key'] = $fact['title_language_key']; + $mainFactItem['icon'] = $fact['icon']; + if ($fact['type'] == 1) { + $mainFactItem['icon'] = 'far fa-tv-retro'; + } + $subCats = $factCollection->where('parent_id', '=', $fact['id']) + ->toArray(); + + array_multisort( + array_column($subCats, 'order_number'), SORT_ASC, + array_column($subCats, 'name'), SORT_ASC, + $subCats + ); + + + $responseSubCats = []; + foreach ($subCats as $subCat) { + + $subCatItem['id'] = $subCat['id']; + $subCatItem['name'] = $subCat['name']; + $subCatItem['language_key'] = $subCat['language_key']; + $subCatItem['title_language_key'] = $subCat['title_language_key']; + $subCatItem['icon'] = $subCat['icon']; + $subCatItem['priority'] = $subCat['order_number']; + $subCatItem['parent_id'] = $fact['id']; + + if ($subCat['type'] == 1) { + $subCatItem['icon'] = 'far fa-tv-retro'; + } + + $subFacts = $factCollection->where('parent_id', '=', $subCat['id']) + ->toArray(); + + array_multisort( + array_column($subFacts, 'order_number'), SORT_ASC, + array_column($subFacts, 'name'), SORT_ASC, + $subFacts + ); + + $responseSubFacts = []; + + foreach ($subFacts as $subFact) { + + $subFactItem['id'] = $subFact['id']; + $subFactItem['name'] = $subFact['name']; + $subFactItem['language_key'] = $subFact['language_key']; + $subFactItem['title_language_key'] = $subFact['title_language_key']; + $subFactItem['type'] = $subFact['type']; + $subFactItem['icon'] = $subFact['icon']; + $subFactItem['parent_id'] = $subCat['id']; + + $checkMappingData = isset($propertyFactMapping[$subFact['id']]) ? $propertyFactMapping[$subFact['id']] : []; + $subFactItem['is_selected'] = $checkMappingData ? true : false; + $subFactItem['mapping_id'] = fillOnUndefined($checkMappingData, 'id'); + + // Todo Will be typed when 'attributes' is added + // $subFactItem['attributes'] = null; + + + $subFactItem['description'] = null; + if ($checkMappingData && fillOnUndefined($checkMappingData, 'description')) { + $descriptionLangContents = json_decode($checkMappingData['description'], 1); + + if(isset($params['locale'])) { + $params['currentLanguage'] = $params['locale']; + } + + if (isset($params['currentLanguage']) && isset($descriptionLangContents[$params['currentLanguage']])) { + $subFactItem['description'] = $descriptionLangContents[$params['currentLanguage']]; + } + } + + $responseSubFacts[] = $subFactItem; + } + $subCatItem['fact'] = $responseSubFacts; + $responseSubCats[$subCat['id']] = $subCatItem; + } + + $mainFactItem['fact_subcategory'] = $responseSubCats; + $return[$fact['id']] = $mainFactItem; + + } + + $response = [ + 'status' => true, + 'data' => $return, + ]; + + } 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); + + + } + + public function searchPropertyFact($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $validationResult = $this->propertyFactSearchValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $propertyFactRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $propertyFactData = $this->propertyFactMappingRepository->findByCriteria($propertyFactRequest, ['id', 'property_id', 'fact_id']); + $propertyFacts = collect($propertyFactData)->keyBy('fact_id')->keys()->toArray(); + + if (!$propertyFacts) { + throw new ApiErrorException(lang('Check fact data from your content')); + } + + + $searchFactRequest = [ + 'whereIn' => [ + ['field' => 'id', 'value' => $propertyFacts] + ] + ]; + + if ($params['search_term']) { + $searchFactRequest['criteria'][] = ['field' => 'name', 'condition' => 'like', 'value' => '%' . $params['search_term'] . '%']; + } + + $searchFactData = $this->propertyFactRepository->findByCriteria($searchFactRequest, ['id', 'name', 'language_key']); + + if (!$searchFactData) { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $response = [ + 'status' => true, + 'data' => $searchFactData, + ]; + + } 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); + + + } + + public function getPropertyRoomFact($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $requestData = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'parent_id', 'condition' => '=', 'value' => 293], + + ], + 'with' => [] + ]; + $factData = $this->select($requestData); + + + if ($factData['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $subFacts = $factData['data'] ? $factData['data'] : []; + + $showRecommended = false; + $propertyRoomFactMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + + $propertyRoomFactMappingData = $this->propertyRoomFactMappingService->select($propertyRoomFactMappingRequest); + if ($propertyRoomFactMappingData['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $propertyRoomFactMappingCollection = collect($propertyRoomFactMappingData['data']); + $propertyRoomFactMappingData = $propertyRoomFactMappingData['data'] ? $propertyRoomFactMappingData['data'] : []; + $propertyFactMapping = []; + foreach ($propertyRoomFactMappingData as $mappingItem) { + $propertyFactMapping[$mappingItem['fact_id']] = $mappingItem; + $propertyRoomFactMapping[$mappingItem['room_id'] . '|' . $mappingItem['fact_id']] = $mappingItem; + } + if ($propertyRoomFactMappingData) { + $showRecommended = true; + } + + + $responseSubFacts = []; + + + if ($showRecommended) { + $showRecommendedData = $propertyRoomFactMappingCollection + ->where('room_id', '=', $params['room_id']) + ->first(); + if ($showRecommendedData) { + $showRecommended = false; + } + } + + + foreach ($subFacts as $subFact) { + + $subFactItem['id'] = $subFact['id']; + $subFactItem['name'] = $subFact['name']; + $subFactItem['language_key'] = $subFact['language_key']; + $subFactItem['title_language_key'] = $subFact['title_language_key']; + $subFactItem['type'] = $subFact['type']; + if ($subFact['type'] == 1) { + $subFactItem['icon'] = $subFact['icon']; + } + + $checkMappingData = isset($propertyRoomFactMapping[$params['room_id'] . '|' . $subFact['id']]) ? $propertyRoomFactMapping[$params['room_id'] . '|' . $subFact['id']] : []; + + $checkRecommended = isset($propertyFactMapping[$subFact['id']]) ? $propertyFactMapping[$subFact['id']] : []; + + $subFactItem['is_selected'] = $checkMappingData ? true : false; + $subFactItem['is_recommended'] = $checkRecommended ? true : false; + $subFactItem['mapping_id'] = fillOnUndefined($checkMappingData, 'id'); + $subFactItem['is_feature'] = fillOnUndefined($checkMappingData, 'is_feature', 0); + + $responseSubFacts[] = $subFactItem; + } + + if (collect($responseSubFacts)->where('is_recommended', 'true')->count() == 0) { + $showRecommended = false; + } + + $response = [ + 'status' => true, + 'data' => [ + 'get_room_facts' => $responseSubFacts, + 'show_recommended' => $showRecommended, + ], + ]; + + } 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); + + + } + + public function getSubCategoryFacts($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $requestData = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'type', 'condition' => '=', 'value' => 1], + ['field' => 'parent_id', 'condition' => '=', 'value' => $params['sub_category_id']], + ], + 'orderBy' => [ + ["field" => "order_number", "value" => "ASC"], + ["field" => "name", "value" => "ASC"], + ] + + ]; + $factData = $this->select($requestData); + + if ($factData['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $factCollection = collect($factData['data']); + + $propertyFactMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + + $propertyFactMappingData = $this->propertyFactMappingService->select($propertyFactMappingRequest); + if ($propertyFactMappingData['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + + $propertyFactMappingData = $propertyFactMappingData['data'] ? $propertyFactMappingData['data'] : []; + $propertyFactMapping = []; + foreach ($propertyFactMappingData as $propertyFactItem) { + $propertyFactMapping[$propertyFactItem['fact_id']] = $propertyFactItem; + } + + $getApplicationLanguages = $this->languageService->getApplicationLanguages(); + if ($getApplicationLanguages['status'] != 'success') { + throw new ApiErrorException($getApplicationLanguages['message']); + } + $applicationLanguages = $getApplicationLanguages['data']; + + $subFacts = $factCollection->toArray(); + $responseSubFacts = []; + + foreach ($subFacts as $subFact) { + + $subFactItem['id'] = $subFact['id']; + $subFactItem['name'] = $subFact['name']; + $subFactItem['language_key'] = $subFact['language_key']; + $subFactItem['title_language_key'] = $subFact['title_language_key']; + $subFactItem['type'] = $subFact['type']; + $subFactItem['icon'] = $subFact['icon']; + + $checkMappingData = isset($propertyFactMapping[$subFact['id']]) ? $propertyFactMapping[$subFact['id']] : []; + $subFactItem['is_selected'] = $checkMappingData ? true : false; + $subFactItem['mapping_id'] = fillOnUndefined($checkMappingData, 'id'); + + $subFactItem['description'] = []; + $descriptionLangContents = $checkMappingData ? json_decode($checkMappingData['description'], 1) : []; + $responseLangDescription = []; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangDescription[] = [ + 'language_code' => $langKey, + 'description' => isset($descriptionLangContents[$langKey]) ? $descriptionLangContents[$langKey] : null + ]; + } + $subFactItem['description'] = $responseLangDescription; + + + $responseSubFacts[] = $subFactItem; + } + + $response = [ + 'status' => true, + 'data' => $responseSubFacts, + ]; + + } 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); + + + } + + public function getSubCategoryMenus($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $requestData = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'type', 'condition' => '=', 'value' => 0], + ['field' => 'id', 'condition' => '!=', 'value' => 9], // Accommodation + ] + ]; + $factData = $this->select($requestData); + + if ($factData['status'] != 'success') { + throw new ApiErrorException(lang('Fact data not loaded')); + } + $factCollection = collect($factData['data']); + $mainFacts = $factCollection->where('parent_id', '=', null)->sortBy('order_number')->toArray(); + $responseSubFacts = []; + $isFoundItem = false; + $nextItem = '/app/content/rooms'; + $prevItem = '/app/content/photos'; + $isDisable = true; + $foundIndex = -1; + $logData = []; + + foreach ($mainFacts as $mainFact) { + $subFacts = $factCollection->where('parent_id', '=', $mainFact['id'])->sortBy('order_number')->toArray(); + foreach ($subFacts as $subFact) { + + $logData[] = $subFact; + + if ($params['parent_id'] == $subFact['parent_id']) { + $subFactItem['id'] = $subFact['id']; + $subFactItem['name'] = $subFact['name']; + $subFactItem['language_key'] = $subFact['language_key']; + $subFactItem['title_language_key'] = $subFact['title_language_key']; + $subFactItem['type'] = $subFact['type']; + $subFactItem['icon'] = $subFact['icon']; + $subFactItem['alias'] = 'Property.Fact.SubCategoryFacts.' . $subFact['id']; + $subFactItem['url'] = '/app/content/facts/' . $subFact['id']; + $subFactItem['is_disable'] = $isDisable; + + $responseSubFacts[] = $subFactItem; + } + if ($params['sub_category_id'] == $subFact['id']) { + $isDisable = false; + } + + if ($isFoundItem) { + $subFactItem['icon'] = $subFact['icon']; + $foundIndex++; + } + + if ($params['sub_category_id'] == $subFact['id']) { + $isFoundItem = true; + $foundIndex = 0; + } + + if ($isFoundItem && $foundIndex == 1) { + $nextItem = '/app/content/facts/' . $subFact['id']; + } + + if (!$isFoundItem) { + $prevItem = '/app/content/facts/' . $subFact['id']; + } + + + } + + } + + $response = [ + 'status' => true, + 'data' => [ + 'menu' => $responseSubFacts, + 'prev' => $prevItem, + 'next' => $nextItem, + + ], + ]; + + } 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); + + + } + + public function getPropertyOfferFilter($params = [], $offerAllFactIds = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $categories = $params; + $responseArray = []; + $accommodationTypes = []; + foreach ($categories as $category) { + $categoryArray = []; + $categoryItem = $category; + foreach ($category['fact_subcategory'] as $subCategory) { + $subCategoryItem = $subCategory; + if ($subCategory['id'] == 9) { + $facts = collect($subCategory['fact'])->map(function ($value) use ($offerAllFactIds) { + $value['is_selected'] = array_search($value['id'], $offerAllFactIds) > -1; + return $value; + })->values()->all(); + $subCategoryItem['fact'] = $facts ? $facts : []; + $accommodationTypes = $subCategoryItem['fact']; + } else { + $facts = collect($subCategory['fact'])->where('is_selected', '=', true)->map(function ($value) use ($offerAllFactIds) { + $value['is_selected'] = array_search($value['id'], $offerAllFactIds) > -1; + return $value; + })->values()->all(); + $subCategoryItem['fact'] = $facts ? $facts : []; + + $categoryArray[$subCategory['id']] = $subCategoryItem; + } + } + $categoryItem['fact_subcategory'] = $categoryArray; + $responseArray[$category['id']] = $categoryItem; + } + $response = [ + 'status' => true, + 'data' => [ + 'get_facts' => $responseArray, + 'accommodation_types' => $accommodationTypes, + ], + ]; + + } 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); + + } + + public function getPropertyOfferFilterForHtml($params = [], $offerAllFactIds = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $categories = $params; + $responseArray = []; + foreach ($categories as $category) { + $categoryArray = []; + $categoryItem = $category; + foreach ($category['fact_subcategory'] as $subCategory) { + $subCategoryItem = $subCategory; + $facts = collect($subCategory['fact']) + ->where('is_selected', '=', true) + ->whereIn('id', $offerAllFactIds) + ->map(function ($value) use ($offerAllFactIds) { + return $value; + })->values()->all(); + $subCategoryItem['fact'] = $facts ? $facts : []; + + if ($facts) { + $categoryArray[$subCategory['id']] = $subCategoryItem; + } + } + $categoryItem['fact_subcategory'] = $categoryArray; + $responseArray[$category['id']] = $categoryItem; + } + + $response = [ + 'status' => true, + 'data' => $responseArray, + ]; + + } 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); + + } + + public function checkPropertyFactMapping($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $request = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ], + 'whereIn' => [ + ['field' => 'fact_id', 'value' => $param['fact_ids']], + ], + 'count' => 1 + ]; + $data = $this->propertyFactMappingRepository->findByCriteria($request); + if ($data < count($param['fact_ids'])) { + throw new ApiErrorException('Property - Fact Mapping not validate.'); + } + $response = [ + 'status' => true, + 'data' => $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); + } + +} diff --git a/app/Core/Service/PropertyGroupMappingService.php b/app/Core/Service/PropertyGroupMappingService.php new file mode 100644 index 0000000..a303bf6 --- /dev/null +++ b/app/Core/Service/PropertyGroupMappingService.php @@ -0,0 +1,88 @@ +request = $request; + $this->propertyGroupRepository = $propertyGroupRepository; + $this->propertyGroupMappingRepository = $propertyGroupMappingRepository; + } + + + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $data = $this->propertyGroupMappingRepository->findByCriteria($param, $column); + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function getPropertyGroupMapping($propertyGroupMappingCriteria, $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $propertyGroupMapping =$this->select($propertyGroupMappingCriteria,$column); + + if(!$propertyGroupMapping){ + throw new ApiErrorException(lang('Property Group Mapping data not found')); + } + + $response['status'] = 1; + $response['data'] = $propertyGroupMapping['data']; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + } + + +} diff --git a/app/Core/Service/PropertyGroupService.php b/app/Core/Service/PropertyGroupService.php new file mode 100644 index 0000000..9df2342 --- /dev/null +++ b/app/Core/Service/PropertyGroupService.php @@ -0,0 +1,83 @@ +request = $request; + $this->propertyGroupRepository = $propertyGroupRepository; + } + + + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $data = $this->propertyGroupRepository->findByCriteria($param, $column); + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function getPropertyGroup($propertyGroupCriteria, $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $propertyGroup =$this->select($propertyGroupCriteria,$column); + + if(!$propertyGroup){ + throw new ApiErrorException(lang('Property Group data not found')); + } + + $response['status'] = 1; + $response['data'] = $propertyGroup; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + } + +} diff --git a/app/Core/Service/PropertyInvoiceService.php b/app/Core/Service/PropertyInvoiceService.php new file mode 100644 index 0000000..dec93e0 --- /dev/null +++ b/app/Core/Service/PropertyInvoiceService.php @@ -0,0 +1,329 @@ +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); + } + +} diff --git a/app/Core/Service/PropertyModuleMappingService.php b/app/Core/Service/PropertyModuleMappingService.php new file mode 100644 index 0000000..1866278 --- /dev/null +++ b/app/Core/Service/PropertyModuleMappingService.php @@ -0,0 +1,253 @@ +propertyModuleMappingRepository = $propertyModuleMappingRepository; + $this->propertyConfigService = $propertyConfigService; + $this->configKey = 'data_rate'; + $this->request = $request; + $this->currentRoute = $this->request->route(); + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "property_module_id" => fillOnUndefined($param, "property_module_id"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->propertyModuleMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyModuleMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyModuleMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function getPropertyModuleMapping($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + + $propertyModuleRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + + ], + 'with' => ['propertyModule'] + ]; + $propertyModuleResponse = $this->select($propertyModuleRequest); + + if ($propertyModuleResponse['status'] != 'success') { + throw new ApiErrorException(lang('ModuleMapping Data Not Loaded')); + } + + $propertyMappedModules = array_column($propertyModuleResponse['data'], 'property_module'); + + $totalPoint = collect($propertyMappedModules)->sum('point'); + + $return['property_modules'] = $propertyMappedModules ; + $return['data_point'] = $totalPoint ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function propertyModuleMappingUpdate($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + $propertyModuleRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_module_id', 'condition' => '=', 'value' => $params['property_module_id']], + ] + ]; + $propertyModuleResponse = $this->select($propertyModuleRequest); + + if ($propertyModuleResponse['status'] != 'success') { + throw new ApiErrorException(lang('ModuleMapping Data Not Loaded')); + } + + if (!$propertyModuleResponse['data']) { + $createData = [ + 'property_id' => $params['property_id'], + 'property_module_id' => $params['property_module_id'], + 'status' => 1, + 'updated_by' => $params['user_id'], + 'created_by' => $params['user_id'], + ]; + $createStatus = $this->create($createData); + if ($createStatus['status'] != 'success') { + throw new Exception($createStatus['message']); + } + + } + + + $propertyModuleMapping = $this->getPropertyModuleMapping($params); + + if ($propertyModuleMapping['status'] != 'success') { + throw new ApiErrorException($propertyModuleMapping['message']); + } + + $configParams = [ + 'config_keys' => [ + [ + 'config_key' => $this->configKey, + 'config_value_json' => json_encode(['point' => $propertyModuleMapping['data']['data_point']]) + ] + ], + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $params['user_id'], + ]; + + $propertyConfig = $this->propertyConfigService->propertyConfigUpdate($configParams); + + if ($propertyConfig['status'] != 'success') { + + throw new ApiErrorException($propertyConfig['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['property_module_mapping' => $propertyModuleMapping['data']]]; + + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + + return output($response); + } + + +} diff --git a/app/Core/Service/PropertyNetworkService.php b/app/Core/Service/PropertyNetworkService.php new file mode 100644 index 0000000..f78f4f4 --- /dev/null +++ b/app/Core/Service/PropertyNetworkService.php @@ -0,0 +1,190 @@ +bookingRepository = $bookingRepository; + $this->bookingRoomRepository = $bookingRoomRepository; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + $this->propertyPromotionRepository = $propertyPromotionRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomBedRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getAllDashboardData($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + if (fillOnUndefined($params, 'property_id', null) == null) { + throw new ApiErrorException(lang('property_id is required')); + } + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'checkin_date', 'condition' => '>', 'value' => Carbon::now()->subWeek(2)->toDate()], + ['field' => 'checkout_date', 'condition' => '<', 'value' => Carbon::now()->addWeek(2)->toDate()] + ], + 'with' => ['bookingRoom'], + 'orderBy' => [["field" => "checkin_date", "value" => "ASC"]], + ]; + + $data = $this->bookingRepository->findByCriteria($criteria); + $data = $data ? $data : []; + + + $dailyRoomCounts = []; + foreach ($data as $perData) { + + $dates = []; + $perDataDatePeriod = CarbonPeriod::create($perData['checkin_date'], Carbon::parse($perData['checkout_date'])->subDay()->toDateString()); + $dates = $perDataDatePeriod->toArray(); + + foreach ($dates as $date) { + + if (!isset($dailyRoomCounts[$date->toDateString()])) { + $dailyRoomCounts[$date->toDateString()] = 0; + } + + $dailyRoomCounts[$date->toDateString()] += count($perData['booking_room']); + + } + } + + $dayLanguages = [ + "Monday" => 'day-short-monday', + "Tuesday" => 'day-short-tuesday', + "Wednesday" => 'day-short-wednesday', + "Thursday" => 'day-short-thursday', + "Friday" => 'day-short-friday', + "Saturday" => 'day-short-saturday', + "Sunday" => 'day-short-sunday' + ]; + + $bookingWeeklyCount = []; + for ($i = 0; $i < 7; $i++) { + + $iterDay = Carbon::now()->addDay($i)->format('l'); + $iterDate = Carbon::now()->addDay($i)->format('Y-m-d'); + + + if (isset($dailyRoomCounts[$iterDate])) { + + $bookingWeeklyCount[$iterDay] = [ + 'date' => $iterDate, + 'day' => $iterDay, + 'abbreviation_day' => $dayLanguages[$iterDay], + 'count' => $dailyRoomCounts[$iterDate], + ]; + } else { + + $bookingWeeklyCount[$iterDay] = [ + 'date' => $iterDate, + 'day' => $iterDay, + 'abbreviation_day' => $dayLanguages[$iterDay], + 'count' => 0 + ]; + + } + } + + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'count' => true + ]; + + $propertyChannelMappingCount = $this->propertyChannelMappingRepository->findByCriteria($propertyChannelMappingCriteria); + + $propertyPromotionCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'count' => true + ]; + $propertyPromotionCount = $this->propertyPromotionRepository->findByCriteria($propertyPromotionCriteria); + + $exportData = [ + 'reservation_count' => $bookingWeeklyCount, + 'channel_count' => $propertyChannelMappingCount, + 'promotion_count' => $propertyPromotionCount + ]; + + + $response = [ + 'status' => true, + 'data' => $exportData, + ]; + + } 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); + } + + +} diff --git a/app/Core/Service/PropertyNonrefundableService.php b/app/Core/Service/PropertyNonrefundableService.php new file mode 100644 index 0000000..3258d2b --- /dev/null +++ b/app/Core/Service/PropertyNonrefundableService.php @@ -0,0 +1,361 @@ +propertyNonrefundableRepository = $propertyNonrefundableRepository; + $this->propertyNonrefundableAddValidator = $propertyNonrefundableAddValidator; + $this->propertyRoomRateChannelMappingRepository = $propertyRoomRateChannelMappingRepository ; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + } + + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyNonrefundableRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function createForm($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $roomRateChannelMappingRequest= [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'whereIn' => [ + ['field' => 'channel_id', 'value' => $params['channels']] + ], + 'with' => ['propertyRoomRateMapping.propertyRoomRate', 'propertyRoomRateMapping.propertyRoom', 'propertyChannel'], + ]; + + $roomRateChannelMappingResponse = $this->propertyRoomRateChannelMappingRepository->findByCriteria($roomRateChannelMappingRequest) ; + $roomRateChannelMappings = $roomRateChannelMappingResponse ? $roomRateChannelMappingResponse : [] ; + $roomRates = [] ; + + $channels = [] ; + + foreach ( $roomRateChannelMappings as $roomRateChannelMapping) { + $channels[] = [ + 'id' => $roomRateChannelMapping['property_channel']['id'], + 'name' => $roomRateChannelMapping['property_channel']['name'], + 'logo' => $roomRateChannelMapping['property_channel']['logo'], + ]; + $roomRates[$roomRateChannelMapping['room_rate_mapping_id']] = [ + 'room_rate_mapping_id' => $roomRateChannelMapping['room_rate_mapping_id'], + 'name' => $roomRateChannelMapping['property_room_rate_mapping']['property_room']['name'] . ' - '. $roomRateChannelMapping['property_room_rate_mapping']['property_room_rate']['name'], + 'room_id' => $roomRateChannelMapping['property_room_rate_mapping']['property_room']['id'], + 'room_name' => $roomRateChannelMapping['property_room_rate_mapping']['property_room']['name'], + 'rate_id' => $roomRateChannelMapping['property_room_rate_mapping']['property_room_rate']['id'], + 'rate_name' => $roomRateChannelMapping['property_room_rate_mapping']['property_room_rate']['name'], + ]; + } + + array_multisort( + array_column($roomRates, 'room_name'), SORT_ASC, + array_column($roomRates, 'rate_name'), SORT_ASC, + $roomRates + ); + + $channels = collect($channels)->unique('id')->sort()->values()->toArray(); + + $responseData = [ + 'channels' => $channels, + 'room_rate_mapping' => $roomRates, + ]; + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function storePropertyNonrefundable($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + DB::beginTransaction(); + + $validationResult = $this->propertyNonrefundableAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $roomRateChannelMappingRequest= [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'whereIn' => [ + ['field' => 'channel_id', 'value' => $params['channels']] + ], + ]; + + $roomRateChannelMappingResponse = $this->propertyRoomRateChannelMappingRepository->findByCriteria($roomRateChannelMappingRequest) ; + $roomRateChannelMappings = $roomRateChannelMappingResponse ? $roomRateChannelMappingResponse : [] ; + + $rateChannel = [] ; + foreach ($roomRateChannelMappings as $roomRateChannelMapping) { + $rateChannel[$roomRateChannelMapping['room_rate_mapping_id'].'|'.$roomRateChannelMapping['channel_id']] =[ + "id" => $roomRateChannelMapping['id'], + "property_id" => $roomRateChannelMapping['property_id'], + "channel_id" => $roomRateChannelMapping['channel_id'], + "room_rate_mapping_id" => $roomRateChannelMapping['room_rate_mapping_id'], + ]; + } + + $propertyChannelMappingRequest= [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'whereIn' => [ + ['field' => 'channel_id', 'value' => $params['channels']] + ], + ]; + + $propertyChannelMappingResponse = $this->propertyChannelMappingRepository->findByCriteria($propertyChannelMappingRequest) ; + $propertyChannelMappings = $propertyChannelMappingResponse ? $propertyChannelMappingResponse : [] ; + + + + $propertyChannel = [] ; + foreach ($propertyChannelMappings as $propertyChannelMapping) { + $propertyChannel[$propertyChannelMapping['channel_id']] =[ + "id" => $propertyChannelMapping['id'], + "property_id" => $propertyChannelMapping['property_id'], + "channel_id" => $propertyChannelMapping['channel_id'], + ]; + } + + + $roomRateMappings = fillOnUndefined($params, 'room_rate_mapping', []) ; + $channels = fillOnUndefined($params, 'channels', []) ; + $storeData = [] ; + foreach ( $roomRateMappings as $roomRateMapping) { + foreach ($channels as $channel) { + $checkChannelKey = $roomRateMapping['room_rate_mapping_id'].'|'.$channel ; + if(isset($rateChannel[$checkChannelKey])) { + if (isset($propertyChannel[$channel])) { + $storeData[] = [ + 'property_id' => $params['property_id'], + 'channel_id' => $channel, + 'channel_mapping_id' => $propertyChannel[$channel]['id'], + 'room_rate_mapping_id' => $roomRateMapping['room_rate_mapping_id'], + 'action_type' => $roomRateMapping['action_type'], + 'value_type' => $roomRateMapping['value_type'], + 'value' => $roomRateMapping['value'], + 'status' => 1, + 'created_by' => fillOnUndefined($params, "user_id"), + 'updated_by' => fillOnUndefined($params, "user_id"), + 'created_at' => time(), + 'updated_at' => time(), + ]; + } + } + } + } + + $roomRates = collect($params['room_rate_mapping'])->keyBy('room_rate_mapping_id')->keys()->toArray(); + $params['delete_room_rates'] = $roomRates; + $nonrefundableDeleteStatus = $this->deleteChannelsWithRatesNonrefundableData($params) ; + if($nonrefundableDeleteStatus['status'] != "success"){ + throw new ApiErrorException($nonrefundableDeleteStatus['message']); + } + + $addNonrefundableStatus = $this->propertyNonrefundableRepository->insert($storeData); + if($addNonrefundableStatus['status'] != "success"){ + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + DB::rollBack(); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deleteChannelsWithRatesNonrefundableData($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $nonrefundableRequest= [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'whereIn' => [ + ['field' => 'channel_id', 'value' => $params['channels']] + ], + ]; + + $nonrefundableResponse = $this->propertyNonrefundableRepository->findByCriteria($nonrefundableRequest) ; + $nonrefundableData = $nonrefundableResponse ? $nonrefundableResponse : [] ; + $roomRates = $params['delete_room_rates']; + $deleteNonrefundableData = collect($nonrefundableData)->whereIn('room_rate_mapping_id', $roomRates)->values(); + $deleteIds = $deleteNonrefundableData->keyBy('id')->keys()->toArray(); + if($deleteIds){ + $nonrefundableDeleteStatus = $this->propertyNonrefundableRepository->destroy($deleteIds) ; + if($nonrefundableDeleteStatus['status'] != "success"){ + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } 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); + } + + public function getNonrefundableData($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $nonrefundableRequest= [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'whereIn' => [ + ['field' => 'channel_id', 'value' => $params['channels']] + ], + 'with' => ['propertyChannel', 'propertyRoomRateMapping.propertyRoomRate', 'propertyRoomRateMapping.propertyRoom'] + ]; + $nonrefundableResponse = $this->propertyNonrefundableRepository->findByCriteria($nonrefundableRequest, ['id', 'property_id', 'channel_id', 'channel_mapping_id', 'room_rate_mapping_id', 'action_type', 'value_type', 'value']) ; + $nonrefundableData = $nonrefundableResponse ? $nonrefundableResponse : [] ; + $nonrefundableCollection = collect($nonrefundableData) ; + + $channelList = $nonrefundableCollection->unique('channel_id')->map(function ($value){ + return $value['property_channel']; + })->sortBy('name')->values()->toArray(); + + $responseData = [] ; + foreach ($channelList as $channel) { + $channelData = $nonrefundableCollection->where('channel_id', '=', $channel['id'])->values()->toArray(); + $channelItem = $channel ; + $responseChannelData = [] ; + foreach ($channelData as $data) { + $dataItem = $data ; + $dataItem['name'] = $data['property_room_rate_mapping']['property_room']['name'] . ' - ' . $data['property_room_rate_mapping']['property_room_rate']['name']; + $dataItem['room_name'] = $data['property_room_rate_mapping']['property_room']['name']; + $dataItem['rate_name'] = $data['property_room_rate_mapping']['property_room_rate']['name']; + unset($dataItem['property_room_rate_mapping']); + unset($dataItem['property_channel']); + $responseChannelData[] = $dataItem; + } + $responseChannelData = collect($responseChannelData)->values()->toArray(); + + array_multisort( + array_column($responseChannelData, 'room_name'), SORT_ASC, + array_column($responseChannelData, 'rate_name'), SORT_ASC, + $responseChannelData + ); + + $channelItem['nonrefundable_data'] = $responseChannelData; + $responseData[] = $channelItem ; + } + + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + +} diff --git a/app/Core/Service/PropertyPaymentService.php b/app/Core/Service/PropertyPaymentService.php new file mode 100644 index 0000000..48a2b75 --- /dev/null +++ b/app/Core/Service/PropertyPaymentService.php @@ -0,0 +1,3850 @@ +request = $request; + $this->paymentTypeRepository = $paymentTypeRepository; + $this->paymentTransactionRepository = $paymentTransactionRepository; + $this->propertyPaymentMappingRepository = $propertyPaymentMappingRepository; + $this->propertyPaymentInstallmentRepository = $propertyPaymentInstallmentRepository; + $this->paymentBinNumberRepository = $paymentBinNumberRepository; + $this->currencyRatesRepository = $currencyRatesRepository; + $this->propertyPaymentMappingCreateValidator = $propertyPaymentMappingCreateValidator; + $this->propertyPaymentMappingUpdateValidator = $propertyPaymentMappingUpdateValidator; + $this->propertyPaymentMappingStatusUpdateValidator = $propertyPaymentMappingStatusUpdateValidator; + $this->propertyPaymentMappingSetDefaultValidator = $propertyPaymentMappingSetDefaultValidator; + $this->propertyPaymentInstallmentsUpdateValidator = $propertyPaymentInstallmentsUpdateValidator; + $this->currencyService = $currencyService; + $this->propertyPaymentTransactionCreateValidator = $propertyPaymentTransactionCreateValidator; + $this->propertyPaymentTransactionUpdateValidator = $propertyPaymentTransactionUpdateValidator; + + } + + public function getExchangeRates() + { + + $exchangeRate = []; + + $exchangeRatesDailyLastParam = [ + 'criteria' => [], + 'orderBy' => [["field" => "date", "value" => "DESC"]], + 'firstRow' => true + ]; + + $exchangeRatesDailyLast = $this->currencyRatesRepository->findByCriteria($exchangeRatesDailyLastParam); + + + if (isset($exchangeRatesDailyLast['date'])) { + $exchangeRatesDailyParam = [ + 'criteria' => [ + ['field' => 'date', 'condition' => '=', 'value' => $exchangeRatesDailyLast['date']] + ] + ]; + + $exchangeRatesDaily = $this->currencyRatesRepository->findByCriteria($exchangeRatesDailyParam); + + foreach ($exchangeRatesDaily as $rate) { + $exchangeRate[$rate['currency_code'] . $rate['exc_currency_code']] = $rate['rate']; + } + } + + + return $exchangeRate; + } + + public function getPaymentTransactionDetail($paymentCode) + { + + $response = ['status' => false, 'message' => '']; + try { + + $paymentTransactionParam = [ + 'criteria' => [ + ['field' => 'code', 'condition' => '=', 'value' => $paymentCode] + ], + 'with' => ['paymentTypeMapping.paymentType', 'property'], + 'firstRow' => true + ]; + + $paymentTransaction = $this->paymentTransactionRepository->findByCriteria($paymentTransactionParam); + + if (empty($paymentTransaction)) { + throw new ApiErrorException(lang('Payment Transaction is not found.')); + } + + $response = [ + 'status' => true, + 'data' => $paymentTransaction, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function getPaymentTypeMapping($propertyId, $currency, $creditCardNumber) + { + + $paymentTypeAndInstallment['propertyPaymentMappingId'] = null; + $paymentTypeAndInstallment['propertyPaymentMappingInstallment'] = []; + $paymentTypeAndInstallment['propertyPaymentMappingCurrency'] = null; + $propertyPaymentMappingInstallment = []; + + $preferredBankPos = null; + + $creditCardNumber = substr($creditCardNumber, 0, 6); + + + $paymentBinNumberParam = [ + 'criteria' => [ + ['field' => 'bin_number', 'condition' => '=', 'value' => $creditCardNumber] + ], + 'firstRow' => true + ]; + + $paymentBinNumber = $this->paymentBinNumberRepository->findByCriteria($paymentBinNumberParam); + + if (!empty($paymentBinNumber)) { + $preferredBankPos = $paymentBinNumber['payment_type_id']; + } + + $propertyPaymentMappingParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '<>', 'value' => 0], + ], + 'with' => ['paymentType'] + ]; + + $propertyPaymentMapping = $this->propertyPaymentMappingRepository->findByCriteria($propertyPaymentMappingParam); + $propertyPaymentMappingCollect = collect($propertyPaymentMapping); + + + if (empty($propertyPaymentMapping)) { + throw new ApiErrorException(lang('Payment Type Mapping not found')); + } + + //Eğer aynı banka ve aynı currency ve taksit var ise + $propertyPaymentMappingSelect = $propertyPaymentMappingCollect->where('currency_code', $currency)->where('payment_type_id', $preferredBankPos)->where('installment', 1)->sortByDesc('id')->first(); + if (!is_null($propertyPaymentMappingSelect)) { + + $paymentMappingInstallmentParam = [ + 'criteria' => [ + ['field' => 'property_payment_mapping_id', 'condition' => '=', 'value' => $propertyPaymentMappingSelect['id']], + ['field' => 'status', 'condition' => '<>', 'value' => 0], + + ], + 'orderBy' => [["field" => "installment", "value" => "ASC"]], + ]; + + $propertyPaymentMappingInstallment = $this->propertyPaymentInstallmentRepository->findByCriteria($paymentMappingInstallmentParam, ['installment', 'commission']); + + $paymentTypeAndInstallment['propertyPaymentMappingId'] = $propertyPaymentMappingSelect['id']; + $paymentTypeAndInstallment['propertyPaymentMappingCurrency'] = $propertyPaymentMappingSelect['currency_code']; + $paymentTypeAndInstallment['propertyPaymentMappingInstallment'] = $propertyPaymentMappingInstallment; + + } + + //Eğer aynı currency ve banka olmayıp taksit var ise + if (is_null($paymentTypeAndInstallment['propertyPaymentMappingId'])) { + $propertyPaymentMappingSelect = $propertyPaymentMappingCollect->where('currency_code', $currency)->where('installment', 1)->where('payment_type.pos_code', '<>', 'POS')->sortByDesc('id')->first(); + if (!is_null($propertyPaymentMappingSelect)) { + + $paymentMappingInstallmentParam = [ + 'criteria' => [ + ['field' => 'property_payment_mapping_id', 'condition' => '=', 'value' => $propertyPaymentMappingSelect['id']], + ['field' => 'status', 'condition' => '<>', 'value' => 0], + ], + 'orderBy' => [["field" => "installment", "value" => "ASC"]], + ]; + + $propertyPaymentMappingInstallment = $this->propertyPaymentInstallmentRepository->findByCriteria($paymentMappingInstallmentParam, ['installment', 'commission']); + + $paymentTypeAndInstallment['propertyPaymentMappingId'] = $propertyPaymentMappingSelect['id']; + $paymentTypeAndInstallment['propertyPaymentMappingCurrency'] = $propertyPaymentMappingSelect['currency_code']; + $paymentTypeAndInstallment['propertyPaymentMappingInstallment'] = $propertyPaymentMappingInstallment; + + } + } + + //Eğer aynı currency ise + if (is_null($paymentTypeAndInstallment['propertyPaymentMappingId'])) { + $propertyPaymentMappingSelect = $propertyPaymentMappingCollect->where('currency_code', $currency)->sortByDesc('id')->first(); + if (!is_null($propertyPaymentMappingSelect)) { + $paymentTypeAndInstallment['propertyPaymentMappingId'] = $propertyPaymentMappingSelect['id']; + $paymentTypeAndInstallment['propertyPaymentMappingCurrency'] = $propertyPaymentMappingSelect['currency_code']; + } + } + + + //Eğer hiç bişey yok ise default + if (is_null($paymentTypeAndInstallment['propertyPaymentMappingId'])) { + $propertyPaymentMappingSelect = $propertyPaymentMappingCollect->where('is_default', 1)->sortByDesc('id')->first(); + if (!is_null($propertyPaymentMappingSelect)) { + $paymentTypeAndInstallment['propertyPaymentMappingId'] = $propertyPaymentMappingSelect['id']; + $paymentTypeAndInstallment['propertyPaymentMappingCurrency'] = $propertyPaymentMappingSelect['currency_code']; + + + if (!is_null($paymentTypeAndInstallment['propertyPaymentMappingCurrency']) && !is_null($preferredBankPos) && $propertyPaymentMappingSelect['payment_type_id'] == $preferredBankPos) { + + $paymentMappingInstallmentParam = [ + 'criteria' => [ + ['field' => 'property_payment_mapping_id', 'condition' => '=', 'value' => $propertyPaymentMappingSelect['id']], + ['field' => 'status', 'condition' => '<>', 'value' => 0], + ], + 'orderBy' => [["field" => "installment", "value" => "ASC"]], + ]; + + $propertyPaymentMappingInstallment = $this->propertyPaymentInstallmentRepository->findByCriteria($paymentMappingInstallmentParam, ['installment', 'commission']); + + $paymentTypeAndInstallment['propertyPaymentMappingInstallment'] = $propertyPaymentMappingInstallment; + + } else if ($propertyPaymentMappingCollect->whereIn('payment_type.pos_code', ['MOK', 'SPY'])->isNotEmpty()) { + $paymentMappingInstallmentParam = [ + 'criteria' => [ + ['field' => 'property_payment_mapping_id', 'condition' => '=', 'value' => $propertyPaymentMappingSelect['id']], + ['field' => 'status', 'condition' => '<>', 'value' => 0], + ], + 'orderBy' => [["field" => "installment", "value" => "ASC"]], + ]; + + $propertyPaymentMappingInstallment = $this->propertyPaymentInstallmentRepository->findByCriteria($paymentMappingInstallmentParam, ['installment', 'commission']); + + $paymentTypeAndInstallment['propertyPaymentMappingInstallment'] = $propertyPaymentMappingInstallment; + } + + + } + + } + + //FOR Turkish credit cards are forced TRY currency via TRY POS + if (!empty($paymentBinNumber)) { + if ($paymentTypeAndInstallment['propertyPaymentMappingCurrency'] != 'TRY') { + + $propertyPaymentMappingSelect = $propertyPaymentMappingCollect + ->where('currency_code', 'TRY') + ->sortByDesc('id')->first(); + + if (!is_null($propertyPaymentMappingSelect)) { + + $paymentMappingInstallmentParam = [ + 'criteria' => [ + ['field' => 'property_payment_mapping_id', 'condition' => '=', 'value' => $propertyPaymentMappingSelect['id']], + ['field' => 'status', 'condition' => '<>', 'value' => 0], + ], + 'orderBy' => [["field" => "installment", "value" => "ASC"]], + ]; + + + $propertyPaymentMappingInstallment = $this->propertyPaymentInstallmentRepository->findByCriteria($paymentMappingInstallmentParam, ['installment', 'commission']); + + + $paymentTypeAndInstallment['propertyPaymentMappingId'] = $propertyPaymentMappingSelect['id']; + $paymentTypeAndInstallment['propertyPaymentMappingCurrency'] = $propertyPaymentMappingSelect['currency_code']; + $paymentTypeAndInstallment['propertyPaymentMappingInstallment'] = $propertyPaymentMappingInstallment; + } + } + } + + return $paymentTypeAndInstallment; + + } + + public function initializePayment($params = []) + { + $response = ['status' => false, 'message' => '']; + try { + + $exchangeRateStore = 1; + $paymentCode = getPaymentGenerate(fillOnUndefined($params, 'type')); + $ipAddress = $this->request->getClientIp() == '127.0.0.1' ? '185.137.215.118' : $this->request->getClientIp(); + $paymentCheckUrl = Config::get('app.url') . '/paymentCheck/' . $paymentCode; + + if (isset($params['preferredPaymentTypeId'])) { + + $propertyPaymentMappingParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['preferredPaymentTypeId']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['propertyId']], + ['field' => 'status', 'condition' => '<>', 'value' => 0], + ], + 'firstRow' => true, + 'with' => ['paymentType'] + ]; + + $propertyPaymentMapping = $this->propertyPaymentMappingRepository->findByCriteria($propertyPaymentMappingParam); + + if (empty($propertyPaymentMapping)) { + throw new ApiErrorException(lang('Payment Type not found')); + } + + $paymentTypeMapping = [ + "propertyPaymentMappingId" => $propertyPaymentMapping['id'], + 'propertyPaymentMappingInstallment' => [], + 'propertyPaymentMappingCurrency' => $propertyPaymentMapping['currency_code'], + ]; + + + } else { + + $paymentTypeMapping = $this->getPaymentTypeMapping($params['propertyId'], $params['currency'], $params['creditCard']['number']); + + if (is_null($paymentTypeMapping['propertyPaymentMappingId'])) { + throw new ApiErrorException(lang('Payment Type not found')); + } + } + + $installmentRate = []; + if ($params['installment'] > 0) { + + if (empty($paymentTypeMapping['propertyPaymentMappingInstallment'])) { + throw new ApiErrorException(lang('Pos not available installment')); + } + + $propertyPaymentMappingInstallment = collect($paymentTypeMapping['propertyPaymentMappingInstallment']); + + $installmentRate = $propertyPaymentMappingInstallment->where('installment', $params['installment'])->first(); + + if (empty($propertyPaymentMappingInstallment->where('installment', $params['installment'])->first())) { + throw new ApiErrorException(lang('Pos not available this installment number')); + } + + } + + + $paymentTypeMappingId = $paymentTypeMapping['propertyPaymentMappingId']; + + $paymentTypeParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $paymentTypeMappingId] + ], + 'with' => ['paymentType'], + 'firstRow' => true + ]; + + $paymentType = $this->propertyPaymentMappingRepository->findByCriteria($paymentTypeParam); + + $params['amount'] = moneyDoubleFormatDecimal($params['amount']); + $params['base_amount'] = moneyDoubleFormatDecimal($params['amount']); + $params['base_currency'] = $params['currency']; + + + //Eğer istenen döviz tipi postan farklı ise convert edilecek + if ($params['currency'] != $paymentType['currency_code']) { + + $exchangeRate = $this->getExchangeRates(); + + if (!isset($exchangeRate[$params['currency'] . $paymentType['currency_code']])) { + throw new ApiErrorException(lang('Currency exchange value not found.')); + } + + $exchangeRateStore = $exchangeRate[$params['currency'] . $paymentType['currency_code']]; + + $params['amount'] = moneyDoubleFormatDecimal($params['amount'] * $exchangeRateStore); + $params['currency'] = $paymentType['currency_code']; + + } + + if ($params['installment'] > 0 && !empty($installmentRate)) { + $params['amount'] = moneyDoubleFormatDecimal($params['amount'] + ($params['amount'] * $installmentRate['commission'] / 100)); + $params['installmentRate'] = $installmentRate; + } + + $paymentParam = []; + + //TODO: PaymentFactory + if ($paymentType['payment_type']['pos_code'] == 'POS') { + + $posAccount = []; + $posAccount['bank'] = $paymentType['payment_type']['bank_code']; + $posAccount['model'] = $paymentType['payment_type']['threed_model']; + $posAccount['env'] = $paymentType['status'] == 1 ? 'production' : 'test'; + + foreach ($paymentType['paramsArray'] as $paramKey => $param) { + + if (isset($paymentType['payment_type']['paramsArray'][$paramKey])) { + $posAccount[$paymentType['payment_type']['paramsArray'][$paramKey]['key']] = $param; + } + } + + $POS = new \App\Core\Payment\Pos\Pos($posAccount); + + $params['installment'] = $params['installment'] == 1 ? 0 : $params['installment']; + + $orderParam = [ + 'id' => $paymentCode, + 'email' => '', + 'name' => $params['creditCard']['name'], + 'amount' => (double)$params['amount'], + 'installment' => $params['installment'], + 'currency' => $paymentType['currency_code'], + 'ip' => $ipAddress, + 'success_url' => $paymentCheckUrl, + 'fail_url' => $paymentCheckUrl, + 'transaction' => 'pay', // pay => Auth, pre PreAuth + 'lang' => 'tr', + 'rand' => microtime() + ]; + + $params['orderParam'] = $orderParam; + + + $creditCard = [ + 'name' => $params['creditCard']['name'], + //'type' => 'visa', + 'number' => $params['creditCard']['number'], + 'month' => $params['creditCard']['month'], + 'year' => $params['creditCard']['year'], + 'cvv' => $params['creditCard']['cvv'] + ]; + + //dd($creditCard, $orderParam); + + $POS->prepare($orderParam, $creditCard); + $paymentParam = $POS->get3dFormData(); + + if (empty($paymentParam)) { + throw new ApiErrorException(lang('Failed to create 3D Form data.')); + } + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'STR') { + + + try { + + //Secret key + $secretKey = $paymentType['paramsArray']['secretKey']; + $stripe = new \Stripe\StripeClient($secretKey); + + $token = $stripe->tokens->create([ + 'card' => [ + 'number' => $params['creditCard']['number'], + 'exp_month' => $params['creditCard']['month'], + 'exp_year' => $params['creditCard']['year'], + 'cvc' => $params['creditCard']['cvv'] + ], + ]); + + + $source = $stripe->sources->create([ + 'currency' => $params['currency'], + 'amount' => ($params['amount'] * 100), + 'type' => 'three_d_secure', + 'three_d_secure' => [ + 'card' => $token->card->id, + ], + 'redirect' => [ + 'return_url' => $paymentCheckUrl + ], + ]); + + //Pending, 3DSecure + if ($source['status'] == 'failed') { + + //Direct Payment + $charge = $stripe->charges->create([ + 'card' => $token->id, + 'currency' => $params['currency'], + 'amount' => ($params['amount'] * 100), + 'description' => $paymentCode, + ]); + + if ($charge['status'] == 'succeeded') { + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode + ], + ]; + + } + + } else { + //Aksi tüm durumlarda 3dSecure devam et + //} elseif (in_array($source['status'], ['pending', 'chargeable'])) { + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } + + } catch (Exception $e) { + $response['message'] = $e->getMessage(); + } + + + $paymentTransactionStatus = 0; + if ($response['status']) { + + $paymentTransactionStatus = 1; + + if (isset($response['data']['redirectUrl'])) { + $paymentTransactionStatus = 2; + } + } + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'bank_order_id' => isset($charge) ? $charge->id : null, + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => isset($source) ? json_encode($source) : null, + 'response' => isset($charge) ? json_encode($charge) : null, + 'message' => fillOnUndefined($response, 'message'), + 'status' => $paymentTransactionStatus, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + //STRIPE END + + + } elseif ($paymentType['payment_type']['pos_code'] == 'ENW') { + + + try { + + //Secret key + $secretKey = config('app.stripeENWKey'); + $stripe = new \Stripe\StripeClient($secretKey); + + $token = $stripe->tokens->create([ + 'card' => [ + 'number' => $params['creditCard']['number'], + 'exp_month' => $params['creditCard']['month'], + 'exp_year' => $params['creditCard']['year'], + 'cvc' => $params['creditCard']['cvv'] + ], + ]); + + + $source = $stripe->sources->create([ + 'currency' => $params['currency'], + 'amount' => ($params['amount'] * 100), + 'type' => 'three_d_secure', + 'three_d_secure' => [ + 'card' => $token->card->id, + ], + 'redirect' => [ + 'return_url' => $paymentCheckUrl + ], + ]); + + //Pending, 3DSecure + if ($source['status'] == 'failed') { + + //Direct Payment + $charge = $stripe->charges->create([ + 'card' => $token->id, + 'currency' => $params['currency'], + 'amount' => ($params['amount'] * 100), + 'description' => $paymentCode, + ]); + + if ($charge['status'] == 'succeeded') { + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode + ], + ]; + + } + + } else { + //Aksi tüm durumlarda 3dSecure devam et + //} elseif (in_array($source['status'], ['pending', 'chargeable'])) { + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } + + } catch (Exception $e) { + $response['message'] = $e->getMessage(); + } + + + $paymentTransactionStatus = 0; + if ($response['status']) { + + $paymentTransactionStatus = 1; + + if (isset($response['data']['redirectUrl'])) { + $paymentTransactionStatus = 2; + } + } + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'bank_order_id' => isset($charge) ? $charge->id : null, + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => isset($source) ? json_encode($source) : null, + 'response' => isset($charge) ? json_encode($charge) : null, + 'message' => fillOnUndefined($response, 'message'), + 'status' => $paymentTransactionStatus, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + //STRIPE ENW END + + + } elseif ($paymentType['payment_type']['pos_code'] == 'SPY') { + + + $paymentInitializeParam = [ + 'merchantId' => $paymentType['paramsArray']['merchantId'], + 'merchantKey' => $paymentType['paramsArray']['merchantKey'], + 'appKey' => $paymentType['paramsArray']['appKey'], + 'appSecret' => $paymentType['paramsArray']['appSecret'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + + $sipayService = new App\Core\Payment\Sipay\Sipay($paymentInitializeParam); + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl + ]; + + $creditPaymentParam['creditCard'] = [ + 'holderName' => $params['creditCard']['name'], + 'number' => $params['creditCard']['number'], + 'expiryMonth' => $params['creditCard']['month'], + 'expiryYear' => $params['creditCard']['year'], + 'cvv' => $params['creditCard']['cvv'], + 'installment' => $params['installment'], + ]; + + + $paymentParam = $sipayService->paySmart3D($creditPaymentParam); + + if (!$paymentParam['status']) { + throw new ApiErrorException(lang('3D Form data not generated.')); + } + + $paymentParam = $paymentParam['data']; + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'MOK') { + + $paymentInitializeParam = [ + 'userName' => $paymentType['paramsArray']['userName'], + 'password' => $paymentType['paramsArray']['password'], + 'dealerCode' => $paymentType['paramsArray']['dealerCode'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + + $mokaService = new App\Core\Payment\Moka\Moka($paymentInitializeParam); + + $params['currency'] = $params['currency'] == 'TRY' ? 'TL' : $params['currency']; + + $paymentCreateParam = [ + 'key' => $params['orderId'], + 'CardHolderFullName' => $params['creditCard']['name'], + 'CardNumber' => $params['creditCard']['number'], + 'ExpMonth' => $params['creditCard']['month'], + 'ExpYear' => $params['creditCard']['year'], + 'CvcNumber' => $params['creditCard']['cvv'], + 'Currency' => $params['currency'], + 'Amount' => (double)$params['amount'],//10.50 + 'InstallmentNumber' => isset($params['installment']) ? $params['installment'] : 1, + 'OtherTrxCode' => $params['orderId'], + 'Description' => fillOnUndefined($params, 'description'), + 'RedirectUrl' => $paymentCheckUrl, + 'IsPreAuth' => 0, //0: Direct Provision 1: PreProvision + 'ClientIP' => config('app.env') == 'local' ? '185.137.215.118' : $ipAddress + ]; + + $paymentParam = $mokaService->DoDirectPaymentThreeD($paymentCreateParam); + + + if (!$paymentParam['status']) { + throw new ApiErrorException(lang('3D Form data not generated.')); + } + + $paymentParam = $paymentParam['data']; + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'BOG') { + + + $paymentInitializeParam = [ + 'clientId' => $paymentType['paramsArray']['clientId'], + 'secretKey' => $paymentType['paramsArray']['secretKey'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + $bankOfGeorgiaService = new App\Core\Payment\BankOfGeorgia\Payriff($paymentInitializeParam); + + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl + ]; + + if ($creditPaymentParam['currencyCode'] != 'GEL') { + throw new ApiErrorException(lang('This payment method only support GEL.')); + } + + $paymentParam = $bankOfGeorgiaService->paymentInitialize($creditPaymentParam); + + + if (!$paymentParam['status']) { + throw new ApiErrorException('Payment Initialize is not started.'); + } + + $paymentParam = $paymentParam['data']; + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'TBC') { + + + $paymentInitializeParam = [ + 'clientId' => $paymentType['paramsArray']['clientId'], + 'clientSecret' => $paymentType['paramsArray']['clientSecret'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + $tbcBankService = new App\Core\Payment\TBCBankGeorgia\TBCBankGeorgia($paymentInitializeParam); + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl, + 'ipAddress' => $ipAddress + ]; + + $paymentParam = $tbcBankService->paymentInitialize($creditPaymentParam); + + if (!$paymentParam['status']) { + throw new ApiErrorException('Payment Initialize is not started.'); + } + + $paymentParam = $paymentParam['data']; + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'QNB') { + + + $paymentInitializeParam = [ + 'merchantId' => $paymentType['paramsArray']['merchantId'], + 'merchantKey' => $paymentType['paramsArray']['merchantKey'], + 'appKey' => $paymentType['paramsArray']['appKey'], + 'appSecret' => $paymentType['paramsArray']['appSecret'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + $qnbPayService = new App\Core\Payment\QNBPay\QNBPay($paymentInitializeParam); + + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl + ]; + + $creditPaymentParam['creditCard'] = [ + 'holderName' => $params['creditCard']['name'], + 'number' => $params['creditCard']['number'], + 'expiryMonth' => $params['creditCard']['month'], + 'expiryYear' => $params['creditCard']['year'], + 'cvv' => $params['creditCard']['cvv'], + 'installment' => $params['installment'], + ]; + + + $paymentParam = $qnbPayService->paySmart3D($creditPaymentParam); + + if (!$paymentParam['status']) { + throw new ApiErrorException(lang('3D Form data not generated.')); + } + + $paymentParam = $paymentParam['data']; + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'TBC') { + + + $paymentInitializeParam = [ + 'clientId' => $paymentType['paramsArray']['clientId'], + 'clientSecret' => $paymentType['paramsArray']['clientSecret'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + $tbcBankService = new App\Core\Payment\TBCBankGeorgia\TBCBankGeorgia($paymentInitializeParam); + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl, + 'ipAddress' => $ipAddress + ]; + + $paymentParam = $tbcBankService->paymentInitialize($creditPaymentParam); + + if (!$paymentParam['status']) { + throw new ApiErrorException('Payment Initialize is not started.'); + } + + $paymentParam = $paymentParam['data']; + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'WEE') { + + + $paymentInitializeParam = [ + 'merchantId' => $paymentType['paramsArray']['merchantId'], + 'apiKey' => $paymentType['paramsArray']['apiKey'], + 'secretKey' => $paymentType['paramsArray']['secretKey'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + 'ipAddress' => $ipAddress + ]; + + $weePayService = new App\Core\Payment\WeePay\WeePay($paymentInitializeParam); + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl + ]; + + $creditPaymentParam['creditCard'] = [ + 'holderName' => $params['creditCard']['name'], + 'number' => $params['creditCard']['number'], + 'expiryMonth' => $params['creditCard']['month'], + 'expiryYear' => $params['creditCard']['year'], + 'cvv' => $params['creditCard']['cvv'], + 'installment' => $params['installment'], + ]; + + + $paymentParam = $weePayService->paySmart3D($creditPaymentParam); + + if (!$paymentParam['status']) { + throw new ApiErrorException(lang('3D Form data not generated.')); + } + + $paymentParam = $paymentParam['data']; + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'HLK') { + + + $paymentInitializeParam = [ + 'merchantId' => $paymentType['paramsArray']['merchantId'], + 'merchantKey' => $paymentType['paramsArray']['merchantKey'], + 'appKey' => $paymentType['paramsArray']['appKey'], + 'appSecret' => $paymentType['paramsArray']['appSecret'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + $paymentTypeService = new App\Core\Payment\HalkOde\HalkOde($paymentInitializeParam); + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl + ]; + + $creditPaymentParam['creditCard'] = [ + 'holderName' => $params['creditCard']['name'], + 'number' => $params['creditCard']['number'], + 'expiryMonth' => $params['creditCard']['month'], + 'expiryYear' => $params['creditCard']['year'], + 'cvv' => $params['creditCard']['cvv'], + 'installment' => $params['installment'], + ]; + + + $paymentParam = $paymentTypeService->paySmart3D($creditPaymentParam); + + if (!$paymentParam['status']) { + throw new ApiErrorException(lang('3D Form data not generated.')); + } + + $paymentParam = $paymentParam['data']; + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'ESN') { + + + $paymentInitializeParam = [ + 'merchant' => $paymentType['paramsArray']['merchant'], + 'merchantKey' => $paymentType['paramsArray']['merchantKey'], + 'contactMail' => $paymentType['paramsArray']['contactMail'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + $esnekPosService = new App\Core\Payment\Esnekpos\Esnekpos($paymentInitializeParam); + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl + ]; + + $creditPaymentParam['creditCard'] = [ + 'holderName' => $params['creditCard']['name'], + 'number' => $params['creditCard']['number'], + 'expiryMonth' => $params['creditCard']['month'], + 'expiryYear' => $params['creditCard']['year'], + 'cvv' => $params['creditCard']['cvv'], + 'installment' => $params['installment'], + ]; + + + $paymentRequest = $esnekPosService->EYV3DPay($creditPaymentParam); + + if (!$paymentRequest['status']) { + throw new ApiErrorException($paymentRequest['message']); + } + + $paymentRequest = $paymentRequest['data']; + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentRequest), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'KVY') { + + $paymentInitializeParam = [ + 'customerId' => $paymentType['paramsArray']['customerId'], + 'merchantId' => $paymentType['paramsArray']['merchantId'], + 'userName' => $paymentType['paramsArray']['userName'], + 'password' => $paymentType['paramsArray']['password'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + $paymentTypeService = new App\Core\Payment\KuveytTurk\KuveytTurk($paymentInitializeParam); + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl + ]; + + $creditPaymentParam['creditCard'] = [ + 'holderName' => $params['creditCard']['name'], + 'number' => $params['creditCard']['number'], + 'expiryMonth' => $params['creditCard']['month'], + 'expiryYear' => $params['creditCard']['year'], + 'cvv' => $params['creditCard']['cvv'], + 'installment' => $params['installment'], + ]; + + + $paymentParam = $paymentTypeService->threeDModelPayGate($creditPaymentParam); + + if (!$paymentParam['status']) { + throw new ApiErrorException(lang('3D Form data not generated.')); + } + + $paymentParam = $paymentParam['data']; + + $params['creditCard']['number'] = creditCardMask($params['creditCard']['number']); + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => $paymentParam, + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'RTL') { + + + $paymentInitializeParam = [ + 'userName' => $paymentType['paramsArray']['userName'], + 'password' => $paymentType['paramsArray']['password'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + 'ipAddress' => $ipAddress + ]; + + $retailPayService = new App\Core\Payment\RetailPay\RetailPay($paymentInitializeParam); + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl + ]; + + $creditPaymentParam['creditCard'] = [ + 'holderName' => $params['creditCard']['name'], + 'number' => $params['creditCard']['number'], + 'expiryMonth' => $params['creditCard']['month'], + 'expiryYear' => $params['creditCard']['year'], + 'cvv' => $params['creditCard']['cvv'], + 'installment' => $params['installment'], + ]; + + + $creditPaymentParam['client'] = [ + 'name' => isset($params['client']['name']) ? $params['client']['name'] : 'Extranetwork', + 'email' => isset($params['client']['email']) ? $params['client']['email'] : 'info@extranetwork.com', + 'phone' => isset($params['client']['phone']) ? $params['client']['phone'] : '+908508886428', + ]; + + + $paymentParam = $retailPayService->ordersAuthorize($creditPaymentParam); + + + if (!$paymentParam['status']) { + throw new ApiErrorException(lang('3D Form data not generated.')); + } + + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam['data']), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } elseif ($paymentType['payment_type']['pos_code'] == 'PYR') { + + + $paymentInitializeParam = [ + 'secretKey' => $paymentType['paramsArray']['secretKey'], + 'env' => $paymentType['status'] == 1 ? 'production' : 'test', + ]; + + $payriffService = new App\Core\Payment\Payriff\Payriff($paymentInitializeParam); + + + $creditPaymentParam = [ + 'amount' => (double)$params['amount'], + 'orderId' => $paymentCode, + 'orderCode' => $params['orderId'], + 'currencyCode' => $params['currency'], + 'paymentCheckUrl' => $paymentCheckUrl + ]; + + if ($creditPaymentParam['currencyCode'] != 'AZN') { + throw new ApiErrorException(lang('This payment method only support AZN.')); + } + + $paymentParam = $payriffService->paymentInitialize($creditPaymentParam); + + if (!$paymentParam['status']) { + throw new ApiErrorException('Payment Initialize is not started.'); + } + + $paymentParam = $paymentParam['data']; + + $paymentTransactionParam = [ + 'property_id' => $params['propertyId'], + 'code' => $paymentCode, + 'order_id' => $params['orderId'], + 'transaction_type' => fillOnUndefined($params, 'type', 'BKG'), + 'payment_type_mapping_id' => $paymentTypeMappingId, + 'base_amount' => $params['base_amount'], + 'base_currency' => $params['base_currency'], + 'exchange_rate' => $exchangeRateStore, + 'amount' => $params['amount'], + 'currency' => $paymentType['currency_code'], + 'installment' => $params['installment'], + 'params' => json_encode($params), + 'extra_params' => json_encode($paymentParam), + 'status' => 2, + ]; + + $paymentTransactionCreate = $this->paymentTransactionRepository->create($paymentTransactionParam); + + if ($paymentTransactionCreate['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => [ + 'paymentCode' => $paymentCode, + 'redirectUrl' => Config::get('app.url') . '/paymentRedirect/' . $paymentCode + ], + ]; + + } else { + throw new ApiErrorException(lang('Payment Type not found')); + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + + public function checkPayment($paymentCode, $params = []) + { + $response = ['status' => false, 'message' => '', 'data' => []]; + try { + + //dd(Input::All()); + $bankOrderId = null; + + $paymentTransactionDetail = $this->getPaymentTransactionDetail($paymentCode); + if (!$paymentTransactionDetail['status']) { + throw new ApiErrorException(lang('Payment Transaction not found.')); + } + + $paymentTransactionDetail = $paymentTransactionDetail['data']; + + $responseUrl = $paymentTransactionDetail['paramsArray']['responseUrl']; + $response['data']['responseUrl'] = $responseUrl; + $response['data']['paymentCode'] = $paymentCode; + + if ($paymentTransactionDetail['status'] != 3) { + Log::error($paymentCode); + Log::error($params); + throw new Exception('A previously terminated transaction.'); + } + + + if ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'POS') { + + $posAccount = []; + $posAccount['bank'] = $paymentTransactionDetail['payment_type_mapping']['payment_type']['bank_code']; + $posAccount['model'] = $paymentTransactionDetail['payment_type_mapping']['payment_type']['threed_model']; + $posAccount['env'] = $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test'; + + + foreach ($paymentTransactionDetail['payment_type_mapping']['paramsArray'] as $paramKey => $param) { + + if (isset($paymentTransactionDetail['payment_type_mapping']['payment_type']['paramsArray'][$paramKey])) { + $posAccount[$paymentTransactionDetail['payment_type_mapping']['payment_type']['paramsArray'][$paramKey]['key']] = $param; + } + } + + $POS = new \App\Core\Payment\Pos\Pos($posAccount); + + if (isset($paymentTransactionDetail['paramsArray']['creditCard'])) { + $paymentTransactionDetail['paramsArray']['orderParam']['creditCardMonth'] = $paymentTransactionDetail['paramsArray']['creditCard']['month']; + $paymentTransactionDetail['paramsArray']['orderParam']['creditCardYear'] = $paymentTransactionDetail['paramsArray']['creditCard']['year']; + $paymentTransactionDetail['paramsArray']['orderParam']['creditCardYearCvv'] = $paymentTransactionDetail['paramsArray']['creditCard']['cvv']; + } + + $POS->prepare($paymentTransactionDetail['paramsArray']['orderParam']); + + $payment = $POS->payment(); + + if ($payment->isSuccess()) { + + $bankOrderId = $payment->response->trans_id; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($payment->response), + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + + } else { + + $errorMessage = null; + + + if (!empty($payment->response->error_message)) { + $errorMessage = $payment->response->error_message; + } + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => json_encode($payment->response), + ]; + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + throw new ApiErrorException($payment->response->error_message); + } + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'STR') { + + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + $secretKey = $paymentTransactionDetail['payment_type_mapping']['paramsArray']['secretKey']; + $stripe = new \Stripe\StripeClient($secretKey); + + $charge = $stripe->charges->create([ + 'currency' => $paymentTransactionDetail['currency'], + 'amount' => ($paymentTransactionDetail['amount'] * 100), + 'source' => $params['source'], + 'description' => $paymentTransactionDetail['code'] + ]); + + Log::debug($charge); + + if ($charge['status'] == 'succeeded') { + + $bankOrderId = $charge->id; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($charge), + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + + } catch (Exception $e) { + $errorMessage = $e->getMessage(); + } + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($charge) ? json_encode($charge) : null, + ]; + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + throw new ApiErrorException($errorMessage); + + } + + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'ENW') { + + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + $secretKey = config('app.stripeENWKey'); + $stripe = new \Stripe\StripeClient($secretKey); + + $charge = $stripe->charges->create([ + 'currency' => $paymentTransactionDetail['currency'], + 'amount' => ($paymentTransactionDetail['amount'] * 100), + 'source' => $params['source'], + 'description' => $paymentTransactionDetail['code'] . ' - ' . $paymentTransactionDetail['property_id'] . ' - ' . $paymentTransactionDetail['property']['name'] + ]); + + Log::debug($charge); + + if ($charge['status'] == 'succeeded') { + + $bankOrderId = $charge->id; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($charge), + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + + } catch (Exception $e) { + + $errorMessage = $e->getMessage(); + } + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($charge) ? json_encode($charge) : null, + ]; + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + throw new ApiErrorException($errorMessage); + + } + + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'SPY') { + + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + $paymentInitializeParam = [ + 'merchantId' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchantId'], + 'merchantKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchantKey'], + 'appKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['appKey'], + 'appSecret' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['appSecret'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $sipayService = new App\Core\Payment\Sipay\Sipay($paymentInitializeParam); + + $checkPaymentStatus = $sipayService->checkPaymentStatus($paymentCode); + + if (isset($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + if (floatval($paymentTransactionDetail['amount']) != floatval($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + throw new ApiErrorException('Payment received, but amounts do not match!'); + } + } + + if ($params['sipay_status'] == 1 && $checkPaymentStatus['status']) { + + $bankOrderId = $checkPaymentStatus['data']['order_id']; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($checkPaymentStatus['serviceResponse']) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + } else { + + throw new ApiErrorException($params['status_description']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkPaymentStatus['serviceResponse']) ? json_encode($checkPaymentStatus['serviceResponse']) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'MOK') { + + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + $paymentInitializeParam = [ + 'userName' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['userName'], + 'password' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['password'], + 'dealerCode' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['dealerCode'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $mokaService = new App\Core\Payment\Moka\Moka($paymentInitializeParam); + + $paramPaymentDetail = [ + //'PaymentId' => fillOnUndefined($param,'OtherTrxCode'), + 'OtherTrxCode' => $paymentTransactionDetail['order_id'], + ]; + + $getPaymentDetail = $mokaService->getPaymentDetail($paramPaymentDetail); + + if (!$getPaymentDetail['status']) { + throw new ApiErrorException($getPaymentDetail['message']); + } + + if (floatval($paymentTransactionDetail['amount']) != floatval($getPaymentDetail['data']['PaymentDetail']['Amount'])) { + throw new Exception('Payment received, but amounts do not match!'); + } + + if ($getPaymentDetail['data']['IsSuccessful'] && $getPaymentDetail['data']['PaymentDetail']['TrxStatus'] == 1) { + + $bankOrderId = $getPaymentDetail['data']['PaymentDetail']['DealerPaymentId']; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($getPaymentDetail) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } else { + + throw new ApiErrorException(reset($getPaymentDetail['data']['PaymentTrxDetailList'])['ResultMessage']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($getPaymentDetail) ? json_encode($getPaymentDetail) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'BOG') { + + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + + $paymentInitializeParam = [ + 'clientId' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['clientId'], + 'secretKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['secretKey'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $bankOfGeorgiaService = new App\Core\Payment\BankOfGeorgia\Payriff($paymentInitializeParam); + + + $checkoutOrderInfo = $bankOfGeorgiaService->checkoutOrderInfo($paymentTransactionDetail['extraParamsArray']['order_id']); + + + if (!$checkoutOrderInfo['status']) { + throw new ApiErrorException($checkoutOrderInfo['message']); + } + + if (floatval($paymentTransactionDetail['amount']) != floatval($checkoutOrderInfo['data']['purchaseUnit']['amount']['value'])) { + throw new Exception('Payment received, but amounts do not match!'); + } + + + if ($checkoutOrderInfo['data']['status'] == 'PERFORMED') { + + $bankOrderId = $checkoutOrderInfo['data']['id']; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($checkoutOrderInfo) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } else { + + $errorMessage = $checkoutOrderInfo['data']['message']; + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkoutOrderInfo) ? json_encode($checkoutOrderInfo) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + throw new ApiErrorException($checkoutOrderInfo['data']['message']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkoutOrderInfo) ? json_encode($checkoutOrderInfo) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'TBC') { + + $bankOrderId = null; + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + $paymentInitializeParam = [ + 'clientId' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['clientId'], + 'clientSecret' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['clientSecret'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $tbcBankService = new App\Core\Payment\TBCBankGeorgia\TBCBankGeorgia($paymentInitializeParam); + + $checkoutOrderInfo = $tbcBankService->checkoutOrderInfo($paymentTransactionDetail['extraParamsArray']['payId']); + + if (!$checkoutOrderInfo['status']) { + //throw new ApiErrorException($checkoutOrderInfo['message']); + $errorMessage = $checkoutOrderInfo['message']; + } + + if (floatval($paymentTransactionDetail['amount']) != floatval($checkoutOrderInfo['data']['confirmedAmount'])) { + //throw new Exception('Payment received, but amounts do not match!'); + $errorMessage = 'Payment received, but amounts do not match!'; + } + + if (empty($errorMessage)) { + + $bankOrderId = $checkoutOrderInfo['data']['payId']; + + if ($checkoutOrderInfo['data']['status'] == 'Succeeded') { + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($checkoutOrderInfo) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + //throw new Exception('api-unknown_error'); + $errorMessage = 'api-unknown_error'; + } + + } else { + + $errorMessage = $checkoutOrderInfo['data']['userMessage']; + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkoutOrderInfo) ? json_encode($checkoutOrderInfo) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + } + + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkoutOrderInfo) ? json_encode($checkoutOrderInfo) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + throw new ApiErrorException($errorMessage); + + } + + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'QNB') { + + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + $paymentInitializeParam = [ + 'merchantId' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchantId'], + 'merchantKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchantKey'], + 'appKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['appKey'], + 'appSecret' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['appSecret'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $qnbPayService = new App\Core\Payment\QNBPay\QNBPay($paymentInitializeParam); + + $checkPaymentStatus = $qnbPayService->checkPaymentStatus($paymentCode); + + if (isset($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + if (floatval($paymentTransactionDetail['amount']) != floatval($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + //throw new ApiErrorException('Payment received, but amounts do not match!'); + } + } + + if ($params['payment_status'] == 1 && $checkPaymentStatus['status']) { + + $bankOrderId = $checkPaymentStatus['data']['order_id']; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($checkPaymentStatus['serviceResponse']) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + } else { + + throw new ApiErrorException($params['status_description']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkPaymentStatus['serviceResponse']) ? json_encode($checkPaymentStatus['serviceResponse']) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'WEE') { + + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + $paymentInitializeParam = [ + 'merchantId' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchantId'], + 'apiKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['apiKey'], + 'secretKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['secretKey'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $weePayService = new App\Core\Payment\WeePay\WeePay($paymentInitializeParam); + + $checkPaymentStatus = $weePayService->checkPaymentStatus($paymentCode); + + if (isset($checkPaymentStatus['data']['price'])) { + if (floatval($paymentTransactionDetail['amount']) != floatval($checkPaymentStatus['data']['price'])) { + throw new ApiErrorException('Payment received, but amounts do not match!'); + } + } + + if ($params['status'] == 'success' && $checkPaymentStatus['status']) { + + $bankOrderId = $checkPaymentStatus['data']['paymentId']; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($checkPaymentStatus['serviceResponse']) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + } else { + + throw new ApiErrorException($checkPaymentStatus['message']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkPaymentStatus['serviceResponse']) ? json_encode($checkPaymentStatus['serviceResponse']) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'HLK') { + + $errorMessage = null; + + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + $paymentInitializeParam = [ + 'merchantId' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchantId'], + 'merchantKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchantKey'], + 'appKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['appKey'], + 'appSecret' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['appSecret'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $qnbPayService = new App\Core\Payment\HalkOde\HalkOde($paymentInitializeParam); + + $checkPaymentStatus = $qnbPayService->checkPaymentStatus($paymentCode); + + if (isset($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + if (floatval($paymentTransactionDetail['amount']) != floatval($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + //throw new ApiErrorException('Payment received, but amounts do not match!'); + } + } + + if ($params['payment_status'] == 1 && $checkPaymentStatus['status']) { + + $bankOrderId = $checkPaymentStatus['data']['order_id']; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($checkPaymentStatus['serviceResponse']) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + } else { + + throw new ApiErrorException($params['status_description']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkPaymentStatus['serviceResponse']) ? json_encode($checkPaymentStatus['serviceResponse']) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'ESN') { + + $errorMessage = null; + + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + $paymentInitializeParam = [ + 'merchant' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchant'], + 'merchantKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchantKey'], + 'contactMail' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['contactMail'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $esnekPosService = new App\Core\Payment\Esnekpos\Esnekpos($paymentInitializeParam); + + $checkPaymentStatus = $esnekPosService->checkPaymentStatus($paymentCode); + + if (isset($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + if (floatval($paymentTransactionDetail['amount']) != floatval($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + //throw new ApiErrorException('Payment received, but amounts do not match!'); + } + } + + if ($params['RETURN_CODE'] == 0 && $checkPaymentStatus['status']) { + + $bankOrderId = $checkPaymentStatus['data']['REFNO']; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($checkPaymentStatus['serviceResponse']) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + } else { + + throw new ApiErrorException($checkPaymentStatus['message']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkPaymentStatus['serviceResponse']) ? json_encode($checkPaymentStatus['serviceResponse']) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'KVY') { + + $errorMessage = null; + + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + $paymentInitializeParam = [ + 'customerId' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['customerId'], + 'merchantId' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['merchantId'], + 'userName' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['userName'], + 'password' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['password'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $posService = new App\Core\Payment\KuveytTurk\KuveytTurk($paymentInitializeParam); + + $checkPaymentStatus = $posService->checkPaymentStatus($paymentCode, $params); + + //dd($paymentTransactionDetail, $params, $checkPaymentStatus); + + if (isset($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + if (floatval($paymentTransactionDetail['amount']) != floatval($checkPaymentStatus['serviceResponse']['transaction_amount'])) { + //throw new ApiErrorException('Payment received, but amounts do not match!'); + } + } + + if ($checkPaymentStatus['status']) { + + $bankOrderId = isset($checkPaymentStatus['data']['ProvisionNumber']) ? $checkPaymentStatus['data']['ProvisionNumber'] : null; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($checkPaymentStatus['serviceResponse']) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + } else { + + throw new ApiErrorException($checkPaymentStatus['message']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkPaymentStatus['serviceResponse']) ? json_encode($checkPaymentStatus['serviceResponse']) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'RTL') { + + $bankOrderId = null; + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + + $paymentInitializeParam = [ + 'userName' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['userName'], + 'password' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['password'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $retailPayService = new App\Core\Payment\RetailPay\RetailPay($paymentInitializeParam); + + if (!isset($params['order_id'])) { + throw new ApiErrorException('order_id not found!'); + } + + + $orderCharge = $retailPayService->orderCharge($params['order_id']); + + if (!$orderCharge['status']) { + throw new ApiErrorException($orderCharge['message']); + } + + $checkPaymentStatus = $retailPayService->checkPaymentStatus($params['order_id']); + + /*if (isset($checkPaymentStatus['data']['price'])) { + if (floatval($paymentTransactionDetail['amount']) != floatval($checkPaymentStatus['data']['price'])) { + throw new ApiErrorException('Payment received, but amounts do not match!'); + } + }*/ + + + if ($checkPaymentStatus['status']) { + + $bankOrderId = $checkPaymentStatus['data']['id']; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($checkPaymentStatus['serviceResponse']) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + } else { + + throw new ApiErrorException($checkPaymentStatus['message']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($checkPaymentStatus['serviceResponse']) ? json_encode($checkPaymentStatus['serviceResponse']) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + } elseif ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'PYR') { + + $bankOrderId = null; + $errorMessage = null; + + if ($paymentTransactionDetail['status'] == 1) { + + $bankOrderId = $paymentTransactionDetail['bank_order_id']; + + } else { + + try { + + + $paymentInitializeParam = [ + 'secretKey' => $paymentTransactionDetail['payment_type_mapping']['paramsArray']['secretKey'], + 'env' => $paymentTransactionDetail['payment_type_mapping']['status'] == 1 ? 'production' : 'test', + ]; + + $payriffService = new App\Core\Payment\Payriff\Payriff($paymentInitializeParam); + + if (!isset($paymentTransactionDetail['extraParamsArray']['payload']['orderId'])) { + throw new ApiErrorException('order_id not found!'); + } + + + $orderInformation = $payriffService->orderInformation($paymentTransactionDetail['extraParamsArray']['payload']['orderId']); + + if (!$orderInformation['status']) { + throw new ApiErrorException($orderInformation['message']); + } + + /*if (isset($checkPaymentStatus['data']['price'])) { + if (floatval($paymentTransactionDetail['amount']) != floatval($checkPaymentStatus['data']['price'])) { + throw new ApiErrorException('Payment received, but amounts do not match!'); + } + }*/ + + + if ($orderInformation['status']) { + + if ($orderInformation['serviceResponse']['payload']['paymentStatus'] == 'APPROVED') { + $bankOrderId = $orderInformation['serviceResponse']['payload']['orderId']; + + $updateParam = [ + 'bank_order_id' => $bankOrderId, + 'status' => 1, + 'message' => null, + 'response' => json_encode($orderInformation['serviceResponse']) + ]; + $paymentTransactionUpdateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + if ($paymentTransactionUpdateResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + } else { + + throw new ApiErrorException('Payment not confirmed!'); + } + + + } else { + + throw new ApiErrorException($orderInformation['message']); + + } + + } catch (ApiErrorException $e) { + + $errorMessage = $e->getMessage(); + } + + } + + + if (!empty($errorMessage)) { + + $updateParam = [ + 'status' => 0, + 'message' => $errorMessage, + 'response' => isset($orderInformation['serviceResponse']) ? json_encode($orderInformation['serviceResponse']) : null, + ]; + + $updateResult = $this->paymentTransactionRepository->update($paymentTransactionDetail['id'], $updateParam); + + } + + } + + $response['status'] = true; + $response['data']['bankOrderId'] = $bankOrderId; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + //Mask CreditCard Number + if ($paymentTransactionDetail['payment_type_mapping']['payment_type']['pos_code'] == 'POS') { + + if (isset($paymentTransactionDetail['extraParamsArray']['inputs']['Pan'])) { + $paymentTransactionDetail['extraParamsArray']['inputs']['Pan'] = creditCardMask($paymentTransactionDetail['extraParamsArray']['inputs']['Pan']); + } elseif (isset($paymentTransactionDetail['extraParamsArray']['inputs']['pan'])) { + $paymentTransactionDetail['extraParamsArray']['inputs']['pan'] = creditCardMask($paymentTransactionDetail['extraParamsArray']['inputs']['pan']); + } elseif (isset($paymentTransactionDetail['extraParamsArray']['inputs']['cardnumber'])) { + $paymentTransactionDetail['extraParamsArray']['inputs']['cardnumber'] = creditCardMask($paymentTransactionDetail['extraParamsArray']['inputs']['cardnumber']); + } + + $this->updatePaymentTransaction($paymentTransactionDetail['id'], ['extra_params' => json_encode($paymentTransactionDetail['extraParamsArray'])]); + } + + $paymentTransactionDetail = $this->getPaymentTransactionDetail($paymentCode); + $response['data']['paymentTransactionDetail'] = $paymentTransactionDetail['data']; + + return $response; + } + + + /***/ + + public function selectPaymentTransaction($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->paymentTransactionRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function updatePaymentTransaction($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->paymentTransactionRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPaymentTypeList($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $paymentRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + ]; + + $paymentResponse = $this->paymentTypeRepository->findByCriteria($paymentRequest); + + $paymentMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + ]; + + $paymentMappingResponse = $this->propertyPaymentMappingRepository->findByCriteria($paymentMappingRequest); + $paymentMapping = collect($paymentMappingResponse); + $paymentList = collect($paymentResponse)->map(function ($value) use ($paymentMapping) { + $hasMapping = $paymentMapping->where("payment_type_id", "=", $value['id']) + ->where("status", "!=", 0) + ->first(); + return [ + 'id' => $value['id'], + 'pos_code' => $value['pos_code'], + 'bank_code' => $value['bank_code'], + 'threed_model' => $value['threed_model'], + 'name' => $value['name'], + 'installment' => $value['installment'], + 'icon' => $value['icon'], + 'status' => $value['status'], + 'bank_params' => $value['paramsArray'], + "has_setup" => isset($hasMapping) + ]; + + + })->values()->all(); + + + $response = [ + 'status' => true, + 'data' => $paymentList, + ]; + + } 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); + + } + + public function getPaymentMappingList($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $paymentMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['paymentType', 'propertyPaymentMappingInstallments'], + 'orderBy' => [ + ['field' => 'payment_type_id', 'value' => 'ASC'] + ] + ]; + + if (isset($params['property_payment_mapping_id'])) { + $paymentMappingRequest['criteria'][] = ['field' => 'id', 'condition' => '=', 'value' => $params['property_payment_mapping_id']]; + } + + $paymentMappingResponse = $this->propertyPaymentMappingRepository->findByCriteria($paymentMappingRequest); + $paymentMappingList = collect($paymentMappingResponse)->map(function ($value) { + + $fieldKeys = $value['paramsArray']; + $fieldsValues = collect($value['payment_type']['paramsArray'])->map(function ($value, $key) use ($fieldKeys) { + $return = $value; + $return['value'] = isset($fieldKeys[$key]) ? $fieldKeys[$key] : null; + return $return; + }); + + return [ + 'id' => $value['id'], + 'property_id' => $value['property_id'], + 'payment_type_id' => $value['payment_type_id'], + 'payment_type_status' => $value['payment_type']['status'], + 'currency_code' => $value['currency_code'], + 'is_default' => $value['is_default'], + 'pos_code' => $value['payment_type']['pos_code'], + 'bank_code' => $value['payment_type']['bank_code'], + 'threed_model' => $value['payment_type']['threed_model'], + 'name' => $value['payment_type']['name'], + 'payment_installment' => $value['payment_type']['installment'], + 'status' => $value['status'], + 'icon' => $value['payment_type']['icon'], + 'payment_params_array' => $fieldsValues, + 'fields' => $fieldKeys, + 'has_installments' => $value['property_payment_mapping_installments'] ? true : false, + 'installments' => $value['property_payment_mapping_installments'], + ]; + })->values(); + + $paymentMappingList = isset($params['property_payment_mapping_id']) ? $paymentMappingList->first() : $paymentMappingList->all(); + + + $response = [ + 'status' => true, + 'data' => $paymentMappingList, + ]; + + } 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); + + } + + public function createPaymentMapping($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + DB::beginTransaction(); + + $validationResult = $this->propertyPaymentMappingCreateValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $paymentType = $this->paymentTypeRepository->find($params['payment_type_id']); + $paymentMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['paymentType'] + ]; + $oldPaymentResponse = $this->propertyPaymentMappingRepository->findByCriteria($paymentMappingRequest); + $oldPayments = collect($oldPaymentResponse)->map(function ($value) { + $return = $value; + $return['pos_code'] = $value['payment_type']['pos_code']; + return $return; + }); + + $activeMappings = $oldPayments->where('status', '=', 1)->all(); + if ($activeMappings) { + $isDefault = $params['is_default']; + } else { + $isDefault = 1; + } + + + $fields = $params['fields']; + $fieldsJson = json_encode($fields); + + $insertMapping = [ + 'property_id' => $params['property_id'], + 'payment_type_id' => $params['payment_type_id'], + 'params' => $fieldsJson, + 'installment' => $paymentType['installment'] ? $paymentType['installment'] : 0, + 'currency_code' => $params['currency'], + 'is_default' => $isDefault, + 'status' => 1, + 'created_by' => fillOnUndefined($params, "user_id", 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + if ($paymentType['pos_code'] == 'POS') { + if ($params['currency'] == 'TRY') { + $hasMapping = $oldPayments->where('currency_code', '=', $params['currency']) + ->where('payment_type_id', '=', $params['payment_type_id']) + ->first(); + + if (!$hasMapping) { + $hasMapping = $oldPayments->where('currency_code', '=', $params['currency']) + ->where('pos_code', '!=', 'POS') + ->where('status', '=', 1) + ->first(); + } + } else { + $hasMapping = $oldPayments + ->where('currency_code', '=', $params['currency']) + ->where('status', '=', 1) + ->first(); + } + } else { + + $hasMapping = $oldPayments->where('currency_code', '=', $params['currency']) + ->where('status', '=', 1) + ->where('pos_code', '!=', 'POS') + ->first(); + } + + if ($hasMapping) { + throw new ApiErrorException('payment mapping error'); + } + + $insertPaymentMapping = $this->propertyPaymentMappingRepository->create($insertMapping); + + if ($insertPaymentMapping['status'] != 'success') { + throw new ApiErrorException($insertPaymentMapping['message']); + } + $insertPaymentMappingData = $insertPaymentMapping['data']; + + if ($params['is_default'] == 1) { + $updateThisIds = $oldPayments->where('is_default', '=', 1)->keyBy('id')->keys()->all(); + + $updateUseMappingData = [ + "is_default" => 0, + "updated_by" => $params['user_id'], + 'updated_at' => time() + ]; + + $updateResult = $this->propertyPaymentMappingRepository->updateWhereIn($updateThisIds, $updateUseMappingData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + + } + + public function updatePaymentMappingStatus($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + DB::beginTransaction(); + + $validationResult = $this->propertyPaymentMappingStatusUpdateValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $paymentMapping = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['paymentType'] + ]; + + $oldPaymentResponse = $this->propertyPaymentMappingRepository->findByCriteria($paymentMapping); + $oldPayments = collect($oldPaymentResponse)->map(function ($value) { + $return = $value; + $return['pos_code'] = $value['payment_type']['pos_code']; + return $return; + }); + + + $thisPaymentMapping = $oldPayments->where('id', '=', $params['property_payment_mapping_id'])->first(); + if (!$thisPaymentMapping) { + throw new Exception('api-unknown_error'); + } + $hasMapping = null; + + if ($params['set_status'] == 1) { + if ($thisPaymentMapping['pos_code'] == 'POS') { + if ($thisPaymentMapping['currency_code'] == 'TRY') { + $hasMapping = $oldPayments + ->where('currency_code', '=', $thisPaymentMapping['currency_code']) + ->where('payment_type_id', '=', $thisPaymentMapping['payment_type_id']) + ->where('id', '!=', $thisPaymentMapping['id']) + ->first(); + + if (!$hasMapping) { + $hasMapping = $oldPayments->where('currency_code', '=', $thisPaymentMapping['currency_code']) + ->where('pos_code', '!=', 'POS') + ->where('status', '=', 1) + ->first(); + } + } else { + $hasMapping = $oldPayments + ->where('currency_code', '=', $thisPaymentMapping['currency_code']) + ->where('status', '=', 1) + ->first(); + } + } else { + $hasMapping = $oldPayments->where('currency_code', '=', $thisPaymentMapping['currency_code']) + ->where('status', '=', 1) + ->first(); + } + + if ($hasMapping) { + throw new ApiErrorException('payment mapping error'); + } + + } + + + $updateMappingCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_payment_mapping_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + + $updateUseMappingData = [ + "status" => $params['set_status'], + "updated_by" => $params['user_id'], + 'updated_at' => time() + ]; + + $updateResult = $this->propertyPaymentMappingRepository->updateWhere($updateMappingCriteria, $updateUseMappingData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $thisPaymentMapping['status'] = $params['set_status']; + + $response = [ + 'status' => true, + 'data' => $thisPaymentMapping, + ]; + + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + + } + + public function setPaymentMappingDefault($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + DB::beginTransaction(); + + $validationResult = $this->propertyPaymentMappingSetDefaultValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $updateMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'is_default', 'condition' => '=', 'value' => 1], + ] + ]; + + $updateResult = $this->propertyPaymentMappingRepository->findByCriteria($updateMappingCriteria, ['id']); + $updateThisIds = collect($updateResult)->keyBy('id')->keys()->all(); + + $updateUseMappingData = [ + "is_default" => 0, + "updated_by" => $params['user_id'], + 'updated_at' => time() + ]; + + $updateResult = $this->propertyPaymentMappingRepository->updateWhereIn($updateThisIds, $updateUseMappingData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + + $updateUseMappingData = [ + "is_default" => 1, + "updated_by" => $params['user_id'], + 'updated_at' => time() + ]; + + $updateResult = $this->propertyPaymentMappingRepository->update($params['property_payment_mapping_id'], $updateUseMappingData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + + } + + public function updatePaymentMapping($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + DB::beginTransaction(); + + $validationResult = $this->propertyPaymentMappingUpdateValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $paymentTypeRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_payment_mapping_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['paymentType'], + 'firstRow' => 1 + ]; + $paymentTypeResponse = $this->propertyPaymentMappingRepository->findByCriteria($paymentTypeRequest); + if (!isset($paymentTypeResponse['payment_type']['paramsArray'])) { + throw new ApiErrorException('payment mapping error'); + } + + $getPaymentParams = $paymentTypeResponse['payment_type']['paramsArray']; + $fields = $params['fields']; + + foreach ($getPaymentParams as $key => $getPaymentParam) { + if (!isset($fields[$key])) { + throw new ApiErrorException('field error'); + } + } + + $fieldsJson = json_encode($fields); + $updateMapping = [ + 'params' => $fieldsJson, + 'is_default' => $params['is_default'], + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'updated_at' => Carbon::now()->timestamp, + ]; + + $updatePaymentMapping = $this->propertyPaymentMappingRepository->update($params['property_payment_mapping_id'], $updateMapping); + if ($updatePaymentMapping['status'] != 'success') { + throw new ApiErrorException($updatePaymentMapping['message']); + } + + if ($params['is_default'] == 1) { + $oldPaymentMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'id', 'condition' => '!=', 'value' => $params['property_payment_mapping_id']], + ], + 'with' => ['paymentType'] + ]; + $oldPaymentResponse = $this->propertyPaymentMappingRepository->findByCriteria($oldPaymentMappingRequest); + $oldPayments = collect($oldPaymentResponse) + ->where('is_default', '=', 1) + ->map(function ($value) { + $return = $value; + $return['pos_code'] = $value['payment_type']['pos_code']; + return $return; + }); + + $updateThisIds = $oldPayments->keyBy('id')->keys()->all(); + $updateUseMappingData = [ + "is_default" => 0, + "updated_by" => $params['user_id'], + 'updated_at' => time() + ]; + + $updateResult = $this->propertyPaymentMappingRepository->updateWhereIn($updateThisIds, $updateUseMappingData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + + } + + public function updateInstallments($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + DB::beginTransaction(); + + + $validationResult = $this->propertyPaymentInstallmentsUpdateValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $updateMapping = [ + 'installment' => $params['has_installments'] ? 1 : 0, + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'updated_at' => Carbon::now()->timestamp, + ]; + + $updatePaymentMapping = $this->propertyPaymentMappingRepository->update($params['property_payment_mapping_id'], $updateMapping); + if ($updatePaymentMapping['status'] != 'success') { + throw new ApiErrorException($updatePaymentMapping['message']); + } + + $paymentTypeRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_payment_mapping_id']], + ], + 'with' => ['paymentType', 'propertyPaymentMappingInstallments'], + 'firstRow' => 1 + ]; + $paymentTypeResponse = $this->propertyPaymentMappingRepository->findByCriteria($paymentTypeRequest); + + if (!isset($paymentTypeResponse['payment_type']['paramsArray'])) { + throw new ApiErrorException('payment mapping error'); + } + + + if ($paymentTypeResponse['payment_type']['installment'] != 1) { + throw new ApiErrorException('installments access error'); + } + $oldInstallments = $paymentTypeResponse['property_payment_mapping_installments']; + $oldInstallments = collect($oldInstallments); + + $installmentArray = []; + foreach ($params['installments'] as $installment) { + + $checkHasInstallment = $oldInstallments->where('installment', '=', $installment['installment'])->first(); + + if ($checkHasInstallment) { + $updateInstallmentData = [ + 'commission' => fillOnUndefinedAndEmpty($installment, 'commission', 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'updated_at' => Carbon::now()->timestamp, + ]; + + $updateInstallment = $this->propertyPaymentInstallmentRepository->update($checkHasInstallment['id'], $updateInstallmentData); + if ($updateInstallment['status'] != 'success') { + throw new ApiErrorException($updateInstallment['message']); + } + + } else { + $installmentArray[] = [ + 'property_id' => $params['property_id'], + 'property_payment_mapping_id' => $params['property_payment_mapping_id'], + 'installment' => $installment['installment'], + 'commission' => fillOnUndefinedAndEmpty($installment, 'commission', 0), + 'status' => 1, + 'created_by' => fillOnUndefined($params, "user_id", 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + } + } + + if ($installmentArray) { + $insertPaymentMapping = $this->propertyPaymentInstallmentRepository->insert($installmentArray); + if ($insertPaymentMapping['status'] != 'success') { + throw new ApiErrorException($insertPaymentMapping['message']); + } + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + + } + + public function updatePaymentInstallmentStatus($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $paymentInstallmentRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['installment_id']], + ['field' => 'property_payment_mapping_id', 'condition' => '=', 'value' => $params['property_payment_mapping_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => 1 + ]; + $installmentResponse = $this->propertyPaymentInstallmentRepository->findByCriteria($paymentInstallmentRequest); + + if (!$installmentResponse) { + throw new Exception('api-unknown_error'); + } + + $updateInstallmentData = [ + 'status' => fillOnUndefinedAndEmpty($params, 'set_status', 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'updated_at' => Carbon::now()->timestamp, + ]; + + $updateInstallment = $this->propertyPaymentInstallmentRepository->update($installmentResponse['id'], $updateInstallmentData); + if ($updateInstallment['status'] != 'success') { + throw new ApiErrorException($updateInstallment['message']); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } 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); + + } + + public function getPaymentType($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $paymentRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['payment_type_id']], + ], + 'firstRow' => 1 + ]; + + $paymentResponse = $this->paymentTypeRepository->findByCriteria($paymentRequest); + if (!$paymentResponse) { + throw new Exception('api-unknown_error'); + } + + $paymentMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'payment_type_id', 'condition' => '=', 'value' => $params['payment_type_id']], + ], + 'with' => ['paymentType', 'propertyPaymentMappingInstallments'], + 'orderBy' => [ + ['field' => 'payment_type_id', 'value' => 'ASC'] + ] + ]; + $paymentMappingResponse = $this->propertyPaymentMappingRepository->findByCriteria($paymentMappingRequest); + $paymentMappingResponse = collect($paymentMappingResponse); + + $currencyList = $this->currencyService->getCurrencyList(); + if ($currencyList['status'] != 'success') { + throw new Exception($currencyList['message']); + } + + $currencyList = collect($currencyList['data'])->map(function ($value) use ($paymentMappingResponse, $paymentResponse) { + $thisMapping = $paymentMappingResponse->where('currency_code', '=', $value['code'])->first(); + + $installments = collect(isset($thisMapping) ? $thisMapping['property_payment_mapping_installments'] : []); + $installmentArray = []; + for ($i = 1; $i <= 12; $i++) { + $installment = $installments->where('installment', '=', $i)->first(); + $installmentArray[] = [ + "installment_id" => isset($installment) ? $installment['id'] : null, + "installment" => $i, + "commission" => isset($installment) ? $installment['commission'] : null, + "has_installments" => isset($installment), + "status" => $installment['status'] ? $installment['status'] : 0, + ]; + } + + return [ + 'code' => $value['code'], + 'name' => $value['name'], + 'id' => isset($thisMapping) ? $thisMapping['id'] : null, + 'bank_params' => isset($paymentResponse) ? $paymentResponse['paramsArray'] : null, + 'language_key' => $value['language_key'], + 'fields' => isset($thisMapping) ? $thisMapping['paramsArray'] : null, + 'has_installments' => isset($thisMapping) ? $thisMapping['installment'] : 0, + 'status' => isset($thisMapping) && $thisMapping['status'] == 1 ? 1 : 0, + 'is_default' => isset($thisMapping) ? $thisMapping['is_default'] : null, + 'installments' => isset($thisMapping['installment']) && $thisMapping['installment'] ? $installmentArray : [] + ]; + })->values()->all(); + + $getPayment = [ + 'id' => $paymentResponse['id'], + 'pos_code' => $paymentResponse['pos_code'], + 'bank_code' => $paymentResponse['bank_code'], + 'threed_model' => $paymentResponse['threed_model'], + 'name' => $paymentResponse['name'], + 'installment' => $paymentResponse['installment'], + 'icon' => $paymentResponse['icon'], + 'status' => $paymentResponse['status'], + 'currencies' => $currencyList, + ]; + + $response = [ + 'status' => true, + 'data' => $getPayment, + ]; + + } 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); + + } + + public function createManualPayment($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + if ($params['payment_type_mapping_id']) { + $paymentRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '!=', 'value' => 0], + ['field' => 'id', 'condition' => '=', 'value' => $params['payment_type_mapping_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => 1 + ]; + $paymentResponse = $this->propertyPaymentMappingRepository->findByCriteria($paymentRequest); + if (!$paymentResponse) { + throw new Exception('api-unknown_error'); + } + } + + $validationResult = $this->propertyPaymentTransactionCreateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $orderId = getCodeGenerate('LNK'); + $baseAmount = fillOnUndefined($params, 'base_amount'); + $commission = fillOnUndefined($params, 'commission'); + $amount = $baseAmount + (($baseAmount * $commission) / 100); + + $paramsColumn = [ + "title" => fillOnUndefined($params, 'title'), + "description" => fillOnUndefined($params, 'description'), + "email" => fillOnUndefined($params, 'email'), + "base_amount" => $baseAmount, + "commission" => $commission, + "amount" => $amount + + ]; + + $insertData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'payment_type_mapping_id' => fillOnUndefined($params, 'payment_type_mapping_id'), + 'code' => null, + 'order_id' => $orderId, + 'transaction_type' => 'LNK', + 'bank_order_id' => null, + 'base_amount' => $amount, + 'base_currency' => fillOnUndefined($params, 'currency'), + 'exchange_rate' => 1, + 'amount' => $amount, + 'currency' => fillOnUndefined($params, 'currency'), + 'installment' => 0, + 'params' => json_encode($paramsColumn), + "status" => fillOnUndefined($params, "status", 5), + "created_by" => fillOnUndefined($params, "user_id", null), + ]; + + + $userCreateResult = $this->paymentTransactionRepository->create($insertData); + + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + + $userData['payment_link'] = Config::get('app.paymentFormLink') . $userData['order_id']; + + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } 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); + + } + + public function updateManualPayment($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $validationResult = $this->propertyPaymentTransactionUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $paymentRequest = [ + 'criteria' => [ + ['field' => 'order_id', 'condition' => '=', 'value' => $params['order_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $paymentResponse = $this->paymentTransactionRepository->findByCriteria($paymentRequest); + if (!$paymentResponse) { + throw new Exception('api-unknown_error'); + } + + $paymentTransactions = collect($paymentResponse); + + $hasSuccessTransaction = $paymentTransactions->whereNotIn('status', [0, 5])->count(); + + if ($hasSuccessTransaction) { + throw new Exception('enw-payment-payment_link-paid_payment-message'); + } + + $updateMe = $paymentTransactions->where('status', '=', 5)->first(); + $paymentTransactionId = $updateMe['id']; + + $baseAmount = fillOnUndefined($params, 'base_amount'); + $commission = fillOnUndefined($params, 'commission'); + $amount = $baseAmount + (($baseAmount * $commission) / 100); + + $paramsColumn = [ + "title" => fillOnUndefined($params, 'title'), + "description" => fillOnUndefined($params, 'description'), + "email" => fillOnUndefined($params, 'email'), + "base_amount" => $baseAmount, + "commission" => $commission, + "amount" => $amount + + ]; + + $updateData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'payment_type_mapping_id' => fillOnUndefined($params, 'payment_type_mapping_id'), + 'base_amount' => $amount, + 'base_currency' => fillOnUndefined($params, 'currency'), + 'exchange_rate' => 1, + 'amount' => $amount, + 'currency' => fillOnUndefined($params, 'currency'), + 'params' => json_encode($paramsColumn), + ]; + + + $userCreateResult = $this->paymentTransactionRepository->update($paymentTransactionId, $updateData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + + $userData['payment_link'] = Config::get('app.paymentFormLink') . $userData['order_id']; + + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } 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); + + } + + public function createManualPaymentForm($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $currencyList = $this->currencyService->getCurrencyList(); + if ($currencyList['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData['currency_list'] = $currencyList['data']; + + $paymentMappingList = $this->getPaymentMappingList($params); + if ($paymentMappingList['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $userData['payment_mapping_list'] = $paymentMappingList['data']; + + + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } 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); + + } + + public function editManualPaymentForm($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $currencyList = $this->currencyService->getCurrencyList(); + if ($currencyList['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData['currency_list'] = $currencyList['data']; + + $paymentMappingList = $this->getPaymentMappingList($params); + if ($paymentMappingList['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $userData['payment_mapping_list'] = $paymentMappingList['data']; + + + $paymentRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['payment_transaction_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $paymentResponse = $this->paymentTransactionRepository->findByCriteria($paymentRequest); + if (!$paymentResponse) { + throw new Exception('api-unknown_error'); + } + + $paymentTransactions = collect($paymentResponse); + + $hasSuccessTransaction = $paymentTransactions->whereNotIn('status', [0, 5])->count(); + + if ($hasSuccessTransaction) { + throw new Exception('api-unknown_error'); + } + + $updateMe = $paymentTransactions->where('status', '=', 5)->first(); + + $userData['manual_payment_data'] = $updateMe; + + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } 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); + + } + + public function getManualPayment($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $paymentRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['payment_transaction_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['relatedTransactions'], + 'firstRow' => 1 + ]; + $paymentResponse = $this->paymentTransactionRepository->findByCriteria($paymentRequest); + if (!$paymentResponse) { + throw new ApiErrorException('api-unknown_error'); + } + + $paymentResponse['payment_link'] = $paymentResponse['status'] == 5 && $paymentResponse['transaction_type'] != 'BKG' ? Config::get('app.paymentFormLink') . $paymentResponse['order_id'] : null; + + $response = [ + 'status' => true, + 'data' => $paymentResponse, + ]; + + } 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); + + } + +} diff --git a/app/Core/Service/PropertyPersonPricingPolicyService.php b/app/Core/Service/PropertyPersonPricingPolicyService.php new file mode 100644 index 0000000..3884ad4 --- /dev/null +++ b/app/Core/Service/PropertyPersonPricingPolicyService.php @@ -0,0 +1,902 @@ +propertyPricingPolicyAdultRepository = $propertyPricingPolicyAdultRepository; + $this->propertyPricingPolicyChildRepository = $propertyPricingPolicyChildRepository; + $this->propertyChannelRoomRatePricingPolicyAdultMappingRepository = $propertyChannelRoomRatePricingPolicyAdultMappingRepository; + $this->updateRoomRateChannelPersonPricingAdultPolicyValidator = $updateRoomRateChannelPersonPricingAdultPolicyValidator; + $this->updateRoomRateChannelPersonPricingChildPolicyValidator = $updateRoomRateChannelPersonPricingChildPolicyValidator; + $this->propertyChannelRoomRatePricingPolicyChildMappingRepository = $propertyChannelRoomRatePricingPolicyChildMappingRepository; + $this->propertyRoomRepository = $propertyRoomRepository; + $this->propertyPricingPolicyAdultAddValidator = $propertyPricingPolicyAdultAddValidator; + $this->propertyPricingPolicyAdultUpdateValidator = $propertyPricingPolicyUpdateValidator; + $this->propertyPricingPolicyChildAddValidator = $propertyPricingPolicyChildAddValidator; + $this->propertyPricingPolicyChildUpdateValidator = $propertyPricingPolicyChildUpdateValidator; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + + + } + + + public function getPropertyPersonPricingPolicyList($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $propertyId = fillOnUndefined($params, 'property_id'); + $propertyPersonPricingPolicyRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ] + ]; + $propertyPersonPricingAdultPolicy = $this->propertyPricingPolicyAdultRepository->findByCriteria($propertyPersonPricingPolicyRequest, ['id', 'property_id', 'name', 'adult_action_type', 'adult', "action_type", "type", "value", "status"]); + $propertyPersonPricingAdultPolicy = $propertyPersonPricingAdultPolicy ? $propertyPersonPricingAdultPolicy : []; + + + $propertyPersonPricingChildPolicy = $this->propertyPricingPolicyChildRepository->findByCriteria($propertyPersonPricingPolicyRequest, ['id', 'property_id', 'name', "adult", "child_order", "child_age_start", "child_age_end", "type", "value", "status"]); + $propertyPersonPricingChildPolicy = $propertyPersonPricingChildPolicy ? $propertyPersonPricingChildPolicy : []; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ["person_pricing_adult_policy" => $propertyPersonPricingAdultPolicy, "person_pricing_child_policy" => $propertyPersonPricingChildPolicy]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function createPropertyPersonPricingAdultPolicy($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $validate = [ + 'name' => fillOnUndefinedAndEmpty($params, 'name', null), + 'adult_action_type' => fillOnUndefined($params, 'adult_action_type'), + 'adult' => fillOnUndefined($params, 'adult'), + 'action_type' => fillOnUndefinedAndEmpty($params, 'action_type', null), + 'type' => fillOnUndefinedAndEmpty($params, 'type', null), + 'value' => fillOnUndefinedAndEmpty($params, 'value', null), + 'property_id' => fillOnUndefinedAndEmpty($params, 'property_id', null), + 'reservation_start_date' => fillOnUndefinedAndEmpty($params, 'reservation_start_date', null), + 'reservation_end_date' => fillOnUndefinedAndEmpty($params, 'reservation_end_date', null), + ]; + + $validationResult = $this->propertyPricingPolicyAdultAddValidator->validate($validate); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $createData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => $validate['name'], + 'adult_action_type' => $validate['adult_action_type'], + 'adult' => $validate['adult'], + 'action_type' => $validate['action_type'], + 'type' => $validate['type'], + 'value' => $validate['value'], + 'reservation_start_date' => $validate['reservation_start_date'], + 'reservation_end_date' => $validate['reservation_end_date'], + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $createResult = $this->propertyPricingPolicyAdultRepository->create($createData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResult['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); + } + + public function updatePropertyPersonPricingAdultPolicy($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $validate = [ + 'name' => fillOnUndefinedAndEmpty($params, 'name', null), + 'adult_action_type' => fillOnUndefined($params, 'adult_action_type'), + 'adult' => fillOnUndefined($params, 'adult'), + 'pricing_policy_id' => fillOnUndefinedAndEmpty($params, 'pricing_policy_id', null), + 'property_id' => fillOnUndefinedAndEmpty($params, 'property_id', null), + 'action_type' => fillOnUndefinedAndEmpty($params, 'action_type', null), + 'type' => fillOnUndefinedAndEmpty($params, 'type', null), + 'value' => fillOnUndefinedAndEmpty($params, 'value', null), + 'reservation_start_date' => fillOnUndefinedAndEmpty($params, 'reservation_start_date', null), + 'reservation_end_date' => fillOnUndefinedAndEmpty($params, 'reservation_end_date', null), + ]; + + $validationResult = $this->propertyPricingPolicyAdultUpdateValidator->validate($validate); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $updateData = + [ + 'name' => $validate['name'], + 'adult_action_type' => $validate['adult_action_type'], + 'adult' => $validate['adult'], + 'action_type' => $validate['action_type'], + 'type' => $validate['type'], + 'value' => $validate['value'], + 'reservation_start_date' => $validate['reservation_start_date'], + 'reservation_end_date' => $validate['reservation_end_date'], + "status" => fillOnUndefined($params, "status", 1), + "updated_by" => fillOnUndefined($params, "user_id"), + "updated_at" => time(), + ]; + + $updateResult = $this->propertyPricingPolicyAdultRepository->update($validate['pricing_policy_id'], $updateData); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $updateResult['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); + } + + public function deletePropertyPersonPricingAdultPolicy($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $adultCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['pricing_policy_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['channelRoomRatePricingPolicyAdultMapping'], + 'firstRow' => true + ]; + + $propertyPersonPricingAdultPolicy = $this->propertyPricingPolicyAdultRepository->findByCriteria($adultCriteria); + + if (empty($propertyPersonPricingAdultPolicy)) { + throw new ApiErrorException('Adult policy data not found'); + } + + DB::beginTransaction(); + + $channelRoomRatePricingPolicyAdultMappingIds = collect($propertyPersonPricingAdultPolicy['channel_room_rate_pricing_policy_adult_mapping'])->pluck('id')->toArray(); + if ($channelRoomRatePricingPolicyAdultMappingIds) { + $deleteChannelRoomRatePricingPolicyAdultMapping = $this->propertyChannelRoomRatePricingPolicyAdultMappingRepository->destroy($channelRoomRatePricingPolicyAdultMappingIds); + } + + $deletePropertyPricingPolicyAdult = $this->propertyPricingPolicyAdultRepository->destroy([$propertyPersonPricingAdultPolicy['id']]); + + if ($deletePropertyPricingPolicyAdult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $deletePropertyPricingPolicyAdult['data']]; + + DB::commit(); + + } catch (ApiErrorException $e) { + DB::rollBack(); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function createPropertyPersonPricingChildPolicy($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + + $validationResult = $this->propertyPricingPolicyChildAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $createData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'adult' => fillOnUndefined($params, 'adult'), + 'child_order' => fillOnUndefined($params, 'child_order'), + 'child_age_start' => fillOnUndefined($params, 'child_age_start'), + 'child_age_end' => fillOnUndefined($params, 'child_age_end'), + 'type' => fillOnUndefined($params, 'type'), + 'value' => fillOnUndefined($params, 'value'), + 'reservation_start_date' => fillOnUndefined($params, 'reservation_start_date'), + 'reservation_end_date' => fillOnUndefined($params, 'reservation_end_date'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $createResult = $this->propertyPricingPolicyChildRepository->create($createData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResult['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); + } + + public function updatePropertyPersonPricingChildPolicy($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $validationResult = $this->propertyPricingPolicyChildUpdateValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'room_rate_channel_mapping_id', 'condition' => '=', 'value' => $params['room_rate_channel_mapping_id']], + ], + 'with' => ['propertyPricingPolicyChild'] + ]; + + $propertyChannelRoomRatePersonPricingPolicyMapping = $this->propertyChannelRoomRatePricingPolicyChildMappingRepository->findByCriteria($criteria); + $propertyChannelRoomRatePersonPricingPolicyMapping = $propertyChannelRoomRatePersonPricingPolicyMapping ? $propertyChannelRoomRatePersonPricingPolicyMapping : []; + + if (!empty($propertyChannelRoomRatePersonPricingPolicyMapping)) { + + $oldDataMappingCollect = collect($propertyChannelRoomRatePersonPricingPolicyMapping)->map(function ($value) { + return [ + 'id' => $value['property_pricing_policy_child']['id'], + 'name' => $value['property_pricing_policy_child']['name'], + 'adult' => $value['property_pricing_policy_child']['adult'], + 'child_order' => $value['property_pricing_policy_child']['child_order'], + 'child_age_start' => $value['property_pricing_policy_child']['child_age_start'], + 'child_age_end' => $value['property_pricing_policy_child']['child_age_end'], + 'type' => $value['property_pricing_policy_child']['type'], + 'value' => $value['property_pricing_policy_child']['value'], + 'reservation_start_date' => $value['property_pricing_policy_child']['reservation_start_date'], + 'reservation_end_date' => $value['property_pricing_policy_child']['reservation_end_date'], + ]; + }); + + for ($age = $params['child_age_start']; $age <= $params['child_age_end']; $age++) { + $checkOldData = $oldDataMappingCollect->where('adult', '=', $params['adult']) + ->where('child_age_start', '<=', $age) + ->where('child_age_end', '>', $age) + ->where('child_order', '=', $params['child_order']) + ->where('reservation_start_date', $params['reservation_start_date']) + ->where('reservation_end_date', $params['reservation_end_date']) + ->where('id', '!=', $params['pricing_policy_id']) + ->first(); + if ($checkOldData) { + throw new ApiErrorException('This mapping added before'); + } + } + + } + $updateData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'adult' => fillOnUndefined($params, 'adult'), + 'child_order' => fillOnUndefined($params, 'child_order'), + 'child_age_start' => fillOnUndefined($params, 'child_age_start'), + 'child_age_end' => fillOnUndefined($params, 'child_age_end'), + 'type' => fillOnUndefined($params, 'type'), + 'value' => fillOnUndefined($params, 'value'), + 'reservation_start_date' => fillOnUndefined($params, 'reservation_start_date'), + 'reservation_end_date' => fillOnUndefined($params, 'reservation_end_date'), + "status" => fillOnUndefined($params, "status", 1), + "updated_by" => fillOnUndefined($params, "user_id"), + "updated_at" => time(), + ]; + + $updateResult = $this->propertyPricingPolicyChildRepository->update($params['pricing_policy_id'], $updateData); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $updateResult['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); + } + + public function deletePropertyPersonPricingChildPolicy($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $adultCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['pricing_policy_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['channelRoomRatePricingPolicyChildMapping'], + 'firstRow' => true + ]; + + $propertyPersonPricingChildPolicy = $this->propertyPricingPolicyChildRepository->findByCriteria($adultCriteria); + + if (empty($propertyPersonPricingChildPolicy)) { + throw new ApiErrorException('Child policy data not found'); + } + + DB::beginTransaction(); + + $channelRoomRatePricingPolicyChildMappingIds = collect($propertyPersonPricingChildPolicy['channel_room_rate_pricing_policy_child_mapping'])->pluck('id')->toArray(); + if ($channelRoomRatePricingPolicyChildMappingIds) { + $deleteChannelRoomRatePricingPolicyAdultMapping = $this->propertyChannelRoomRatePricingPolicyChildMappingRepository->destroy($channelRoomRatePricingPolicyChildMappingIds); + } + + $deletePropertyPricingPolicyChild = $this->propertyPricingPolicyChildRepository->destroy([$propertyPersonPricingChildPolicy['id']]); + + if ($deletePropertyPricingPolicyChild['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $deletePropertyPricingPolicyChild['data']]; + + DB::commit(); + + } catch (ApiErrorException $e) { + DB::rollBack(); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function updateRoomRatePricingAdultPolicy($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + $validationResult = $this->updateRoomRateChannelPersonPricingAdultPolicyValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'room_rate_channel_mapping_id', 'condition' => '=', 'value' => $params['room_rate_channel_mapping_id']], + ], + 'with' => ['propertyPricingPolicyAdult'] + ]; + + $propertyChannelRoomRatePersonPricingPolicyMapping = $this->propertyChannelRoomRatePricingPolicyAdultMappingRepository->findByCriteria($criteria); + $propertyChannelRoomRatePersonPricingPolicyMapping = $propertyChannelRoomRatePersonPricingPolicyMapping ? $propertyChannelRoomRatePersonPricingPolicyMapping : []; + // $deleteThis = collect($propertyChannelRoomRatePersonPricingPolicyMapping)->keyBy('id')->keys()->toArray(); + $insertData = []; + if ($params['is_selected'] == true) { + + /*foreach ($propertyChannelRoomRatePersonPricingPolicyMapping as $adultMapping) { + $property_pricing_policy_adult = fillOnUndefined($adultMapping, 'property_pricing_policy_adult'); + $adultCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['pricing_policy_adult_id']], + ] + ]; + + $propertyPersonPricingAdultPolicy = $this->propertyPricingPolicyAdultRepository->findByCriteria($adultCriteria, ['id', 'property_id', 'name', 'adult_action_type', 'adult', "action_type", "type", "value", 'reservation_start_date', 'reservation_end_date', "status"]); + if ( + $property_pricing_policy_adult['adult_action_type'] == $propertyPersonPricingAdultPolicy['adult_action_type'] && + $property_pricing_policy_adult['adult'] == $propertyPersonPricingAdultPolicy['adult'] + ) { + + throw new ApiErrorException('enw-person_pricing_one_selected_error'); + } + }*/ + + $insertData[] = [ + 'property_id' => $params['property_id'], + 'pricing_policy_adult_id' => $params['pricing_policy_adult_id'], + 'room_rate_channel_mapping_id' => $params['room_rate_channel_mapping_id'], + 'status' => 1, + 'created_by' => fillOnUndefined($params, 'user_id'), + 'updated_by' => fillOnUndefined($params, 'user_id'), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + if ($insertData) { + $createResult = $this->propertyChannelRoomRatePricingPolicyAdultMappingRepository->insert($insertData); + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } else { + + $criteria = [ + 'criteria' => [ + ['field' => 'pricing_policy_adult_id', 'condition' => '=', 'value' => $params['pricing_policy_adult_id']], + ['field' => 'room_rate_channel_mapping_id', 'condition' => '=', 'value' => $params['room_rate_channel_mapping_id']], + ], + 'firstRow' => true + ]; + + $propertyChannelRoomRatePersonPricingPolicyMapping = $this->propertyChannelRoomRatePricingPolicyAdultMappingRepository->findByCriteria($criteria); + + if ($propertyChannelRoomRatePersonPricingPolicyMapping['id']) { + $deleteThisArray = $this->propertyChannelRoomRatePricingPolicyAdultMappingRepository->destroy([$propertyChannelRoomRatePersonPricingPolicyMapping['id']]); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + db::rollBack(); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateRoomRatePricingChildPolicy($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + $validationResult = $this->updateRoomRateChannelPersonPricingChildPolicyValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'room_rate_channel_mapping_id', 'condition' => '=', 'value' => $params['room_rate_channel_mapping_id']], + ], + 'with' => ['propertyPricingPolicyChild'] + ]; + + $propertyChannelRoomRatePersonPricingPolicyMapping = $this->propertyChannelRoomRatePricingPolicyChildMappingRepository->findByCriteria($criteria); + $propertyChannelRoomRatePersonPricingPolicyMapping = $propertyChannelRoomRatePersonPricingPolicyMapping ? $propertyChannelRoomRatePersonPricingPolicyMapping : []; + + if ($params['is_selected'] == true) { + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'id', 'condition' => '=', 'value' => $params['pricing_policy_child_id']], + ], + 'firstRow' => 1 + ]; + + $addPricingPolicy = $this->propertyPricingPolicyChildRepository->findByCriteria($criteria); + if (!$addPricingPolicy) { + throw new ApiErrorException('Child data not found'); + } + + $oldDataMappingCollect = collect($propertyChannelRoomRatePersonPricingPolicyMapping)->map(function ($value) { + return [ + 'id' => $value['property_pricing_policy_child']['id'], + 'name' => $value['property_pricing_policy_child']['name'], + 'adult' => $value['property_pricing_policy_child']['adult'], + 'child_order' => $value['property_pricing_policy_child']['child_order'], + 'child_age_start' => $value['property_pricing_policy_child']['child_age_start'], + 'child_age_end' => $value['property_pricing_policy_child']['child_age_end'], + 'type' => $value['property_pricing_policy_child']['type'], + 'value' => $value['property_pricing_policy_child']['value'], + ]; + }); + + for ($age = $addPricingPolicy['child_age_start']; $age <= $addPricingPolicy['child_age_end']; $age++) { + /*$checkOldData = $oldDataMappingCollect->where('adult', '=', $addPricingPolicy['adult']) + ->where('child_age_start', '<=', $age) + ->where('child_age_end', '>', $age) + ->where('child_order', '=', $addPricingPolicy['child_order']) + ->first(); + if ($checkOldData) { + throw new ApiErrorException('This mapping added before'); + }*/ + } + + $insertData[] = [ + 'property_id' => $params['property_id'], + 'pricing_policy_child_id' => $params['pricing_policy_child_id'], + 'room_rate_channel_mapping_id' => $params['room_rate_channel_mapping_id'], + 'status' => 1, + 'created_by' => fillOnUndefined($params, 'user_id'), + 'updated_by' => fillOnUndefined($params, 'user_id'), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + if ($insertData) { + $createResult = $this->propertyChannelRoomRatePricingPolicyChildMappingRepository->insert($insertData); + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + + if ($params['is_selected'] == false) { + $deleteThis = collect($propertyChannelRoomRatePersonPricingPolicyMapping) + ->where('pricing_policy_child_id', '=', $params['pricing_policy_child_id']) + ->keyBy('id')->keys()->toArray(); + if ($deleteThis) { + $deleteThisArray = $this->propertyChannelRoomRatePricingPolicyChildMappingRepository->destroy($deleteThis); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + db::rollBack(); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getRoomRateChannelPersonPricingPolicy($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyRoomType', 'propertyRoomRateMapping.propertyRoomRateChannel.propertyRoomRateChannelPricingAdultPolicy', 'propertyRoomRateMapping.propertyRoomRateChannel.propertyRoomRateChannelPricingChildPolicy', 'propertyRoomRateMapping.propertyRoomRate'] + ]; + + $roomData = $this->propertyRoomRepository->findByCriteria($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'exclude_occupancy', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number']); + + + $propertyPricingAdultPolicyRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $propertyPricingAdultPolicy = $this->propertyPricingPolicyAdultRepository->findByCriteria($propertyPricingAdultPolicyRequest, ['id', 'property_id', 'name', 'adult_action_type', 'adult', "action_type", "type", 'reservation_start_date', 'reservation_end_date', "value"]); + $propertyPricingAdultPolicy = $propertyPricingAdultPolicy ? $propertyPricingAdultPolicy : []; + + array_multisort( + array_column($propertyPricingAdultPolicy, 'adult'), SORT_ASC, + array_column($propertyPricingAdultPolicy, 'adult_action_type'), SORT_ASC, + $propertyPricingAdultPolicy + ); + + $propertyPricingChildPolicyRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $propertyPricingChildPolicy = $this->propertyPricingPolicyChildRepository->findByCriteria($propertyPricingChildPolicyRequest, ['id', 'property_id', 'name', 'adult', 'child_order', 'child_age_start', 'child_age_end', 'reservation_start_date', 'reservation_end_date', 'type', 'value']); + $propertyPricingChildPolicy = $propertyPricingChildPolicy ? $propertyPricingChildPolicy : []; + + + $return = []; + + foreach ($roomData as $room) { + + if ($room['property_room_rate_mapping']) { + $item = $room; + $item['exclude_occupancy'] = json_decode($room['exclude_occupancy']); + $roomRateMappings = $room['property_room_rate_mapping']; + $mapping = []; + $addedMapping = 0; + foreach ($roomRateMappings as $roomRateMapping) { + $propertyRoomRateChannel = null; + if (isset($roomRateMapping['property_room_rate_channel'])) { + $propertyRoomRateChannel = collect($roomRateMapping['property_room_rate_channel']) + ->where('channel_id', '=', $params['channel_id']) + ->where('room_rate_mapping_id', '=', $roomRateMapping['id']) + ->first(); + } + + $mappingStatus = false; + $propertyPersonPricingAdultPolicyArray = []; + $propertyPersonPricingChildPolicyArray = []; + + if ($propertyRoomRateChannel) { + if ($propertyRoomRateChannel['status'] == 1) { + $mappingStatus = true; + + foreach ($propertyPricingAdultPolicy as $adultPolicy) { + $propertyRoomRateChannelAdultPricingPolicy = []; + if (isset($propertyRoomRateChannel['property_room_rate_channel_pricing_adult_policy'])) { + + $propertyRoomRateChannelAdultPricingPolicy = collect($propertyRoomRateChannel['property_room_rate_channel_pricing_adult_policy']) + ->where('pricing_policy_adult_id', '=', $adultPolicy['id']) + ->where('room_rate_channel_mapping_id', '=', $propertyRoomRateChannel['id']) + ->values()->toArray(); + } + + $propertyPersonPricingAdultPolicyArray[] = [ + 'id' => $adultPolicy['id'], + 'property_id' => $adultPolicy['property_id'], + 'name' => $adultPolicy['name'], + 'adult_action_type' => $adultPolicy['adult_action_type'], + 'adult' => $adultPolicy['adult'], + 'action_type' => $adultPolicy['action_type'], + 'type' => $adultPolicy['type'], + 'value' => $adultPolicy['value'], + 'reservation_start_date' => $adultPolicy['reservation_start_date'], + 'reservation_end_date' => $adultPolicy['reservation_end_date'], + 'pricing_adult_policy_is_selected' => $propertyRoomRateChannelAdultPricingPolicy ? true : false, + ]; + + } + + + foreach ($propertyPricingChildPolicy as $childPolicy) { + $propertyRoomRateChannelChildPricingPolicy = []; + if (isset($propertyRoomRateChannel['property_room_rate_channel_pricing_child_policy'])) { + $propertyRoomRateChannelChildPricingPolicy = collect($propertyRoomRateChannel['property_room_rate_channel_pricing_child_policy']) + ->where('pricing_policy_child_id', '=', $childPolicy['id']) + ->where('room_rate_channel_mapping_id', '=', $propertyRoomRateChannel['id']) + ->values()->toArray(); + } + + $propertyPersonPricingChildPolicyArray[] = [ + 'id' => $childPolicy['id'], + 'property_id' => $childPolicy['property_id'], + 'name' => $childPolicy['name'], + 'adult' => $childPolicy['adult'], + 'child_order' => $childPolicy['child_order'], + 'child_age_start' => $childPolicy['child_age_start'], + 'child_age_end' => $childPolicy['child_age_end'], + 'reservation_start_date' => $childPolicy['reservation_start_date'], + 'reservation_end_date' => $childPolicy['reservation_end_date'], + 'type' => $childPolicy['type'], + 'value' => $childPolicy['value'], + 'pricing_child_policy_is_selected' => $propertyRoomRateChannelChildPricingPolicy ? true : false, + ]; + } + + array_multisort(array_column($propertyPersonPricingChildPolicyArray, 'adult'), SORT_ASC, + array_column($propertyPersonPricingChildPolicyArray, 'child_order'), SORT_ASC, + $propertyPersonPricingChildPolicyArray); + } + } + + + $roomRateMapping['name'] = $roomRateMapping['property_room_rate']['name']; + $roomRateMapping['is_selected'] = $mappingStatus; + $roomRateMapping['has_date'] = $propertyRoomRateChannel['has_date'] == 0 ? false : true; + $roomRateMapping['end_date'] = $propertyRoomRateChannel['end_date']; + $roomRateMapping['start_date'] = $propertyRoomRateChannel['start_date']; + $roomRateMapping['room_rate_channel_mapping_id'] = $propertyRoomRateChannel['id']; + $roomRateMapping['room_rate_channel_adult_pricing_policy'] = $propertyPersonPricingAdultPolicyArray; + $roomRateMapping['room_rate_channel_child_pricing_policy'] = $propertyPersonPricingChildPolicyArray; + + + unset($roomRateMapping['property_room_rate']); + unset($roomRateMapping['property_room_rate_channel']); + if ($mappingStatus) { + $addedMapping++; + $mapping[] = $roomRateMapping; + } + } + $item['property_room_rate_mapping'] = $mapping; + if ($addedMapping > 0) { + $return[] = $item; + } + } + + } + + $collection = collect($return); + $sorted = $collection->sortBy('id')->values()->all(); + + $response = [ + 'status' => true, + 'data' => $sorted, + ]; + + } 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); + } + + public function checkChannelRoomPricingType($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = + [ + "criteria" => + [ + ["field" => "property_id", "condition" => "=", "value" => $params['property_id']], + ["field" => "channel_id", "condition" => "=", "value" => $params['channel_id']], + ], + 'with' => ['channelRoomPricingType'], + 'firstRow' => true + ]; + $mapping = $this->propertyChannelMappingRepository->findByCriteria($criteria); + if (!$mapping) { + throw new ApiErrorException('Property Channel Setup not found '); + } + + if ($mapping['channel_room_pricing_type']['code'] != 'PRS') { + throw new ApiErrorException('Property Channel Room Pricing Type is not Per Pricing'); + } + + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } 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); + } + +} diff --git a/app/Core/Service/PropertyPhotoService/PropertyPhotoCategoryService.php b/app/Core/Service/PropertyPhotoService/PropertyPhotoCategoryService.php new file mode 100644 index 0000000..54241e3 --- /dev/null +++ b/app/Core/Service/PropertyPhotoService/PropertyPhotoCategoryService.php @@ -0,0 +1,105 @@ +propertyPhotoCategoryRepository = $propertyPhotoCategoryRepository; + + } + + + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyPhotoCategoryRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + + + public function getPropertyPhotoCategoryList($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + // get all photo category types + $photoCategoryRequest = [ + 'with' => [] + ]; + $photoCategoryResponse = $this->select($photoCategoryRequest); + + if($photoCategoryResponse['status'] == false){ + throw new ApiErrorException(lang('Category Data Not Loaded')); + } + $photoCategoryCollection = collect($photoCategoryResponse['data']); + foreach ($photoCategoryCollection as $photoCategory){ + + + + // Get Locale data + $nameLocale = []; + + + $photoCategoryItem['id'] = $photoCategory['id']; + $photoCategoryItem['category_name'] = $photoCategory['category_name']; + + $return[] = $photoCategoryItem ; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['photo_category_list' => $return]]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + + + +} diff --git a/app/Core/Service/PropertyPhotoService/PropertyPhotoService.php b/app/Core/Service/PropertyPhotoService/PropertyPhotoService.php new file mode 100644 index 0000000..32748ed --- /dev/null +++ b/app/Core/Service/PropertyPhotoService/PropertyPhotoService.php @@ -0,0 +1,1212 @@ +propertyPhotoRepository = $propertyPhotoRepository; + $this->photoGoogleLabelRepository = $photoGoogleLabelRepository ; + $this->googleVisionLabelService = $googleVisionLabelService; + $this->propertyRoomPhotoMappingService = $propertyRoomPhotoMappingService; + $this->propertyRoomService = $propertyRoomService ; + $this->offerPhotoMappingService = $offerPhotoMappingService ; + $this->propertyWebPhotoMappingService = $propertyWebPhotoMappingService; + $this->propertyPhotoValidator = $propertyPhotoValidator ; + $this->propertyPlaceService = $propertyPlaceService ; + $this->propertyPlacePhotoMappingRepository = $propertyPlacePhotoMappingRepository ; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyPhotoRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function updateWhere($criteria, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyPhotoRepository->updateWhere($criteria, $param); + + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyPhotos($params) + { + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + try + { + $propertyId = $params['property_id']; + + $getPropertyPhotoCriteria = + [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => + [ + ["field" => "photo_order", "value" => "ASC"] + ], + ]; + + $customIds = fillOnUndefined($params, 'custom', []); + if(!empty($customIds)) { + $getPropertyPhotoCriteria['whereIn'] = [ + ["field" => "id", "value" => $customIds] + ]; + } + + $getPropertyPhotos = $this->propertyPhotoRepository->findByCriteria($getPropertyPhotoCriteria); + $getPropertyPhotos = $getPropertyPhotos ? $getPropertyPhotos : []; + + + $filteredList = []; + foreach ($getPropertyPhotos as $key => $getPropertyPhoto) + { + $photoUrlFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_1024x768.{$getPropertyPhoto['file_ext']}"; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_200x200.{$getPropertyPhoto['file_ext']}"; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_1024x768.{$getPropertyPhoto['file_ext']}"; + }else { + $photoUrlFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_medium.{$getPropertyPhoto['file_ext']}"; + } + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_200x200.{$getPropertyPhoto['file_ext']}"; + }else{ + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_thumbnail.{$getPropertyPhoto['file_ext']}"; + } + + $filteredList[] = + [ + 'id' => $getPropertyPhoto['id'], + 'property_id' => $getPropertyPhoto['property_id'], + 'property_photo_category_id' => $getPropertyPhoto['property_photo_category_id'], + 'photo_url' => $photoUrlFilePath, + 'photo_url_thump' => $photoUrlThumbFilePath, + "photo_name" => $getPropertyPhoto["photo_name"], + "photo_rank" => $getPropertyPhoto["photo_rank"], + "file_size" => $getPropertyPhoto["file_size"], + "file_ext" => $getPropertyPhoto["file_ext"], + "photo_resolution" => $getPropertyPhoto["photo_resolution"], + "photo_order" => $getPropertyPhoto["photo_order"], + "is_default" => $getPropertyPhoto["is_default"], + "is_compatible_with_myweb_slider" => $getPropertyPhoto["is_compatible_with_myweb_slider"], + "is_temp" => $getPropertyPhoto["is_temp"], + "status" => $getPropertyPhoto["status"], + ]; + } + + $response = ['status' => 1, 'statusCode' => 200 ,'message' => '', 'data' => $filteredList]; + + }catch (ApiErrorException $e){ + $response['message'] = $e->getMessage(); + $response['statusCode'] = 204; + }catch (Exception $e){ + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + return output($response); + } + + public function matchGoogleLabelPhotoCategory($params) + { + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + try + { + + $collectLabels = collect($params); + + $descriptions = $collectLabels->keyBy('description')->keys()->toArray(); + $labelCriteria = [ + 'whereIn' => [ + ["field" => "label", "value" => $descriptions] + ], + 'with' => ['photoCategoryLabelMapping.photoCategory'] + ]; + $categoryList = $this->photoGoogleLabelRepository->findByCriteria($labelCriteria); + + $dbLabels = []; + $ratedCategories = []; + foreach ($categoryList as $category){ + $label = $category["label"]; + $categoryId = $category["photo_category_label_mapping"]["photo_category"]["id"]; + $categoryName = $category["photo_category_label_mapping"]["photo_category"]["category_name"]; + + //$dbLabels[$label][$categoryId]["category_name"] = $categoryName; + + $rate = $collectLabels->where('description', '=', $label)->first(); + + $ratedCategories[$categoryId]["category_id"] = $categoryId; + $ratedCategories[$categoryId]["category_name"] = $categoryName; + if(!isset($ratedCategories[$categoryId]["rate"])){ + $ratedCategories[$categoryId]["rate"] = 0; + } + else{ + $ratedCategories[$categoryId]["rate"] += $rate['score']; + } + + } + + $winner = collect($ratedCategories)->sortByDesc('rate')->first(); + + $response = ['status' => 1, 'statusCode' => 200 ,'message' => '', 'data' => $winner]; + + }catch (ApiErrorException $e){ + $response['message'] = $e->getMessage(); + $response['statusCode'] = 204; + }catch (Exception $e){ + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + return output($response); + } + + public function insertPropertyPhoto($param) + { + return $this->propertyPhotoRepository->create($param); + } + + public function setPhotoOrder($params){ + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + try + { + foreach ($params['set_photo_order'] as $orderList){ + $updateData = [ + 'photo_order' => $orderList['photo_order'], + 'updated_at' => time() , + ]; + $updateCriteria = [ + 'criteria' => [ + ['field' => 'id' , 'condition' => '=', 'value' => $orderList['photo_id'] ], + ['field' => 'property_id' , 'condition' => '=', 'value' => $params['property_id'] ], + ] + ]; + $updateStatus = $this->updateWhere($updateCriteria, $updateData); + if($updateStatus['status'] != 'success'){ + throw new Exception($updateStatus['message']); + } + $response = ['status' => 1, 'statusCode' => 200 ,'message' => '', 'data' => []]; + } + }catch (ApiErrorException $e){ + $response['message'] = $e->getMessage(); + $response['statusCode'] = 204; + }catch (Exception $e){ + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + return output($response); + + + } + + public function setPhotoCategory($params){ + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + try + { + foreach ($params['set_photo_category'] as $photoList){ + $updateData = [ + 'property_photo_category_id' => $photoList['photo_category_id'], + 'updated_at' => time() , + ]; + $updateCriteria = [ + 'criteria' => [ + ['field' => 'id' , 'condition' => '=', 'value' => $photoList['photo_id'] ], + ['field' => 'property_id' , 'condition' => '=', 'value' => $params['property_id'] ], + ] + ]; + + $updateStatus = $this->updateWhere($updateCriteria, $updateData); + + if($updateStatus['status'] != 'success'){ + throw new ApiErrorException(lang('User data is not updated.')); + } + } + $response = ['status' => 1, 'statusCode' => 200 ,'message' => '', 'data' => []]; + }catch (ApiErrorException $e){ + $response['message'] = $e->getMessage(); + $response['statusCode'] = 204; + }catch (Exception $e){ + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + return output($response); + + + } + + public function update($id, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyPhotoRepository->update($id,$param); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + // todo: query performance should be corrected + public function setDefaultPhoto($params) + { + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + try + { + + $photo_id = $params['photo_id']; + $propertyId = $params['property_id']; + + $requestDataCriteria = + [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'id', 'condition' => '=', 'value' => $photo_id], + ], + ]; + + $requestDataCheck = $this->propertyPhotoRepository->findByCriteria($requestDataCriteria,['id']); + + if (empty($requestDataCheck)) { + throw new ApiErrorException(lang('PropertyPhoto data not found')); + } + + $propertyPhotoCriteria = + [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'is_default', 'condition' => '=', 'value' => 1] + ], + ]; + + $getPropertyPhotoData = $this->propertyPhotoRepository->findByCriteria($propertyPhotoCriteria,['id','is_default']); + + if (!empty($getPropertyPhotoData)) { + foreach ($getPropertyPhotoData as $photoData ) { + $updatedPhotoisDefault = $this->update($photoData['id'],['is_default' => 0]); + } + + if ($updatedPhotoisDefault['status'] == false) { + throw new ApiErrorException(lang('PropertyPhoto "is_default fields" can not updated')); + } + } + + $PhotoCriteria = + [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'id', 'condition' => '=', 'value' => $photo_id], + ['field' => 'is_default', 'condition' => '=', 'value' => 0], + ], + ]; + + $getPhotoData = $this->propertyPhotoRepository->findByCriteria($PhotoCriteria); + + if (!empty($getPhotoData)) { + $setPropertPhotoDefault = $this->update($photo_id,['is_default' => 1]); + } + + if ($setPropertPhotoDefault['status'] == false) { + throw new ApiErrorException(lang('PropertyPhoto "is_default" can not updated')); + } + + $response = ['status' => 1, 'statusCode' => 200 ,'message' => '', 'data' => '']; + + }catch (ApiErrorException $e){ + $response['message'] = $e->getMessage(); + + }catch (Exception $e){ + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + $response['message'] = $e->getMessage(); + Log::error($message); + } + return output($response); + } + + public function publishPhotos($params) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + + try { + $photos = $params['photos']; + $photoData = []; + + foreach ( $photos as $photo ) { + if ( !is_null($photo['photo_id']) ) { + if ( $photo['is_temp'] == 0 ) { + $photoData['publish'][] = [$photo['photo_id'] ]; + } else{ + $photoData['unPublish'][] = [$photo['photo_id']]; + } + } + } + + if (Arr::has($photoData,'publish')) { + $photoPublishCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'whereIn' => [ + ['field' => 'id', 'condition' => '=', 'value' => $photoData['publish']], + ] + ]; + + $propertyPhotoPublishData = $this->propertyPhotoRepository->findByCriteria($photoPublishCriteria, ['id', 'is_temp']); + + if ( empty($propertyPhotoPublishData) ) { + throw new ApiErrorException(lang('photo is_temp data not loaded')); + } + + foreach ( $propertyPhotoPublishData as $publishData ) { + $updatePhotoPublishData = $this->update($publishData['id'], ['is_temp' => 0]); + } + if ( $updatePhotoPublishData[ 'status' ] == false ) { + throw new ApiErrorException(lang('PropertyPhoto can not published')); + } + } + + if (Arr::has($photoData,'unPublish')) { + $photoUnPublishCriteria = [ + 'criteria' => [ + [ 'field' => 'property_id', 'condition' => '=', 'value' => $params[ 'property_id' ] ], + ], + 'whereIn' => [ + [ 'field' => 'id', 'condition' => '=', 'value' => $photoData[ 'unPublish' ] ], + ] + ]; + + $propertyPhotoUnPublishData = $this->propertyPhotoRepository->findByCriteria($photoUnPublishCriteria, [ 'id', 'is_temp' ]); + + foreach ( $propertyPhotoUnPublishData as $unPublishData ) { + $updatePhotoUnPublishData = $this->update($unPublishData[ 'id' ], [ 'is_temp' => 1 ]); + } + + if ( $updatePhotoUnPublishData[ 'status' ] == false ) { + throw new ApiErrorException(lang('PropertyPhoto can not unpublished')); + } + } + + $response = [ 'status' => 1, 'statusCode' => 200, 'message' => '', 'data' => '' ]; + + } catch ( ApiErrorException $e ) { + $response[ 'message' ] = $e->getMessage(); + $response[ 'statusCode' ] = 404; + } catch ( Exception $e ) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + $response[ 'message' ] = $e->getMessage(); + Log::error($message); + } + return output($response); + } + + public function deletePhotos($params) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + + try { + DB::beginTransaction(); + + $photos = $params['photos']; + + if (empty($photos)) { + throw new ApiErrorException(lang('property photos not found')); + } + + $photoDeleteCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'whereIn' => [ + ['field' => 'id', 'condition' => '=', 'value' => $photos], + ] + ]; + + $propertyPhotoDeleteData = $this->propertyPhotoRepository->findByCriteria($photoDeleteCriteria); + + if (empty($propertyPhotoDeleteData)) { + throw new ApiErrorException(lang('property photos delete data not loaded')); + } + + $propertyPhotoDeleteData = $propertyPhotoDeleteData ? $propertyPhotoDeleteData : [] ; + $defaultPhotoDeleted = false ; + foreach ( $propertyPhotoDeleteData as $deleteData ) { + + $destroyCriteria = [ + 'photo_id' => $deleteData['id'], + 'property_id' => $params['property_id'] + ]; + $roomPhotoDelete = $this->propertyRoomPhotoMappingService->destroy($destroyCriteria); + if ($roomPhotoDelete['status'] == false) { + throw new ApiErrorException($roomPhotoDelete['message']); + } + + $offerPhotoDelete = $this->offerPhotoMappingService->destroy($destroyCriteria); + if ($offerPhotoDelete['status'] == false) { + throw new ApiErrorException($offerPhotoDelete['message']); + } + + $webPhotoDelete = $this->propertyWebPhotoMappingService->destroy($destroyCriteria); + if ($webPhotoDelete['status'] == false) { + throw new Exception('api-unknown_error'); + } + + + $placePhotoDelete = $this->propertyPlaceService->destroyPhotoMapping($destroyCriteria); + if ($placePhotoDelete['status'] == false) { + throw new Exception('api-unknown_error'); + } + + + if($deleteData['id']){ + $propertyPhotoDelete = $this->propertyPhotoRepository->destroy([$deleteData['id']]); + if ($propertyPhotoDelete['status'] == false) { + throw new Exception('api-unknown_error'); + } + } + + if($deleteData['is_default']){ + $defaultPhotoDeleted = true ; + } + + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/'.$deleteData['photo_name'].'_200x200.'.$deleteData['file_ext']; + + if (File::exists($photoUrlThumbFilePath)) { + $filePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/'.$deleteData['photo_name'].'_200x200.' .$deleteData['file_ext']; + }else{ + $filePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/'.$deleteData['photo_name']. '_thumbnail.'.$deleteData['file_ext']; + } + + //delete thumbnail image + // $filePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/'.$deleteData['photo_name'].'_200x200.'.$deleteData['file_ext']; + if (File::exists($filePath)) { + if(!File::delete($filePath)){ + Log::alert($filePath . ' not deleted'); + } + } + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/'.$deleteData['photo_name'].'_1024x768.'.$deleteData['file_ext']; + + if (File::exists($photoUrlFilePath)) { + $filePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/'.$deleteData['photo_name']. '_1024x768.' .$deleteData['file_ext']; + }else { + $filePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/'.$deleteData['photo_name']. '_medium.'.$deleteData['file_ext']; + } + + + //delete large image + // $filePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/'.$deleteData['photo_name'].'_1024x768.'.$deleteData['file_ext']; + if (File::exists($filePath)) { + if(!File::delete($filePath)){ + Log::alert($filePath . ' not deleted'); + } + } + + //delete original image + $filePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/'.$deleteData['photo_name'].'.'.$deleteData['file_ext']; + if (File::exists($filePath)) { + if(!File::delete($filePath)){ + Log::alert($filePath . ' not deleted'); + } + } + + + } + + if($defaultPhotoDeleted){ + $photoCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + + $propertyPhotoData = $this->propertyPhotoRepository->findByCriteria($photoCriteria, ['id']); + if($propertyPhotoData){ + $data = $this->propertyPhotoRepository->update($propertyPhotoData['id'], ['is_default' => 1]); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + + $response = [ 'status' => 1, 'statusCode' => 200, 'message' => '', 'data' => '' ]; + DB::commit(); + + } catch ( ApiErrorException $e ) { + $response[ 'message' ] = $e->getMessage(); + DB::rollBack(); + + } catch ( Exception $e ) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + $response[ 'message' ] = $e->getMessage(); + DB::rollBack(); + Log::error($message); + } + return output($response); + } + + public function propertyPhotoUploadFilter($param) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + try + { + $propertyId = fillOnUndefined($param, 'property_id'); + $uploadedPhoto = $this->uploadPhoto($param); + if($uploadedPhoto['status'] != 'success'){ + + throw new ApiErrorException($uploadedPhoto['message']) ; + } + $uploadedPhoto = $uploadedPhoto['data'] ; + + + $defaultPhotoCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_default', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + + $propertyPhotoData = $this->propertyPhotoRepository->findByCriteria($defaultPhotoCriteria, ['id']); + + $insertPropertyPhoto = + [ + 'property_id' => $propertyId, + 'property_photo_category_id' => 1, + 'photo_path' => $uploadedPhoto['photo_path'], + 'photo_name' => $uploadedPhoto['photo_name'], + 'file_size' => $uploadedPhoto['file_size'], + 'file_ext' => $uploadedPhoto['file_ext'], + 'photo_resolution' => $uploadedPhoto['file_resolution'], + 'is_default' => $propertyPhotoData ? 0 : 1, + 'is_compatible_with_myweb_slider' => $uploadedPhoto['is_compatible_with_myweb_slider'], + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time() + ]; + + $response = $this->insertPropertyPhoto($insertPropertyPhoto); + $photoId = $response['data']['id']; + + $getPhotoCategory['category_id'] = 1; + $getPhotoCategory['category_name'] = 'Property'; + + // Set Photo Order.... + $selectLastPhotoCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId] + ], + 'orderBy' => [ + ['field' => 'photo_order', 'value' => 'DESC'] + ], + 'firstRow' => true + ]; + $selectLastPhoto = $this->select($selectLastPhotoCriteria); + $selectLastPhoto = $selectLastPhoto['data']; + $lastOrder = fillOnUndefined($selectLastPhoto, 'photo_order'); + $lastOrder += 1; + + $updatePhotoValues = [ + 'property_photo_category_id' => $getPhotoCategory['category_id'], + 'photo_order' => $lastOrder, + ]; + $updatePhoto = $this->update($photoId , $updatePhotoValues) ; // update + $updatePhoto = $updatePhoto["data"]; + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$updatePhoto['property_id']}" . "/{$updatePhoto['photo_name']}_1024x768.{$updatePhoto['file_ext']}"; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$updatePhoto['property_id']}" . "/{$updatePhoto['photo_name']}_200x200.{$updatePhoto['file_ext']}"; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . "/property-photos/{$updatePhoto['property_id']}" . "/{$updatePhoto['photo_name']}_1024x768.{$updatePhoto['file_ext']}"; + }else { + $photoUrlFilePath = Config::get('app.imageUrl') . "/property-photos/{$updatePhoto['property_id']}" . "/{$updatePhoto['photo_name']}_medium.{$updatePhoto['file_ext']}"; + } + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$updatePhoto['property_id']}" . "/{$updatePhoto['photo_name']}_200x200.{$updatePhoto['file_ext']}"; + }else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$updatePhoto['property_id']}" . "/{$updatePhoto['photo_name']}_thumbnail.{$updatePhoto['file_ext']}"; + } + + $responsePropertyPhoto = []; + $responsePropertyPhoto[] = + [ + 'id' => $updatePhoto['id'], + 'property_id' => $updatePhoto['property_id'], + 'property_photo_category_id' => $updatePhoto['property_photo_category_id'], + 'property_photo_category_name' => $getPhotoCategory['category_name'], + 'photo_name' => $updatePhoto['photo_name'], + 'file_ext' => $updatePhoto['file_ext'], + 'photo_url' => $photoUrlFilePath, + 'photo_url_thump' => $photoUrlThumbFilePath, + 'is_default' => $updatePhoto['is_default'], + 'is_temp' => $updatePhoto['is_temp'], + 'status' => $updatePhoto['status'], + ]; + + $response = ['status' => 1, 'statusCode' => 200 ,'message' => '', 'data' => $responsePropertyPhoto]; + + + }catch (Exception $e) + { + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['status'] = false; + } + + return output($response); + } + + public function getPropertyRoomPhoto($params) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + try { + $propertyId = $params['property_id']; + $roomId = $params['room_id']; + $roomCategory = 11; // For room category priority + + + $getPropertyRoomCriteria = + [ + 'criteria' => + [ + ['field' => 'id', 'condition' => '=', 'value' => $roomId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ], + 'firstRow' => 1 + ]; + $roomInfo = $this->propertyRoomService->select($getPropertyRoomCriteria); + $roomInfo = $roomInfo['data'] ? $roomInfo['data'] : []; + if(!$roomInfo){ + throw new ApiErrorException(lang('Property room not found')); + } + + + $getPropertyPhotoCriteria = + [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyRooms.propertyRoom'], + 'orderBy' => + [ + ["field" => "photo_order", "value" => "ASC"] + ], + ]; + $getPropertyPhotos = $this->propertyPhotoRepository->findByCriteria($getPropertyPhotoCriteria); + $getPropertyPhotos = $getPropertyPhotos ? $getPropertyPhotos : null; + + + $getPropertyPhotos = collect($getPropertyPhotos)->map(function ($value) { + $response = $value ; + + $propertyRoomPhotos = collect($value['property_rooms']) + ->map(function ($value) { + return $value['property_room'] ; + })->values()->all(); + + $response['property_rooms'] = $propertyRoomPhotos; + return $response ; + })->values()->all(); + + if (empty($getPropertyPhotos)) { + throw new ApiErrorException(lang('Property photos not found')); + } + + $getPropertyRoomPhotoCriteria = + [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ], + 'with' => ['propertyRoom'] + ]; + $roomPhotos = $this->propertyRoomPhotoMappingService->select($getPropertyRoomPhotoCriteria); + $roomPhotos = $roomPhotos['data'] ? $roomPhotos['data'] : []; + + + $roomPhotos = collect($roomPhotos); + + + $roomPhotos = $roomPhotos->map(function ($value) { + $response = $value ; + $response['room_type_id'] = $value['property_room']['room_type_id']; + unset($response['property_room']); + return $response ; + }); + + + $filteredList = []; + $showRecommended = true ; + foreach ($getPropertyPhotos as $key => $getPropertyPhoto) { + + $checkMapping = $roomPhotos + ->where('photo_id', '=', $getPropertyPhoto['id']) + ->where('room_id', '=', $roomId) + ->first(); + + $requestedRooms = collect($getPropertyPhoto['property_rooms']) + ->where('room_type_id', '=', $roomInfo['room_type_id']) + ->keyBy('id')->keys(); + + + $checkRecommended = $roomPhotos + ->where('photo_id', '=', $getPropertyPhoto['id']) + ->whereIn('room_id', $requestedRooms) + ->first(); + + if($checkMapping){ + $showRecommended = false ; + } + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_1024x768.{$getPropertyPhoto['file_ext']}"; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_200x200.{$getPropertyPhoto['file_ext']}"; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_1024x768.{$getPropertyPhoto['file_ext']}"; + }else { + $photoUrlFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_medium.{$getPropertyPhoto['file_ext']}"; + } + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_200x200.{$getPropertyPhoto['file_ext']}"; + }else{ + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_thumbnail.{$getPropertyPhoto['file_ext']}"; + } + + $filteredItem = [ + 'id' => $getPropertyPhoto['id'], + 'property_id' => $getPropertyPhoto['property_id'], + 'property_photo_category_id' => $getPropertyPhoto['property_photo_category_id'], + 'photo_url' => $photoUrlFilePath, + 'photo_url_thump' => $photoUrlThumbFilePath, + "photo_name" => $getPropertyPhoto["photo_name"], + "photo_rank" => $getPropertyPhoto["photo_rank"], + "file_size" => $getPropertyPhoto["file_size"], + "file_ext" => $getPropertyPhoto["file_ext"], + "photo_resolution" => $getPropertyPhoto["photo_resolution"], + "photo_order" => $getPropertyPhoto["photo_order"], + "is_default" => $getPropertyPhoto["is_default"], + "is_temp" => $getPropertyPhoto["is_temp"], + "status" => $getPropertyPhoto["status"], + "is_selected" => $checkMapping ? true : false, + "is_recommended" => $checkRecommended ? true : false, + 'order_priority' => $getPropertyPhoto['property_photo_category_id'] == $roomCategory ? 0 : 1 , + ]; + $filteredList[] = $filteredItem ; + + } + + if(collect($filteredList)->where('is_recommended', 'true')->count() == 0){ + $showRecommended = false ; + } + array_multisort( + array_column($filteredList, 'order_priority'), SORT_ASC, + array_column($filteredList, 'photo_order'), SORT_ASC, + $filteredList + ); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => '', 'data' => ['get_room_photos' => $filteredList, 'show_recommended' => $showRecommended] ]; + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + $response['statusCode'] = 204; + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + return output($response); + } + + public function uploadPhoto($params = []){ + + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + try + { + $validateParams = [ + 'photo' => $params['photo'] + ]; + + $uploadedFile = $params['photo']; + $validationResult = $this->propertyPhotoValidator->validate($validateParams); + if ($validationResult->errors()->first()) { + throw new ApiErrorException($validationResult->errors()->all()); + } + + $uploadedOriginalImage = Image::make($uploadedFile); + $imageWidth = $uploadedOriginalImage->width() ; + $imageHeight = $uploadedOriginalImage->height(); + + $fileExt = $uploadedFile->getClientOriginalExtension(); + $fileSize = (int)ceil(Image::make($uploadedFile)->filesize() / 1024); + + $imageExtension = 'jpg'; + $fileExt = $fileExt != $imageExtension ? $imageExtension : $fileExt; + $quality = 80; + + $urlPath = '/property-photos/' . $params['property_id'] . "/"; + $filePath = Config::get('app.fileSystemDriver') . $urlPath ; + $fileName = time().'_'.generateRandomString(10); ; + + if (!File::exists($filePath)) { + File::makeDirectory($filePath, 0777, true); + } + $originalImageMustResize = ($imageHeight > 2000 || $imageWidth > 2000) ; + + if($imageHeight < 768){ + throw new ApiErrorException('image size error: minimum height must be 768px') ; + } + + + if ($imageHeight > $imageWidth) { + + $ratio = $imageHeight/768; + //$ratio200 = 768/200; + $resizeWidth = intval($imageWidth/$ratio); + //$resizeThumbWidth = intval($resizeWidth/$ratio200); + + if($originalImageMustResize){ + $imageHRatio = $imageHeight / 2000 ; + $imageResizedOriginalWidth = intval($imageWidth / $imageHRatio); + $image = $uploadedOriginalImage->fit($imageResizedOriginalWidth, 2000)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality) ; + }else{ + $image = $uploadedOriginalImage->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality) ; + } + $image->fit($resizeWidth, 768)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_medium' . '.' . $fileExt); + $image->fit(300,300)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_thumbnail' . '.' . $fileExt ); + } + else{ + + $ratio = $imageHeight/768; + $resizeWidth = intval($imageWidth/$ratio); + + $ratioTumbnail = 768/300; + $resizeThumbWidth = intval($resizeWidth/$ratioTumbnail); + + + if($originalImageMustResize){ + $imageWRatio = $imageWidth / 2000 ; + $imageResizedOriginalHeight = intval($imageHeight / $imageWRatio); + $image = $uploadedOriginalImage->fit(2000, $imageResizedOriginalHeight)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality) ; + }else{ + $image = $uploadedOriginalImage->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality) ; + } + + if($resizeWidth > 1599){ + $image->fit(1600, 768)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_medium' . '.' . $fileExt); + $image->fit(300,300)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_thumbnail' . '.' . $fileExt); + } + else{ + $image->fit($resizeWidth, 768)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_medium' . '.' . $fileExt); + $image->fit(300)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_thumbnail' . '.' . $fileExt); + } + + } + + $isCompatibleWithMywebSlider = 0; + if($imageWidth >= 1150 && $imageHeight >= 600){ + $isCompatibleWithMywebSlider = 1; + } + + $response = [ + 'status' => true, + 'data' => [ + 'photo_path' => $urlPath, + 'photo_name' => $fileName, + 'file_size' => $fileSize, + 'file_ext' => $fileExt, + 'file_resolution' => $imageWidth .'x'. $imageHeight, + 'is_compatible_with_myweb_slider' => $isCompatibleWithMywebSlider + ] + ]; + }catch (Exception $e) + { + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['status'] = false; + } + + return output($response); + } + + public function getPropertyPlacePhoto($params) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + try { + $propertyId = $params['property_id']; + $placeId = $params['place_id']; + $placeCategory = 0; + $getPropertyPlaceCriteria = + [ + 'criteria' => + [ + ['field' => 'id', 'condition' => '=', 'value' => $placeId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ], + 'firstRow' => 1 + ]; + $placeInfo = $this->propertyPlaceService->select($getPropertyPlaceCriteria); + $placeInfo = $placeInfo['data'] ? $placeInfo['data'] : []; + + if(!$placeInfo){ + throw new ApiErrorException(lang('Property place not found')); + } + + + $getPropertyPhotoCriteria = + [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyPlaces.propertyPlace'], + 'orderBy' => + [ + ["field" => "photo_order", "value" => "ASC"] + ], + ]; + $getPropertyPhotos = $this->propertyPhotoRepository->findByCriteria($getPropertyPhotoCriteria); + $getPropertyPhotos = $getPropertyPhotos ? $getPropertyPhotos : null; + + + $getPropertyPhotos = collect($getPropertyPhotos)->map(function ($value) { + $response = $value ; + + $propertyPlacePhotos = collect($value['property_places']) + ->map(function ($value) { + return $value['property_place'] ; + })->values()->all(); + + $response['property_places'] = $propertyPlacePhotos; + return $response ; + })->values()->all(); + + if (empty($getPropertyPhotos)) { + throw new ApiErrorException(lang('Property photos not found')); + } + + $getPropertyPlacePhotoCriteria = + [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ], + 'with' => ['propertyPlace'] + ]; + $placePhotos = $this->propertyPlacePhotoMappingRepository->findByCriteria($getPropertyPlacePhotoCriteria); + + $placePhotos = $placePhotos ? $placePhotos : []; + + + $placePhotos = collect($placePhotos); + + + $placePhotos = $placePhotos->map(function ($value) { + $response = $value ; + $response['place_category_id'] = $value['property_place']['place_category_id']; + unset($response['property_place']); + return $response ; + }); + + + $filteredList = []; + $showRecommended = true ; + foreach ($getPropertyPhotos as $key => $getPropertyPhoto) { + + $checkMapping = $placePhotos + ->where('photo_id', '=', $getPropertyPhoto['id']) + ->where('place_id', '=', $placeId) + ->first(); + + $requestedPlaces = collect($getPropertyPhoto['property_places']) + ->where('place_category_id', '=', $placeInfo['place_category_id']) + ->keyBy('id')->keys(); + + + $checkRecommended = $placePhotos + ->where('photo_id', '=', $getPropertyPhoto['id']) + ->whereIn('place_id', $requestedPlaces) + ->first(); + + if($checkMapping){ + $showRecommended = false ; + } + + $photoUrlFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_1024x768.{$getPropertyPhoto['file_ext']}"; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_200x200.{$getPropertyPhoto['file_ext']}"; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_1024x768.{$getPropertyPhoto['file_ext']}"; + }else { + $photoUrlFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_medium.{$getPropertyPhoto['file_ext']}"; + } + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_200x200.{$getPropertyPhoto['file_ext']}"; + }else{ + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$getPropertyPhoto['property_id']}" . "/{$getPropertyPhoto['photo_name']}_thumbnail.{$getPropertyPhoto['file_ext']}"; + } + + $filteredItem = [ + 'id' => $getPropertyPhoto['id'], + 'property_id' => $getPropertyPhoto['property_id'], + 'property_photo_category_id' => $getPropertyPhoto['property_photo_category_id'], + 'photo_url' => $photoUrlFilePath, + 'photo_url_thump' => $photoUrlThumbFilePath, + "photo_name" => $getPropertyPhoto["photo_name"], + "photo_rank" => $getPropertyPhoto["photo_rank"], + "file_size" => $getPropertyPhoto["file_size"], + "file_ext" => $getPropertyPhoto["file_ext"], + "photo_resolution" => $getPropertyPhoto["photo_resolution"], + "photo_order" => $getPropertyPhoto["photo_order"], + "is_default" => $getPropertyPhoto["is_default"], + "is_temp" => $getPropertyPhoto["is_temp"], + "status" => $getPropertyPhoto["status"], + "is_selected" => $checkMapping ? true : false, + "is_recommended" => $checkRecommended ? true : false, + 'order_priority' => $getPropertyPhoto['property_photo_category_id'] == $placeCategory ? 0 : 1 , + ]; + $filteredList[] = $filteredItem ; + + } + + if(collect($filteredList)->where('is_recommended', 'true')->count() == 0){ + $showRecommended = false ; + } + array_multisort( + array_column($filteredList, 'order_priority'), SORT_ASC, + array_column($filteredList, 'photo_order'), SORT_ASC, + $filteredList + ); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => '', 'data' => ['get_place_photos' => $filteredList, 'show_recommended' => $showRecommended] ]; + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + $response['statusCode'] = 204; + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + return output($response); + } + +} diff --git a/app/Core/Service/PropertyPlaceService.php b/app/Core/Service/PropertyPlaceService.php new file mode 100644 index 0000000..39ee735 --- /dev/null +++ b/app/Core/Service/PropertyPlaceService.php @@ -0,0 +1,2027 @@ +propertyPlaceRepository = $propertyPlaceRepository; + $this->propertyPlaceCategoryRepository = $propertyPlaceCategoryRepository; + $this->propertyPlaceWorkingHourRepository = $propertyPlaceWorkingHourRepository; + $this->propertyPlaceCreateValidator = $propertyPlaceCreateValidator; + $this->propertyPlacePhotoMappingRepository = $propertyPlacePhotoMappingRepository; + $this->propertyPlacePhotoMappingAddValidator = $propertyPlacePhotoMappingAddValidator; + $this->propertyPlaceUpdateValidator = $propertyPlaceUpdateValidator; + $this->propertyPlaceFactTitleFactMappingRepository = $propertyPlaceFactTitleFactMappingRepository; + $this->propertyPlaceFactMappingRepository = $propertyPlaceFactMappingRepository; + $this->propertyPlaceFactMappingUpdateValidator = $propertyPlaceFactMappingUpdateValidator; + $this->placeCategoryFieldRepository = $placeCategoryFieldRepository; + $this->placeCategoryFieldOptionRepository = $placeCategoryFieldOptionRepository; + $this->placeCategoryFieldOptionFieldMappingRepository = $placeCategoryFieldOptionFieldMappingRepository; + $this->propertyPlaceCategoryFieldValueRepository = $propertyPlaceCategoryFieldValueRepository; + $this->placeCategoryFieldMappingRepository = $placeCategoryFieldMappingRepository; + $this->propertyUnitRepository = $propertyUnitRepository; + $this->languageService = $languageService; + + $this->include = [ + [ + 'id' => 1, + 'name' => 'Paid Services', + 'language_key' => 'input-option-paid_services', + ], + [ + 'id' => 2, + 'name' => 'Free', + 'language_key' => 'input-option-free', + ] + ]; + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + $validationResult = $this->propertyPlaceCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $description = []; + foreach (fillOnUndefined($param, "description", []) as $desc) { + $description[$desc['language_code']] = $desc['description']; + } + + $languageName = []; + foreach (fillOnUndefined($param, "language_name", []) as $value) { + $languageName[$value['language_code']] = $value['name']; + } + + + $insertData = [ + "property_id" => fillOnUndefined($param, "property_id"), + "name" => fillOnUndefined($param, "name"), + "language_name" => !empty($languageName) ? json_encode($languageName) : null, + "place_category_id" => fillOnUndefined($param, "place_category_id"), + "place_working_hour_id" => fillOnUndefined($param, "place_working_hour_id"), + "reservation_requirement" => fillOnUndefined($param, "reservation_requirement"), + "include" => fillOnUndefined($param, "include"), + "web" => fillOnUndefined($param, "web"), + "menu" => fillOnUndefined($param, "menu"), + "contact_form" => fillOnUndefined($param, "contact_form"), + "description" => !empty($description) ? json_encode($description) : null, + "status" => 1, + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->propertyPlaceRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $placeData = $userCreateResult['data']; + + $placeCategoryRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_place_category_id', 'condition' => '=', 'value' => $placeData['place_category_id']], + ], + 'with' => ['placeCategoryField'], + 'orderBy' => [["field" => "order_number", "value" => "ASC"]], + ]; + $placeCategoryMappingData = $this->placeCategoryFieldMappingRepository->findByCriteria($placeCategoryRequest); + $placeCategoryMappingData = $placeCategoryMappingData ? $placeCategoryMappingData : []; + $placeCategories = collect($placeCategoryMappingData)->map(function ($value) { + return $value['place_category_field']; + }); + + $fieldValues = fillOnUndefined($param, 'field_values', []); + $insertFieldValue = []; + + foreach ($fieldValues as $fieldKey => $fieldValue) { + + if (strpos($fieldKey, '_unit') > -1) { + continue; + } + + if (strpos($fieldKey, '_max') > -1) { + continue; + } + + $fieldInfo = $placeCategories->where('id', '=', $fieldValue['field_id'])->where('name', '=', $fieldValue['field_name'])->first(); + if (!$fieldInfo) { + throw new Exception('api-unknown_error'); + } + + $fieldStatics = [ + 'property_id' => $param['property_id'], + 'place_id' => $placeData['id'], + 'property_place_category_id' => $placeData['place_category_id'], + 'place_category_field_id' => $fieldValue['field_id'], + 'status' => 1, + 'created_by' => fillOnUndefined($param, 'user_id'), + 'updated_by' => fillOnUndefined($param, 'user_id'), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + if ($fieldInfo['element'] == 'multiselect') { + foreach ($fieldValue['field_value'] as $item) { + $value = [ + 'input_value' => null, + 'option_value' => $item, + 'unit_value' => null, + ]; + $insertFieldValue[] = array_merge($fieldStatics, $value); + } + } elseif ($fieldInfo['element'] == 'inputunit') { + $searchKey = $fieldValue['field_id'] . '_unit'; + if (!isset($fieldValues[$searchKey]['field_unit_value'])) { + throw new Exception('api-unknown_error'); + } + $value = [ + 'input_value' => $fieldValue['field_value'], + 'option_value' => null, + 'unit_value' => $fieldValues[$searchKey]['field_unit_value'], + ]; + + $insertFieldValue[] = array_merge($fieldStatics, $value); + + } elseif ($fieldInfo['element'] == 'slider') { + + $maxData = null; + if (isset($fieldValues['3_max']) && isset($fieldValues['3_max']['field_max_value']) && $fieldValues['3_max']['field_max_value']) { + $maxData = $fieldValues['3_max']['field_max_value']; + } + + + if (($fieldValue['field_value'] && $maxData === NULL) || $maxData && !$fieldValue['field_value']) { + throw new Exception('Min Age and Max Age is required'); + } + + $inputValue = $fieldValue['field_value'] && $maxData ? $fieldValue['field_value'] . '-' . $maxData : null; + $value = [ + 'input_value' => $inputValue, + 'option_value' => null, + 'unit_value' => null, + ]; + + $insertFieldValue[] = array_merge($fieldStatics, $value); + + } else { + $value = [ + 'input_value' => $fieldInfo['element'] != 'select' ? $fieldValue['field_value'] : null, + 'option_value' => $fieldInfo['element'] == 'select' ? $fieldValue['field_value'] : null, + 'unit_value' => null, + ]; + $insertFieldValue[] = array_merge($fieldStatics, $value); + + } + } + + if ($insertFieldValue) { + $createResult = $this->propertyPlaceCategoryFieldValueRepository->insert($insertFieldValue); + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + DB::commit(); + } catch (ApiErrorException $e) { + DB::rollBack(); + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyPlaceRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function selectPhotoMapping($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyPlacePhotoMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + DB::beginTransaction(); + $placeId = fillOnUndefined($param, "property_place_id"); + $placeRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => $placeId], + ], + 'firstRow' => true + ]; + $propertyWebData = $this->propertyPlaceRepository->findByCriteria($placeRequest, ['id', 'name', 'place_category_id']); + + if (!$propertyWebData) { + throw new ApiErrorException(lang('Property - Place mapping not found.')); + } + + + $paramPlaceCategoryId = fillOnUndefined($param, 'place_category_id'); + + if ($paramPlaceCategoryId && ($propertyWebData['place_category_id'] != $paramPlaceCategoryId)) { + + $propertyPlaceFactMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'property_place_id', 'condition' => '=', 'value' => $placeId] + ] + + ]; + $propertyPlaceFactMappingData = $this->propertyPlaceFactMappingRepository->findByCriteria($propertyPlaceFactMappingRequest); + + $propertyPlaceFactMappingData = $propertyPlaceFactMappingData ? $propertyPlaceFactMappingData : []; + + $propertyPlaceFactMappingIds = collect($propertyPlaceFactMappingData)->keyBy('id')->keys()->all(); + + if ($propertyPlaceFactMappingIds) { + + $propertyPlaceFactMappingDelete = $this->propertyPlaceFactMappingRepository->destroy($propertyPlaceFactMappingIds); + + if ($propertyPlaceFactMappingDelete['status'] != "success") { + throw new Exception('api-unknown_error'); + } + } + + + $propertyPlacePhotoMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'place_id', 'condition' => '=', 'value' => $placeId] + ] + + ]; + + $propertyPlacePhotoMapping = $this->propertyPlacePhotoMappingRepository->findByCriteria($propertyPlacePhotoMappingRequest); + $propertyPlacePhotoMapping = $propertyPlacePhotoMapping ? $propertyPlacePhotoMapping : []; + + $propertyPlacePhotoMappingIds = collect($propertyPlacePhotoMapping)->keyBy('id')->keys()->all(); + if ($propertyPlacePhotoMappingIds) { + + $propertyPlacePhotoMappingDelete = $this->propertyPlacePhotoMappingRepository->destroy($propertyPlacePhotoMappingIds); + + if ($propertyPlacePhotoMappingDelete['status'] != "success") { + throw new Exception('api-unknown_error'); + } + } + + } + + $description = []; + foreach (fillOnUndefined($param, "description", []) as $desc) { + $description[$desc['language_code']] = $desc['description']; + } + + $languageName = []; + foreach (fillOnUndefined($param, "language_name", []) as $value) { + $languageName[$value['language_code']] = $value['name']; + } + + $updateData = [ + "name" => fillOnUndefined($param, "name"), + "language_name" => !empty($languageName) ? json_encode($languageName) : null, + "place_category_id" => fillOnUndefined($param, "place_category_id"), + "place_working_hour_id" => fillOnUndefined($param, "place_working_hour_id"), + "reservation_requirement" => fillOnUndefined($param, "reservation_requirement"), + "include" => fillOnUndefined($param, "include"), + "description" => !empty($description) ? json_encode($description) : null, + "web" => fillOnUndefined($param, "web"), + "menu" => fillOnUndefined($param, "menu"), + "contact_form" => fillOnUndefined($param, "contact_form"), + "status" => fillOnUndefined($param, "status", 1), + "updated_by" => fillOnUndefined($param, "user_id"), + "updated_at" => time(), + ]; + + $validationResult = $this->propertyPlaceUpdateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $updateResult = $this->propertyPlaceRepository->update($placeId, $updateData); + + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + + $fieldValuesRequest = [ + 'criteria' => [ + ['field' => 'place_id', 'condition' => '=', 'value' => $placeId], + ], + + ]; + $oldValuesData = $this->propertyPlaceCategoryFieldValueRepository->findByCriteria($fieldValuesRequest); + $oldValuesData = $oldValuesData ? $oldValuesData : []; + + $deleteThisIds = collect($oldValuesData)->keyBy('id')->keys()->all(); + + if ($deleteThisIds) { + $deleteThisArray = $this->propertyPlaceCategoryFieldValueRepository->destroy($deleteThisIds); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + $placeCategoryRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_place_category_id', 'condition' => '=', 'value' => $updateData['place_category_id']], + ], + 'with' => ['placeCategoryField'], + 'orderBy' => [["field" => "order_number", "value" => "ASC"]], + ]; + $placeCategoryMappingData = $this->placeCategoryFieldMappingRepository->findByCriteria($placeCategoryRequest); + $placeCategoryMappingData = $placeCategoryMappingData ? $placeCategoryMappingData : []; + $placeCategories = collect($placeCategoryMappingData)->map(function ($value) { + return $value['place_category_field']; + }); + + $fieldValues = fillOnUndefined($param, 'field_values', []); + $insertFieldValue = []; + foreach ($fieldValues as $fieldKey => $fieldValue) { + + if (strpos($fieldKey, '_unit') > -1) { + continue; + } + + $fieldInfo = $placeCategories->where('id', '=', $fieldValue['field_id'])->where('name', '=', $fieldValue['field_name'])->first(); + if (!$fieldInfo) { + throw new Exception('api-unknown_error'); + } + + $fieldStatics = [ + 'property_id' => $updateData['property_id'], + 'place_id' => $placeId, + 'property_place_category_id' => $updateData['place_category_id'], + 'place_category_field_id' => $fieldValue['field_id'], + 'status' => 1, + 'created_by' => fillOnUndefined($param, 'user_id'), + 'updated_by' => fillOnUndefined($param, 'user_id'), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + if ($fieldInfo['element'] == 'multiselect') { + foreach ($fieldValue['field_value'] as $item) { + $value = [ + 'input_value' => null, + 'option_value' => $item, + 'unit_value' => null, + ]; + $insertFieldValue[] = array_merge($fieldStatics, $value); + } + } elseif ($fieldInfo['element'] == 'inputunit') { + $searchKey = $fieldValue['field_id'] . '_unit'; + $fieldUnitValue = null; + if (isset($fieldValues[$searchKey]['field_unit_value'])) { + $fieldUnitValue = $fieldValues[$searchKey]['field_unit_value']; + } + $value = [ + 'input_value' => $fieldValue['field_value'], + 'option_value' => null, + 'unit_value' => $fieldUnitValue == "" ? null : $fieldUnitValue, + ]; + + $insertFieldValue[] = array_merge($fieldStatics, $value); + + } elseif ($fieldInfo['element'] == 'slider') { + + if (isset($fieldValue['field_value']) && !empty($fieldValue['field_value'])) { + + $count = collect($fieldValue['field_value'])->count(); + if ($count == 1) { + throw new Exception('Min Age and Max Age is required'); + } + + if (($fieldValue['field_value'][0] && $fieldValue['field_value'][1] === NULL) || ($fieldValue['field_value'][0] === NULL && $fieldValue['field_value'][1])) { + throw new Exception('Min Age and Max Age is required'); + } + + } + $saveValue = null; + if (!in_array("", $fieldValue['field_value'])) { + $saveValue = implode('-', $fieldValue['field_value']); + } + + + $value = [ + 'input_value' => $saveValue, + 'option_value' => null, + 'unit_value' => null, + ]; + + $insertFieldValue[] = array_merge($fieldStatics, $value); + + } else { + $value = [ + 'input_value' => $fieldInfo['element'] != 'select' ? $fieldValue['field_value'] : null, + 'option_value' => $fieldInfo['element'] == 'select' && !empty($fieldValue['field_value']) ? $fieldValue['field_value'] : null, + 'unit_value' => null, + + ]; + $insertFieldValue[] = array_merge($fieldStatics, $value); + + } + } + + if ($insertFieldValue) { + $createResult = $this->propertyPlaceCategoryFieldValueRepository->insert($insertFieldValue); + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPropertyPlaceCategories($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $placeCategoryRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $placeCategoryData = $this->propertyPlaceCategoryRepository->findByCriteria($placeCategoryRequest); + $placeCategories = collect($placeCategoryData); + + $categoryTriesRequest = [ + 'parent_id' => null, + 'placeCategories' => $placeCategories + ]; + + + $categoryTries = $this->categoryTries($categoryTriesRequest); + + + $response = [ + 'status' => true, + 'data' => $categoryTries, + ]; + + } 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); + } + + private function categoryTries($params) + { + + $placeCategories = $params['placeCategories']; + $parentId = $params['parent_id']; + $placeCategoryList = $placeCategories->where('parent_id', '=', $parentId)->all(); + $responseData = []; + foreach ($placeCategoryList as $placeCategory) { + + $responseItem = [ + "id" => $placeCategory['id'], + "name" => $placeCategory['name'], + "language_key" => $placeCategory['language_key'], + "parent_id" => $placeCategory['parent_id'], + "level" => $placeCategory['level'], + "order_number" => $placeCategory['order_number'], + "icon" => $placeCategory['icon'], + "child" => null + ]; + + $checkChild = $placeCategories->where('parent_id', '=', $placeCategory['id'])->count(); + + if ($checkChild > 0) { + $categoryTriesRequest = [ + 'parent_id' => $placeCategory['id'], + 'placeCategories' => $placeCategories + ]; + $categoryTries = $this->categoryTries($categoryTriesRequest); + $responseItem['child'] = $categoryTries; + } + + $responseData[$placeCategory['id']] = $responseItem; + + } + + + return $responseData; + + } + + private function categoryTriesSingleArray($params) + { + + $placeCategories = $params['placeCategories']; + $parentId = $params['parent_id']; + $placeCategoryList = $placeCategories->where('parent_id', '=', $parentId)->all(); + $responseData = []; + foreach ($placeCategoryList as $placeCategory) { + + $responseItem = [ + "id" => $placeCategory['id'], + "name" => $placeCategory['name'], + "language_key" => $placeCategory['language_key'], + "parent_id" => $placeCategory['parent_id'], + "level" => $placeCategory['level'], + "order_number" => $placeCategory['order_number'], + "icon" => $placeCategory['icon'], + + ]; + + $checkChild = $placeCategories->where('parent_id', '=', $placeCategory['id'])->count(); + $categoryTries = []; + if ($checkChild > 0) { + $categoryTriesRequest = [ + 'parent_id' => $placeCategory['id'], + 'placeCategories' => $placeCategories + ]; + $categoryTries = $this->categoryTriesSingleArray($categoryTriesRequest); + } + $responseData[] = $responseItem; + $responseData = array_merge($responseData, $categoryTries); + } + + return $responseData; + + } + + public function getPropertyWorkingHours($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $placeCategoryRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $placeWorkingHourData = $this->propertyPlaceWorkingHourRepository->findByCriteria($placeCategoryRequest); + $placeWorkingHours = collect($placeWorkingHourData)->map(function ($value) { + + return [ + "id" => $value['id'], + "name" => $value['name'], + "language_key" => $value['language_key'], + "status" => $value['status'], + ]; + + }); + + $response = [ + 'status' => true, + 'data' => $placeWorkingHours, + ]; + + } 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); + } + + public function listPlaces($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $placeRequest = [ + 'criteria' => [ + //['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyPlaceCategory', 'propertyPlaceWorkingHour'] + ]; + $placeData = $this->propertyPlaceRepository->findByCriteria($placeRequest); + $placeData = $placeData ? $placeData : []; + + + $placeCategoryRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $placeCategoryData = $this->propertyPlaceCategoryRepository->findByCriteria($placeCategoryRequest); + $placeCategories = collect($placeCategoryData); + + + $getApplicationLanguages = $this->languageService->getApplicationLanguages(); + if ($getApplicationLanguages['status'] != 'success') { + throw new ApiErrorException($getApplicationLanguages['message']); + } + $applicationLanguages = $getApplicationLanguages['data']; + + $places = []; + + foreach ($placeData as $place) { + $include = collect($this->include)->where('id', '=', $place['include'])->first(); + + $descriptionLangContents = json_decode($place['description'], 1); + $responseLangDescription = []; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangDescription[] = [ + 'language_code' => $langKey, + 'description' => isset($descriptionLangContents[$langKey]) ? $descriptionLangContents[$langKey] : null + ]; + } + + $languageNameContents = json_decode($place['language_name'], 1); + $responseLanguageName = []; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLanguageName[] = [ + 'language_code' => $langKey, + 'name' => isset($languageNameContents[$langKey]) ? $languageNameContents[$langKey] : null + ]; + } + + $exportPlace = [ + "id" => $place['id'], + "property_id" => $place['property_id'], + "name" => $place['name'], + "place_category_id" => $place['place_category_id'], + "place_working_hour_id" => $place['place_working_hour_id'], + "place_working_hour_name" => $place['property_place_working_hour']['name'], + "place_working_hour_language_key" => $place['property_place_working_hour']['language_key'], + "reservation_requirement" => $place['reservation_requirement'], + "contact_form" => $place['contact_form'], + "include_name" => isset($include) ? $include['name'] : null, + "include_language_key" => isset($include) ? $include['language_key'] : null, + "order_number" => $place['order_number'], + "description" => $responseLangDescription, + "language_name" => $responseLanguageName, + "web" => $place['web'], + "menu" => $place['menu'], + "include" => $place['include'], + "status" => $place['status'], + ]; + + $categoryParentRequest = [ + 'parent_id' => isset($place['property_place_category']) ? $place['property_place_category']['parent_id'] : null, + 'placeCategories' => $placeCategories + ]; + + $placeCategoryArray = []; + + $placeCategoryArray[] = $place['property_place_category']; + if ($place['property_place_category']['parent_id'] != null) { + $parentCategory = $this->categoryParents($categoryParentRequest); + $placeCategoryArray = array_merge($placeCategoryArray, $parentCategory); + } + $categories = collect($placeCategoryArray)->sortBy('level')->all(); + + $level = 1; + $categoryFilter = []; + foreach ($categories as $category) { + $exportPlace['category_tree'][$level] = [ + "id" => $category['id'], + "name" => $category['name'], + "language_key" => $category['language_key'], + "parent_id" => $category['parent_id'], + "level" => $category['level'], + "order_number" => $category['order_number'], + "icon" => $category['icon'], + "status" => $category['status'], + ]; + $level++; + $categoryFilter[] = $category['id']; + } + $exportPlace['category_filters'] = $categoryFilter; + + $places[] = $exportPlace; + } + + + $response = [ + 'status' => true, + 'data' => $places, + ]; + + } 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); + } + + private function categoryParents($params) + { + $placeCategories = $params['placeCategories']; + $parentId = $params['parent_id']; + $placeCategory = $placeCategories->where('id', '=', $parentId)->first(); + $responseData[] = $placeCategory; + + if (isset($placeCategory) && $placeCategory['parent_id'] != null) { + $categoryParentRequest = [ + 'parent_id' => $placeCategory['parent_id'], + 'placeCategories' => $placeCategories + ]; + $parentCategory = $this->categoryParents($categoryParentRequest); + $responseData = array_merge($responseData, $parentCategory); + } + return $responseData; + } + + public function updatePropertyPlacePhotoMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyPlacePhotoMappingAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $addPropertyPlacePhotoMapping = []; + $addPhotoList = collect($params['property_place_photo']) + ->where('is_selected', '=', true); + $addPhotoIds = $addPhotoList->keyBy("id")->keys()->toArray(); + + $removePhotoList = collect($params['property_place_photo']) + ->where('is_selected', '=', false); + + $removePhotoIds = $removePhotoList->keyBy("id")->keys()->toArray(); + + $checkPropertyMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'place_id', 'condition' => '=', 'value' => $params['place_id']], + ], + "whereIn" => [ + ["field" => "photo_id", "value" => $addPhotoIds] + ] + ]; + $checkPropertyMappingResponse = $this->select($checkPropertyMappingRequest, ['id', 'property_id', 'photo_id']); + + if ($checkPropertyMappingResponse['status'] != 'success') { + throw new ApiErrorException(lang('Mapping data not loaded')); + } + $checkPropertyMappingCollect = collect($checkPropertyMappingResponse['data']); + + foreach ($addPhotoList->toArray() as $key => $param) { + + if ( + !$checkPropertyMappingCollect->where('property_id', '=', $params['property_id']) + ->where('photo_id', '=', $param['id']) + ->first() + ) { + + $addPropertyPlacePhotoMapping[] = + [ + 'property_id' => $params['property_id'], + 'place_id' => $params['place_id'], + 'photo_id' => fillOnUndefined($param, 'id'), + 'created_by' => $params['user_id'], + 'updated_by' => $params['user_id'], + ]; + } + } + + $response = $this->propertyPlacePhotoMappingRepository->createAll($addPropertyPlacePhotoMapping); + + if ($removePhotoIds) { + + $findCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'place_id', 'condition' => '=', 'value' => $params['place_id']], + ], + "whereIn" => + [ + ["field" => "photo_id", "value" => $removePhotoIds] + ] + ]; + + $deletePropertyPlacePhotoMapping = $this->propertyPlacePhotoMappingRepository->findByCriteria($findCriteria); + + $deleteThisIds = []; + foreach ($deletePropertyPlacePhotoMapping as $deleteThisItem) { + $deleteThisIds[] = $deleteThisItem['id']; + } + + if ($deleteThisIds) { + $this->propertyPlacePhotoMappingRepository->deleteById($deleteThisIds); + } + } + + if ($response['status'] != 'success') { + throw new ApiErrorException(lang('Data is not added')); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + public function updateStatus($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $placeId = fillOnUndefined($param, "property_place_id"); + $placeRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => $placeId], + ] + ]; + $propertyWebData = $this->propertyPlaceRepository->findByCriteria($placeRequest, ['id', 'name']); + + if (!$propertyWebData) { + throw new ApiErrorException(lang('Property - Place mapping not found.')); + } + + $updateData = + [ + "status" => fillOnUndefined($param, "set_status", 0), + "updated_by" => fillOnUndefined($param, "user_id"), + "updated_at" => time(), + ]; + + $updateResult = $this->propertyPlaceRepository->update($placeId, $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deletePlace($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + DB::beginTransaction(); + + $placeRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_place_id')], + ], + 'with' => ['propertyPlaceCategory', 'propertyPlaceWorkingHour', 'propertyPlacePhotoMapping'], + "firstRow" => 1 + ]; + $placeData = $this->propertyPlaceRepository->findByCriteria($placeRequest); + if (!$placeData) { + throw new ApiErrorException(lang('Property - Place not found.')); + } + $deleteIds = collect($placeData['property_place_photo_mapping'])->keyBy('id')->keys()->all(); + if ($deleteIds) { + $deletePhotoMapping = $this->propertyPlacePhotoMappingRepository->destroy($deleteIds); + if ($deletePhotoMapping['status'] != "success") { + throw new Exception('api-unknown_error'); + } + } + + $deletePlaceCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_place_id']] + ] + ]; + $this->propertyPlaceRepository->delete($deletePlaceCriteria); + + + $response = [ + 'status' => true, + 'data' => null, + ]; + + db::commit(); + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + db::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + db::rollBack(); + } + + return output($response); + } + + public function getPropertyPlaceCategoriesSingle($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $placeCategoryRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + ]; + + $placeCategoryData = $this->propertyPlaceCategoryRepository->findByCriteria($placeCategoryRequest); + $placeCategories = collect($placeCategoryData); + + $categoryTries = $placeCategories->map(function ($value) { + + return [ + "id" => $value['id'], + "name" => $value['name'], + "language_key" => $value['language_key'], + "parent_id" => $value['parent_id'], + "level" => $value['level'], + "order_number" => $value['order_number'], + "icon" => $value['icon'], + ]; + })->values()->all(); + + $response = [ + 'status' => true, + 'data' => $categoryTries, + ]; + + } 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); + } + + public function editPlaces($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $placeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['property_place_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyPlaceCategory', 'propertyPlaceWorkingHour'], + 'firstRow' => 1 + ]; + $placeData = $this->propertyPlaceRepository->findByCriteria($placeRequest); + if (!$placeData) { + throw new ApiErrorException(lang('Property - Place not found.')); + } + $placeCategoryRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $placeCategoryData = $this->propertyPlaceCategoryRepository->findByCriteria($placeCategoryRequest); + $placeCategories = collect($placeCategoryData); + + $place = $placeData; + $include = collect($this->include)->where('id', '=', $place['include'])->first(); + + + $exportPlace = [ + "id" => $place['id'], + "property_id" => $place['property_id'], + "name" => $place['name'], + "place_category_id" => $place['place_category_id'], + "place_working_hour_id" => $place['place_working_hour_id'], + "place_working_hour_name" => $place['property_place_working_hour']['name'], + "place_working_hour_language_key" => $place['property_place_working_hour']['language_key'], + "reservation_requirement" => $place['reservation_requirement'], + "include" => $place['include'], + "include_name" => isset($include) ? $include['name'] : null, + "include_language_key" => isset($include) ? $include['language_key'] : null, + "web" => $place['web'], + "status" => $place['status'], + ]; + + $categoryParentRequest = [ + 'parent_id' => isset($place['property_place_category']) ? $place['property_place_category']['parent_id'] : null, + 'placeCategories' => $placeCategories + ]; + + $placeCategoryArray = []; + + $placeCategoryArray[] = $place['property_place_category']; + if ($place['property_place_category']['parent_id'] != null) { + $parentCategory = $this->categoryParents($categoryParentRequest); + $placeCategoryArray = array_merge($placeCategoryArray, $parentCategory); + } + $categories = collect($placeCategoryArray)->sortBy('level')->all(); + + $level = 1; + $categoryFilter = []; + foreach ($categories as $category) { + $exportPlace['category_tree'][$level] = [ + "id" => $category['id'], + "name" => $category['name'], + "language_key" => $category['language_key'], + "parent_id" => $category['parent_id'], + "level" => $category['level'], + "order_number" => $category['order_number'], + "icon" => $category['order_number'], + "status" => $category['status'], + ]; + $level++; + $categoryFilter[] = $category['id']; + } + $exportPlace['category_filters'] = json_encode($categoryFilter); + + $categoryTries = $placeCategories->map(function ($value) { + + return [ + "id" => $value['id'], + "name" => $value['name'], + "language_key" => $value['language_key'], + "parent_id" => $value['parent_id'], + "level" => $value['level'], + "order_number" => $value['order_number'], + "icon" => $value['icon'], + ]; + })->values()->all(); + + + $responseData = [ + 'place' => $exportPlace, + 'place_categories' => $categoryTries, + 'place_includes' => $this->include, + + ]; + + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function listPlaceFacilities($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $placeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'id', 'condition' => '=', 'value' => $params['place_id']], + ], + 'firstRow' => true, + ]; + $placeData = $this->propertyPlaceRepository->findByCriteria($placeRequest); + if (!$placeData) { + throw new ApiErrorException(lang('Property - Place not found.')); + } + + $placeFactTitleFactMappingRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'place_category_id', 'condition' => '=', 'value' => $placeData['place_category_id']], + ], + 'with' => ['placeCategory', 'placeFactTitle', 'placeFact'] + ]; + + $placeFactTitleFactMappingData = $this->propertyPlaceFactTitleFactMappingRepository->findByCriteria($placeFactTitleFactMappingRequest); + $placeFactTitleFactMapping = collect($placeFactTitleFactMappingData); + + $placeFactTitleFactMappingRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_place_id', 'condition' => '=', 'value' => $params['place_id']], + ], + ]; + + $placeFactMappingData = $this->propertyPlaceFactMappingRepository->findByCriteria($placeFactTitleFactMappingRequest); + $placeFactMapping = collect($placeFactMappingData); + + $titleGroups = $placeFactTitleFactMapping->groupBy('place_fact_title_id')->toArray(); + $exportTitles = []; + + foreach ($titleGroups as $titleGroupList) { + if (!$titleGroupList) { + continue; + } + $exportTitleGroup = $titleGroupList[0]['place_fact_title']; + $facts = []; + foreach ($titleGroupList as $title) { + $checkSelected = $placeFactMapping + ->where('place_fact_title_fact_mapping_id', '=', $title['id']) + ->where('property_place_id', '=', $params['place_id']) + ->first(); + $exportItem = $title['place_fact']; + $exportItem['place_fact_title_fact_mapping_id'] = $title['id']; + $exportItem['is_selected'] = isset($checkSelected); + $facts[] = $exportItem; + } + $exportTitleGroup['facts'] = $facts; + $exportTitles[] = $exportTitleGroup; + } + + $response = [ + 'status' => true, + 'data' => $exportTitles, + ]; + + } 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); + } + + public function updatePlaceFacilities($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyPlaceFactMappingUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $placeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + $propertyPlaceData = $this->propertyPlaceRepository->findByCriteria($placeRequest, ['id', 'name']); + + if (!$propertyPlaceData) { + throw new ApiErrorException(lang('Property - Place mapping not found.')); + } + + $propertyPlaceFactMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_place_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'place_id')], + ['field' => 'place_fact_title_fact_mapping_id', 'condition' => '=', 'value' => $params['place_fact_title_fact_mapping_id']], + ], + 'firstRow' => true + ]; + $propertyPlaceFactMappingData = $this->propertyPlaceFactMappingRepository->findByCriteria($propertyPlaceFactMappingRequest); + + + if ($params['is_selected']) { + + if ($propertyPlaceFactMappingData) { + throw new Exception('api-unknown_error'); + } + + $insertData = + [ + "property_id" => fillOnUndefined($params, "property_id"), + "property_place_id" => fillOnUndefined($params, "place_id"), + "place_fact_title_fact_mapping_id" => fillOnUndefined($params, "place_fact_title_fact_mapping_id"), + "status" => 1, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $createResult = $this->propertyPlaceFactMappingRepository->create($insertData); + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } else { + if (!$propertyPlaceFactMappingData) { + throw new Exception('api-unknown_error'); + } + + $deleteThis[] = $propertyPlaceFactMappingData['id']; + $this->propertyPlaceFactMappingRepository->deleteById($deleteThis); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPlaceCategoryFields($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $placeCategoryRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_place_category_id', 'condition' => '=', 'value' => $params['place_category_id']], + ], + 'with' => ['placeCategoryField.placeFieldOptionMapping.placeCategoryFieldOption'], + 'orderBy' => [["field" => "order_number", "value" => "ASC"]], + + ]; + $placeCategoryMappingData = $this->placeCategoryFieldMappingRepository->findByCriteria($placeCategoryRequest); + $placeCategoryMappingData = $placeCategoryMappingData ? $placeCategoryMappingData : []; + + $oldValuesData = []; + if ($params['place_id']) { + $fieldValuesRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_place_category_id', 'condition' => '=', 'value' => $params['place_category_id']], + ['field' => 'place_id', 'condition' => '=', 'value' => $params['place_id']], + ], + + ]; + $oldValuesData = $this->propertyPlaceCategoryFieldValueRepository->findByCriteria($fieldValuesRequest); + $oldValuesData = $oldValuesData ? $oldValuesData : []; + } + $oldValues = collect($oldValuesData); + $responseFields = []; + + + $fieldUnitsRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + + ]; + $fieldUnitData = $this->propertyUnitRepository->findByCriteria($fieldUnitsRequest); + $fieldUnitData = collect($fieldUnitData); + + foreach ($placeCategoryMappingData as $mapping) { + if (!$mapping['place_category_field']) { + continue; + } + + $field = $mapping['place_category_field']; + $fieldUnitShortName = null; + $oldFieldValues = $oldValues + ->where('property_place_category_id', '=', $params['place_category_id']) + ->where('place_category_field_id', '=', $field['id']); + + if ($field['element'] == 'multiselect') { + $oldValue = $oldFieldValues->keyBy('option_value')->keys()->all(); + } elseif ($field['element'] == 'select') { + $oldValue = $oldFieldValues->keyBy('option_value')->keys()->first(); + } elseif ($field['element'] == 'slider') { + $getOldValue = $oldFieldValues->keyBy('input_value')->keys()->first(); + $oldValue = $getOldValue ? explode('-', $getOldValue) : []; + } else { + $oldValue = $oldFieldValues->first(); + $oldValue = $oldValue['input_value']; + } + + $fieldUnits = null; + $oldUnitValue = null; + $unitShortName = null; + if ($field['element'] == 'inputunit') { + $fieldAttribute = json_decode($field['field_attribute'], 1); + if (!isset($fieldAttribute['units'])) { + throw new Exception('api-unknown_error'); + } + + $oldUnit = $oldFieldValues->first(); + $oldUnitValue = $oldUnit['unit_value']; + $oldUnitData = $fieldUnitData->where('id', '=', $oldUnit['unit_value'])->first(); + $unitShortName = $oldUnitData['short_name']; + + $fieldUnits = $fieldUnitData->whereIn('id', $fieldAttribute['units'])->map(function ($unit) use ($oldUnitValue) { + return [ + "id" => $unit['id'], + "name" => $unit['name'], + "language_key" => $unit['language_key'], + "short_name" => $unit['short_name'], + "is_selected" => $oldUnitValue == $unit['id'], + ]; + })->values()->all(); + } + + $responseField = [ + 'field_id' => $field['id'], + 'field_name' => $field['name'], + 'field_value' => $oldValue, + 'field_unit_value' => $oldUnitValue, + 'field_unit_short_name' => $unitShortName, + 'field_place_category_id' => $field['id'], + 'field_label' => $field['label'], + 'field_language_key' => $field['language_key'], + 'field_element' => $field['element'], + 'field_type' => $field['type'], + 'field_attribute' => json_decode($field['field_attribute'], 1), + 'field_order_number' => $mapping['order_number'], + 'field_status' => $field['status'], + 'field_units' => $fieldUnits, + + ]; + if ($field['place_field_option_mapping']) { + $searchArray = $field['element'] != 'multiselect' ? [$oldValue] : $oldValue; + $options = collect($field['place_field_option_mapping'])->mapWithKeys(function ($value) use ($searchArray) { + + $optionIsSelected = array_search($value['place_category_field_option']['id'], $searchArray) > -1 ? true : false; + return [ + $value['place_category_field_option']['id'] => + [ + 'option_id' => $value['place_category_field_option']['id'], + 'option_name' => $value['place_category_field_option']['name'], + 'option_language_key' => $value['place_category_field_option']['language_key'], + 'option_status' => $value['place_category_field_option']['status'], + 'option_is_selected' => $optionIsSelected, + ] + ]; + + })->toArray(); + $responseField['options'] = $options; + } + $responseFields[$field['id']] = $responseField; + if ($field['element'] == 'inputunit') { + $responseField['field_id'] = $field['id'] . '_unit'; + unset($responseField['field_element']); + $responseFields[$field['id'] . '_unit'] = $responseField; + } + } + $response = [ + 'status' => true, + 'data' => $responseFields, + ]; + + } 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); + + } + + public function destroyPhotoMapping($params) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $deleteCriteria = [ + 'criteria' => [ + ['field' => 'photo_id', 'condition' => '=', 'value' => $params['photo_id']], + ] + ]; + $deleteData = $this->propertyPlacePhotoMappingRepository->findByCriteria($deleteCriteria, ['id']); + $deleteData = $deleteData ? $deleteData : []; + if ($deleteData) { + $deleteIds = array_column($deleteData, 'id'); + $destroyResult = $this->propertyPlacePhotoMappingRepository->destroy($deleteIds); + if ($destroyResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null + ]; + + } 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); + + + } + + public function insertNewFieldMapping($params) + { + + // bu fonksiyon yeni bir field eklendiğinde mapping eklenebilmesi için konmuştur. + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + + die(); + $mappingData = $this->placeCategoryFieldMappingRepository->findByCriteria(); + $mapping = collect($mappingData); + $categoryList = $mapping->where('property_place_category_field_id', '=', $params['before_field'])->all(); + $withoutList = $mapping + ->where('property_place_category_field_id', '!=', $params['before_field']) + ->where('property_place_category_field_id', '!=', $params['add_field_id']) + ->all(); + + + $orderList = []; + foreach ($withoutList as $item) { + if ($item['order_number'] > 1) { + $update = [ + 'order_number' => $item['order_number'] + 1, + ]; + $orderList[$item['id']] = [ + 'order_number' => $item['order_number'] + 1, + ]; + + // $this->placeCategoryFieldMappingRepository->update($item['id'], $update) ; + } + } + + $insertThis = []; + foreach ($categoryList as $data) { + $insertData = + [ + "property_place_category_id" => $data['property_place_category_id'], + "property_place_category_field_id" => $params['add_field_id'], + "order_number" => $data['order_number'] + 1, + "status" => 1, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + $insertThis[] = $insertData; + } + + if ($insertThis) { + $createResult = $this->placeCategoryFieldMappingRepository->insert($insertThis); + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + $response = [ + 'status' => true, + 'data' => null + ]; + + } 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); + + + } + + public function webMenuPlaceCategoriesAndPlaces($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $placeData = $this->listPlaces($params); + if ($placeData['status'] != 'success') { + throw new ApiErrorException($placeData['message']); + } + $places = $placeData['data'] ? $placeData['data'] : []; + $responsePlaces = []; + $responsePlaceCategories = []; + foreach ($places as $place) { + $responsePlaces[] = [ + "id" => $place['id'], + "name" => $place['name'], + "language_key" => $place['name'], + "route" => 'place/' . $place['id'], + "menu_type" => "PLC", + "menu_code" => "TOP", + "order_number" => $place['order_number'], + "icon" => "fas fa-check", + "alias" => "place-" . $place['id'], + "is_selected" => false, + "order_number_mapping" => null, + "status" => $place['status'], + ]; + + $placeCategory = $place['category_tree'][1]; + $responsePlaceCategories[$placeCategory['id']] = [ + "id" => $placeCategory['id'], + "name" => $placeCategory['name'], + "language_key" => $placeCategory['language_key'], + "route" => 'place-category/' . $placeCategory['id'], + "menu_type" => "PLCCAT", + "menu_code" => "TOP", + "order_number" => $placeCategory['order_number'], + "icon" => $placeCategory["icon"] ? $placeCategory["icon"] : "fas fa-check", + "alias" => "place-category-" . $placeCategory['id'], + "is_selected" => false, + "order_number_mapping" => null + ]; + } + + $response = array_merge($responsePlaceCategories, $responsePlaces); + $response = [ + 'status' => true, + 'data' => $response, + ]; + + } 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); + } + + + public function getCategoriesInList($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $placeCategoryRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $placeCategoryData = $this->propertyPlaceCategoryRepository->findByCriteria($placeCategoryRequest); + $placeCategories = collect($placeCategoryData); + + $categoryTriesRequest = [ + 'parent_id' => $params['place_category_id'], + 'placeCategories' => $placeCategories + ]; + $categoryArray = $this->categoryTriesSingleArray($categoryTriesRequest); + $categoryIds = collect($categoryArray)->keyBy('id')->keys()->all(); + + $parentCategory = $placeCategories->where('id', $params['place_category_id'])->first(); + $responseData['category'] = [ + 'id' => $parentCategory['id'], + 'name' => $parentCategory['name'], + 'language_key' => $parentCategory['language_key'], + ]; + + $placeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'whereIn' => [ + ['field' => 'place_category_id', 'value' => $categoryIds] + ], + 'with' => [ + 'propertyPlaceCategory', 'propertyPlaceWorkingHour', + 'propertyPlacePhotoMapping.propertyPlacePhoto', + 'propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFact', + 'propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFactTitle' + ] + ]; + $placeData = $this->propertyPlaceRepository->findByCriteria($placeRequest); + $placeData = $placeData ? $placeData : []; + + + $responseData['places'] = collect($placeData)->map(function ($value) { + $return = $value; + + $return['photos'] = collect($value['property_place_photo_mapping'])->map(function ($photoMap) { + return isset($photoMap['property_place_photo']) ? $photoMap['property_place_photo']['photoUrl'] : null; + })->values()->all(); + + $return['default_photo'] = collect($value['property_place_photo_mapping'])->map(function ($photoMap) { + return isset($photoMap['property_place_photo']) ? $photoMap['property_place_photo']['photoUrl'] : null; + })->values()->first(); + + unset($return['property_place_photo_mapping']); + $return['slug'] = Str::slug($return['name'], $separator = '-', $language = 'en'); + + $factMappingArray = []; + foreach ($return['property_place_fact_mapping'] as $factMapping) { + + $factMappingArray[$factMapping['property_place_fact_title_fact_mapping']['place_fact_title_id']]['category'] = [ + 'id' => $factMapping['property_place_fact_title_fact_mapping']['place_fact_title']['id'], + 'name' => $factMapping['property_place_fact_title_fact_mapping']['place_fact_title']['name'], + 'language_key' => $factMapping['property_place_fact_title_fact_mapping']['place_fact_title']['language_key'], + ]; + + $factMappingArray[$factMapping['property_place_fact_title_fact_mapping']['place_fact_title_id']]['fact'][$factMapping['property_place_fact_title_fact_mapping']['place_fact_id']] = $factMapping['property_place_fact_title_fact_mapping']['place_fact']; + } + + unset($return['property_place_fact_mapping']); + $return['fact_mapping'] = $factMappingArray; + + return $return; + })->values()->all(); + + $response = [ + 'status' => true, + 'data' => $responseData, + ]; + + } 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); + } + + public function getPlace($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $placeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['place_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyPlaceCategory', 'propertyPlaceWorkingHour', 'propertyPlacePhotoMapping.propertyPlacePhoto', 'propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFact', 'propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFactTitle'], + 'firstRow' => true + ]; + $placeData = $this->propertyPlaceRepository->findByCriteria($placeRequest); + + if (!$placeData) { + throw new Exception('api-unknown_error'); + } + $return = $placeData; + + $return['facts'] = collect($placeData['property_place_fact_mapping'])->map(function ($factMap) { + + return [ + + 'id' => $factMap['id'], + 'property_place_fact_title_fact_mapping_id' => $factMap['property_place_fact_title_fact_mapping']['id'], + 'place_fact_id' => $factMap['property_place_fact_title_fact_mapping']['place_fact']['id'], + 'place_fact_name' => $factMap['property_place_fact_title_fact_mapping']['place_fact']['name'], + 'place_fact_language_key' => $factMap['property_place_fact_title_fact_mapping']['place_fact']['language_key'], + 'place_fact_icon' => $factMap['property_place_fact_title_fact_mapping']['place_fact']['icon'], + 'place_fact_title_id' => $factMap['property_place_fact_title_fact_mapping']['place_fact_title']['id'], + 'place_fact_title_name' => $factMap['property_place_fact_title_fact_mapping']['place_fact_title']['name'], + 'place_fact_title_language_key' => $factMap['property_place_fact_title_fact_mapping']['place_fact_title']['language_key'], + 'place_fact_title_language_icon' => $factMap['property_place_fact_title_fact_mapping']['place_fact_title']['icon'], + ]; + })->values()->all(); + + + $return['photos'] = collect($placeData['property_place_photo_mapping'])->map(function ($photoMap) { + return isset($photoMap['property_place_photo']) ? $photoMap['property_place_photo']['photoUrl'] : null; + })->values()->all(); + $return['default_photo'] = collect($placeData['property_place_photo_mapping'])->map(function ($photoMap) { + return isset($photoMap['property_place_photo']) ? $photoMap['property_place_photo']['photoUrl'] : null; + })->values()->first(); + unset($return['property_place_photo_mapping']); + unset($return['property_place_fact_mapping']); + $return['slug'] = Str::slug($return['name'], $separator = '-', $language = 'en'); + + $getPlaceCategoryFieldsRequest = [ + 'place_category_id' => $return['place_category_id'], + 'place_id' => $return['id'], + + ]; + $getPlaceCategoryFields = $this->getPlaceCategoryFields($getPlaceCategoryFieldsRequest); + if ($getPlaceCategoryFields['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $placeFields = collect($getPlaceCategoryFields['data'])->filter(function ($value, $key) { + if (strpos($key, '_unit') === false) { + return $value; + } + })->map(function ($value) { + $return = $value; + if ($value['field_element'] == 'multiselect') { + $options = collect($value['options'])->where('option_is_selected', '=', true); + $return['options'] = $options->values()->all(); + $return['field_value'] = $options->map(function ($opt) { + return $opt['option_language_key']; + })->values()->all(); + + } elseif ($value['field_element'] == 'select') { + $options = collect($value['options'])->where('option_is_selected', '', true); + $return['options'] = $options->values()->first(); + $return['field_value'] = $return['options'] ? $return['options']['option_language_key'] : null; + + } elseif ($value['field_element'] == 'slider') { + $return['field_value'] = implode('-', $return['field_value']); + } + + unset($return['options']); + + return $return; + })->values()->all(); + + $return['place_fields'] = $placeFields; + + + $placeCategoryRequest['criteria'] = [['field' => 'status', 'condition' => '=', 'value' => 1]]; + $placeCategoryData = $this->propertyPlaceCategoryRepository->findByCriteria($placeCategoryRequest); + $placeCategories = collect($placeCategoryData); + + $parentCategory = null; + $currentCategoryId = $placeData['place_category_id']; + while (is_null($parentCategory)) { + $currentCategory = $placeCategories->where('id', $currentCategoryId)->first(); + if (is_null($currentCategory['parent_id'])) { + $parentCategory = $currentCategory; + } else { + $currentCategoryId = $currentCategory['parent_id']; + } + } + + $return['category'] = [ + 'id' => $parentCategory['id'], + 'name' => $parentCategory['name'], + 'language_key' => $parentCategory['language_key'], + ]; + + $response = [ + 'status' => true, + 'data' => $return + ]; + + } 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); + } + + public function propertyPlaceCategories($params, $select = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $placeCategoryData = $this->propertyPlaceCategoryRepository->findByCriteria($params, $select); + + $response = [ + 'status' => true, + 'data' => $placeCategoryData, + ]; + + } 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); + } + + public function getPlaceIncludes($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $response = [ + 'status' => true, + 'data' => $this->include, + ]; + + } 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); + } + + +} diff --git a/app/Core/Service/PropertyProductMappingService.php b/app/Core/Service/PropertyProductMappingService.php new file mode 100644 index 0000000..e668ff1 --- /dev/null +++ b/app/Core/Service/PropertyProductMappingService.php @@ -0,0 +1,123 @@ +propertyProductMappingRepository = $propertyProductMappingRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyProductMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $insertData = []; + foreach ($params['products'] as $product){ + $insertData[] = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'product_id' => fillOnUndefined($product, 'product_id'), + 'expiration_date' => fillOnUndefined($product, 'expiration_date'), + 'amount' => fillOnUndefined($product, 'amount'), + 'currency' => fillOnUndefined($product, 'currency'), + 'status' => fillOnUndefined($product, 'status', 1), + 'created_by' => fillOnUndefined($product, 'user_id', 1), + 'updated_by' => fillOnUndefined($product, 'user_id', 1), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + } + + $insertResult = $this->propertyProductMappingRepository->insert($insertData); + if ($insertResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $userData = $insertResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyProductMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyPromotionService.php b/app/Core/Service/PropertyPromotionService.php new file mode 100644 index 0000000..55c35df --- /dev/null +++ b/app/Core/Service/PropertyPromotionService.php @@ -0,0 +1,863 @@ +promotionTypeRepository = $promotionTypeRepository; + $this->propertyPromotionRepository = $propertyPromotionRepository; + $this->propertyPromotionMappingRepository = $propertyPromotionMappingRepository; + $this->propertyRoomRepository = $propertyRoomRepository; + $this->propertyPromotionAddValidator = $propertyPromotionAddValidator; + $this->propertyPromotionUpdateValidator = $propertyPromotionUpdateValidator; + $this->updateRoomRateChannelPromotionValidator = $updateRoomRateChannelPromotionValidator; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyPromotionRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function selectPropertyPromotionMapping($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyPromotionMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPromotionTypeList($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $promotionTypeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'parent_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'parent_id', null)], + + ], + ]; + $promotionType = $this->promotionTypeRepository->findByCriteria($promotionTypeRequest, ['id', 'name', 'language_key', 'parent_id', 'type_code', 'order_number', 'status']); + + $promotionType = $promotionType ? $promotionType : []; + + array_multisort( + array_column($promotionType, 'order_number'), SORT_ASC, + array_column($promotionType, 'name'), SORT_ASC, + $promotionType + ); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $promotionType]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function getPropertyPromotionList($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $propertyId = fillOnUndefined($params, 'property_id'); + $propertyPromotionRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + + ], + ]; + $propertyPromotion = $this->propertyPromotionRepository->findByCriteria($propertyPromotionRequest, + ['id', 'property_id','title', 'promotion_type_id', 'start_date', 'end_date', 'reservation_start_date', 'reservation_end_date', + 'is_time', 'start_time', 'end_time', 'day_before', 'amount', 'min_stay', 'days', 'is_mobile', 'params', 'exclude_dates','detail', 'status'] + ); + + $propertyPromotion = $propertyPromotion ? $propertyPromotion : []; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyPromotion]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function createPropertyPromotion($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + DB::beginTransaction(); + + $promotionTypeRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($params, 'promotion_type_id', null)], + ], + 'firstRow' => 1 + ]; + $promotionType = $this->promotionTypeRepository->findByCriteria($promotionTypeRequest, ['id', 'name', 'language_key', 'parent_id', 'type_code', 'order_number', 'status']); + + + if (!$promotionType) { + throw new Exception('api-unknown_error'); + } + + + $params['start_date'] = fillOnUndefined($params, 'start_date', null) != null ? Carbon::parse($params['start_date'])->format('Y-m-d') : null; + $params['end_date'] = fillOnUndefined($params, 'start_date', null) != null ? Carbon::parse($params['end_date'])->format('Y-m-d') : null; + $params['reservation_start_date'] = fillOnUndefined($params, 'start_date', null) != null ? Carbon::parse($params['reservation_start_date'])->format('Y-m-d') : null; + $params['reservation_end_date'] = fillOnUndefined($params, 'start_date', null) != null ? Carbon::parse($params['reservation_end_date'])->format('Y-m-d') : null; + $params['is_time'] = fillOnUndefined($params, 'is_time', null) != null ? 1 : null; + $params['start_time'] = fillOnUndefined($params, 'start_time', null) != null ? Carbon::parse($params['start_time'])->format('H:i:s') : null; + $params['end_time'] = fillOnUndefined($params, 'end_time', null) != null ? Carbon::parse($params['end_time'])->format('H:i:s') : null; + $params['min_stay'] = fillOnUndefinedAndEmpty($params, 'min_stay'); + + $validationResult = $this->propertyPromotionAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $createDays = fillOnUndefined($params, 'days', null) != null ? json_encode($params['days']) : null; + $excludeDates = fillOnUndefined($params, 'exclude_dates', null) != null ? json_encode($params['exclude_dates']) : null; + $createParams = fillOnUndefined($params, 'params', null) != null ? json_encode($params['params']) : null; + $createDetail = fillOnUndefined($params, 'detail', null) != null ? json_encode($params['detail']) : null; + + + $dayBefore = fillOnUndefined($params, 'day_before'); + if ($promotionType['type_code'] == 'LST') { + $dayBefore = fillOnUndefined($params, 'last_minute'); + } + + $createData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'promotion_type_id' => fillOnUndefined($params, 'promotion_type_id'), + 'title' => fillOnUndefined($params, 'title'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + 'reservation_start_date' => fillOnUndefined($params, 'reservation_start_date'), + 'reservation_end_date' => fillOnUndefined($params, 'reservation_end_date'), + 'is_time' => fillOnUndefined($params, 'is_time'), + 'start_time' => fillOnUndefined($params, 'start_time'), + 'end_time' => fillOnUndefined($params, 'end_time'), + 'day_before' => $dayBefore, + 'amount' => fillOnUndefined($params, 'amount'), + 'min_stay' => fillOnUndefinedAndEmpty($params, 'min_stay'), + 'days' => $createDays, + 'exclude_dates' => $excludeDates, + 'is_mobile' => fillOnUndefined($params, 'is_mobile'), + 'params' => $createParams, + 'detail' => $createDetail, + 'status' => 1, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + + $createResult = $this->propertyPromotionRepository->create($createData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResult['data']]; + + DB::commit(); + } catch (ApiErrorException $e) { + db::rollBack(); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getRoomRateChannelPromotion($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyRoomType', 'propertyRoomRateMapping.propertyRoomRateChannel.propertyRoomRateChannelPromotion', 'propertyRoomRateMapping.propertyRoomRate'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $roomData = $this->propertyRoomRepository->findByCriteria($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'exclude_occupancy', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number']); + + $propertyId = fillOnUndefined($params, 'property_id'); + $propertyPromotionRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + + ], + ]; + $propertyPromotions = $this->propertyPromotionRepository->findByCriteria($propertyPromotionRequest, ['id', 'property_id', 'promotion_type_id', 'title', 'start_date', 'end_date', 'is_time', 'start_time', 'end_time', 'reservation_start_date', 'reservation_end_date', 'day_before', 'amount', 'min_stay', 'is_mobile', 'days', 'params','exclude_dates', 'detail', 'status']); + + + $return = []; + + foreach ($roomData as $room) { + + if ($room['property_room_rate_mapping']) { + $item = $room; + $item['exclude_occupancy'] = json_decode($room['exclude_occupancy']); + $roomRateMappings = $room['property_room_rate_mapping']; + $mapping = []; + $addedMapping = 0; + foreach ($roomRateMappings as $roomRateMapping) { + $propertyRoomRateChannel = null; + if (isset($roomRateMapping['property_room_rate_channel'])) { + $propertyRoomRateChannel = collect($roomRateMapping['property_room_rate_channel']) + ->where('channel_id', '=', $params['channel_id']) + ->where('room_rate_mapping_id', '=', $roomRateMapping['id']) + ->first(); + } + + $mappingStatus = false; + $propertyPromotionArray = []; + if ($propertyRoomRateChannel) { + if ($propertyRoomRateChannel['status'] == 1) { + + + $mappingStatus = true; + foreach ($propertyPromotions as $promotion) { + $propertyRoomRateChannelPromotion = []; + if (isset($propertyRoomRateChannel['property_room_rate_channel_promotion'])) { + + + $propertyRoomRateChannelPromotion = collect($propertyRoomRateChannel['property_room_rate_channel_promotion']) + ->where('property_promotion_id', '=', $promotion['id']) + ->where('room_rate_channel_mapping_id', '=', $propertyRoomRateChannel['id']) + ->where('status', '=', 1) + ->values()->toArray(); + } + + $propertyPromotionArray[] = [ + 'id' => $promotion['id'], + 'property_id' => $promotion['property_id'], + 'promotion_type_id' => $promotion['promotion_type_id'], + 'title' => $promotion['title'], + 'start_date' => $promotion['start_date'], + 'end_date' => $promotion['end_date'], + 'reservation_start_date' => $promotion['reservation_start_date'], + 'reservation_end_date' => $promotion['reservation_end_date'], + 'day_before' => $promotion['day_before'], + 'is_time' => $promotion['is_time'], + 'start_time' => $promotion['start_time'], + 'end_time' => $promotion['end_time'], + 'amount' => $promotion['amount'], + 'min_stay' => $promotion['min_stay'], + 'is_mobile' => $promotion['is_mobile'] ? true : false, + 'days' => $promotion['days'], + 'params' => $promotion['params'], + 'excludeDatesArray' => $promotion['excludeDatesArray'], + 'detail' => $promotion['detail'], + 'promotion_status' => $promotion['status'], + 'is_selected' => $propertyRoomRateChannelPromotion ? true : false, + ]; + } + } + } + $roomRateMapping['name'] = $roomRateMapping['property_room_rate']['name']; + $roomRateMapping['is_selected'] = $mappingStatus; + $roomRateMapping['has_date'] = $propertyRoomRateChannel['has_date'] == 0 ? false : true; + $roomRateMapping['end_date'] = $propertyRoomRateChannel['end_date']; + $roomRateMapping['start_date'] = $propertyRoomRateChannel['start_date']; + $roomRateMapping['room_rate_channel_mapping_id'] = $propertyRoomRateChannel['id']; + $roomRateMapping['room_rate_channel_promotion'] = $propertyPromotionArray; + + unset($roomRateMapping['property_room_rate']); + unset($roomRateMapping['property_room_rate_channel']); + if ($mappingStatus) { + $addedMapping++; + $mapping[] = $roomRateMapping; + } + } + $item['property_room_rate_mapping'] = $mapping; + if ($addedMapping > 0) { + $return[] = $item; + } + } + + } + + $collection = collect($return); + $sorted = $collection->sortBy('id')->values()->all(); + + $response = [ + 'status' => true, + 'data' => $sorted, + ]; + + } 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); + } + + public function updatePropertyPromotion($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + + $propertyPromotionRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['promotion_id']], + + ], + 'firstRow' => 1, + ]; + $propertyPromotion = $this->propertyPromotionRepository->findByCriteria($propertyPromotionRequest, ['id', 'property_id', 'promotion_type_id', 'title','start_date', 'end_date', 'reservation_start_date', 'reservation_end_date', 'day_before', 'amount', 'min_stay', 'days', 'params', 'detail', 'status']); + + if (!$propertyPromotion) { + throw new Exception('api-unknown_error'); + } + + $params['promotion_type_id'] = $propertyPromotion['promotion_type_id']; + $params['end_date'] = fillOnUndefined($params, 'start_date', null) != null ? Carbon::parse($params['end_date'])->format('Y-m-d') : null; + $params['reservation_start_date'] = fillOnUndefined($params, 'start_date', null) != null ? Carbon::parse($params['reservation_start_date'])->format('Y-m-d') : null; + $params['reservation_end_date'] = fillOnUndefined($params, 'start_date', null) != null ? Carbon::parse($params['reservation_end_date'])->format('Y-m-d') : null; + $params['is_time'] = fillOnUndefined($params, 'is_time', null) != null ? 1 : null; + $params['start_time'] = fillOnUndefined($params, 'start_time', null) != null ? Carbon::parse($params['start_time'])->format('H:i:s') : null; + $params['end_time'] = fillOnUndefined($params, 'end_time', null) != null ? Carbon::parse($params['end_time'])->format('H:i:s') : null; + $params['min_stay'] = fillOnUndefinedAndEmpty($params, 'min_stay'); + + $validationResult = $this->propertyPromotionUpdateValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $createDays = fillOnUndefined($params, 'days', null) != null ? json_encode($params['days']) : null; + $excludeDates = fillOnUndefined($params, 'exclude_dates', null) != null ? json_encode($params['exclude_dates']) : null; + $createParams = fillOnUndefined($params, 'params', null) != null ? json_encode($params['params']) : null; + $createDetail = fillOnUndefined($params, 'detail', null) != null ? json_encode($params['detail']) : null; + + $dayBefore = fillOnUndefined($params, 'day_before'); + if ($propertyPromotion['promotion_type_id'] == 4) { + $dayBefore = fillOnUndefined($params, 'last_minute'); + } + + $promotionUpdateData = + [ + 'title' => fillOnUndefined($params, 'title'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + 'reservation_start_date' => fillOnUndefined($params, 'reservation_start_date'), + 'reservation_end_date' => fillOnUndefined($params, 'reservation_end_date'), + 'is_time' => fillOnUndefined($params, 'is_time'), + 'start_time' => fillOnUndefined($params, 'start_time'), + 'end_time' => fillOnUndefined($params, 'end_time'), + 'day_before' => $dayBefore, + 'amount' => fillOnUndefined($params, 'amount'), + 'min_stay' => fillOnUndefinedAndEmpty($params, 'min_stay'), + 'days' => $createDays, + 'exclude_dates' => $excludeDates, + 'is_mobile' => fillOnUndefined($params, 'is_mobile'), + 'params' => $createParams, + 'detail' => $createDetail, + 'updated_by' => fillOnUndefined($params, 'user_id'), + 'updated_at' => time() + ]; + + $promotionId = fillOnUndefined($params, 'promotion_id'); + + $promotionResult = $this->propertyPromotionRepository->update($promotionId, $promotionUpdateData); + + if ($promotionResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $promotionResult['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + + public function updateRoomRateChannelPromotion($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + $validationResult = $this->updateRoomRateChannelPromotionValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyPromotion.promotionType.parentPromotionType'] + ]; + + $propertyChannelRoomRatePromotionMapping = $this->propertyPromotionMappingRepository->findByCriteria($criteria); + $oldMappingData = collect($propertyChannelRoomRatePromotionMapping); + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'id', 'condition' => '=', 'value' => $params['property_promotion_id']], + ], + 'with' => ['promotionType.parentPromotionType'], + 'firstRow' => 1 + ]; + + $mappedPromotion = $this->propertyPromotionRepository->findByCriteria($criteria); + if (!$mappedPromotion) { + throw new Exception('api-unknown-error'); + } + + $addThisPromotion = [ + "id" => $mappedPromotion['id'], + "property_id" => $mappedPromotion['property_id'], + "promotion_type_id" => $mappedPromotion['promotion_type_id'], + "start_date" => $mappedPromotion['start_date'], + "end_date" => $mappedPromotion['end_date'], + "reservation_start_date" => $mappedPromotion['reservation_start_date'], + "reservation_end_date" => $mappedPromotion['reservation_end_date'], + 'is_time' => $mappedPromotion['is_time'], + 'start_time' => $mappedPromotion['start_time'], + 'end_time' => $mappedPromotion['end_time'], + 'is_mobile' => $mappedPromotion['is_mobile'], + "day_before" => $mappedPromotion['day_before'], + "amount" => $mappedPromotion['amount'], + "min_stay" => $mappedPromotion['min_stay'], + "days" => $mappedPromotion['days'], + "params" => $mappedPromotion['params'], + "detail" => $mappedPromotion['detail'], + "status" => $mappedPromotion['status'], + 'promotion_type_name' => $mappedPromotion['promotion_type']['name'], + 'promotion_type_language_key' => $mappedPromotion['promotion_type']['language_key'], + 'promotion_type_code' => $mappedPromotion['promotion_type']['type_code'], + 'parent_promotion_type_name' => $mappedPromotion['promotion_type']['parent_promotion_type']['name'], + 'parent_promotion_type_language_key' => $mappedPromotion['promotion_type']['parent_promotion_type']['language_key'], + 'parent_promotion_type_code' => $mappedPromotion['promotion_type']['parent_promotion_type']['type_code'], + ]; + + $oldDataMappingCollect = collect($oldMappingData)->map(function ($value) { + return [ + 'id' => $value['id'], + 'property_id' => $value['property_id'], + 'property_promotion_id' => $value['property_promotion_id'], + 'room_rate_channel_mapping_id' => $value['room_rate_channel_mapping_id'], + 'promotion_type_id' => $value['property_promotion']['promotion_type_id'], + 'start_date' => $value['property_promotion']['start_date'], + 'end_date' => $value['property_promotion']['end_date'], + 'is_time' => $value['property_promotion']['is_time'], + 'start_time' => $value['property_promotion']['start_time'], + 'end_time' => $value['property_promotion']['start_time'], + 'is_mobile' => $value['property_promotion']['is_mobile'], + 'reservation_start_date' => $value['property_promotion']['reservation_start_date'], + 'reservation_end_date' => $value['property_promotion']['reservation_end_date'], + 'day_before' => $value['property_promotion']['day_before'], + 'amount' => $value['property_promotion']['amount'], + 'min_stay' => $value['property_promotion']['min_stay'], + 'days' => $value['property_promotion']['days'], + 'promotion_type_name' => $value['property_promotion']['promotion_type']['name'], + 'promotion_type_language_key' => $value['property_promotion']['promotion_type']['language_key'], + 'promotion_type_code' => $value['property_promotion']['promotion_type']['type_code'], + 'parent_promotion_type_name' => $value['property_promotion']['promotion_type']['parent_promotion_type']['name'], + 'parent_promotion_type_language_key' => $value['property_promotion']['promotion_type']['parent_promotion_type']['language_key'], + 'parent_promotion_type_code' => $value['property_promotion']['promotion_type']['parent_promotion_type']['type_code'], + 'mapping_status' => $value['status'] + ]; + }); + + + $haveThisMapping = $oldDataMappingCollect + ->where('property_promotion_id', '=', $params['property_promotion_id']) + ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id']) + ->where('property_id', '=', $params['property_id']); + + + if ($params['is_selected'] == true) { + + // control adding roles ; + + + /*$checkRole = $oldDataMappingCollect + ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id']) + ->where('property_promotion_id', '=', $params['property_promotion_id']) + ->where('property_id', '=', $params['property_id']) + ->where('mapping_status', '=', 1) + ->first(); + + dd($checkRole,$params, $oldDataMappingCollect); + if ($checkRole) { + throw new Exception('enw-booking_engine_promotion-overlapping_error'); + }*/ + + if ($addThisPromotion['parent_promotion_type_code'] == 'ERB' && $addThisPromotion['promotion_type_code'] == 'PRD') { + $checkRoles = $oldDataMappingCollect + ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id']) + ->where('promotion_type_code', '=', $addThisPromotion['promotion_type_code']) + ->where('mapping_status', '=', 1); + + + $overLappingError = null; + foreach ($checkRoles as $checkRole) { + + + + if ($checkRole && ($checkRole['min_stay'] == $addThisPromotion['min_stay'])) { + if ($checkRole['start_date'] <= $addThisPromotion['start_date']) { + if ($checkRole['reservation_start_date'] <= $addThisPromotion['reservation_start_date']) { + //$overLappingError = true; + } elseif ($checkRole['reservation_start_date'] >= $addThisPromotion['reservation_start_date'] + && $addThisPromotion['reservation_end_date'] >= $checkRole['reservation_start_date']) { + $overLappingError = true; + + } + + + } /*elseif ($checkRole['start_date'] >= $addThisPromotion['start_date'] && $addThisPromotion['end_date'] >= $checkRole['start_date']) { + + + if ($checkRole['reservation_start_date'] <= $addThisPromotion['reservation_start_date']) { + $overLappingError = true; + dd($checkRole); + } elseif ($checkRole['reservation_start_date'] >= $addThisPromotion['reservation_start_date'] + && $addThisPromotion['reservation_end_date'] >= $checkRole['reservation_start_date']) { + $overLappingError = true; + } + }*/ + } + } + + if ($overLappingError) { + throw new Exception('enw-booking_engine_promotion-overlapping_error'); + } + } + + if ($addThisPromotion['parent_promotion_type_code'] == 'ERB' && $addThisPromotion['promotion_type_code'] == 'BFD') { + + $checkRole = $oldDataMappingCollect + ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id']) + ->where('promotion_type_code', '=', $addThisPromotion['promotion_type_code']) + ->where('day_before', '=', $addThisPromotion['day_before']) + ->where('min_stay', '=', $addThisPromotion['min_stay']) + ->where('is_mobile', '=', $addThisPromotion['is_mobile']) + ->where('mapping_status', '=', 1) + ->first(); + if ($checkRole) { + throw new Exception('enw-booking_engine_promotion-overlapping_error'); + } + } + + if ($addThisPromotion['promotion_type_code'] == 'LST') { + + $checkRole = $oldDataMappingCollect + ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id']) + ->where('promotion_type_code', '=', $addThisPromotion['promotion_type_code']) + ->where('min_stay', '=', $addThisPromotion['min_stay']) + ->where('is_time', '=', $addThisPromotion['is_time']) + ->where('is_mobile', '=', $addThisPromotion['is_mobile']) + ->where('amount', '=', $addThisPromotion['amount']) + ->where('mapping_status', '=', 1) + ->first(); + if ($checkRole) { + throw new Exception('enw-booking_engine_promotion-overlapping_error'); + } + } + + + if ($addThisPromotion['promotion_type_code'] == 'DSC') { + + $checkRole = $oldDataMappingCollect + ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id']) + ->where('promotion_type_code', '=', $addThisPromotion['promotion_type_code']) + ->where('min_stay', '=', $addThisPromotion['min_stay']) + ->where('is_time', '=', $addThisPromotion['is_time']) + ->where('is_mobile', '=', $addThisPromotion['is_mobile']) + ->where('amount', '=', $addThisPromotion['amount']) + ->where('mapping_status', '=', 1) + ->first(); + + //dd($addThisPromotion,$checkRole, $params['room_rate_channel_mapping_id'],$oldDataMappingCollect); + if ($checkRole) { + throw new Exception('enw-booking_engine_promotion-overlapping_error'); + } + + } + + + if ($haveThisMapping->first()) { + $thisMapping = $haveThisMapping->first(); + $updateData = [ + 'status' => 1, + 'updated_by' => fillOnUndefined($params, 'user_id'), + 'updated_at' => Carbon::now()->timestamp, + ]; + $updateThis = $this->propertyPromotionMappingRepository->update($thisMapping['id'], $updateData); + if ($updateThis['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } else { + + $insertData = []; + $insertData[] = [ + 'property_id' => $params['property_id'], + 'room_rate_channel_mapping_id' => $params['room_rate_channel_mapping_id'], + 'property_promotion_id' => $params['property_promotion_id'], + 'status' => 1, + 'created_by' => fillOnUndefined($params, 'user_id'), + 'updated_by' => fillOnUndefined($params, 'user_id'), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + if ($insertData) { + $createResult = $this->propertyPromotionMappingRepository->insert($insertData); + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + } + + + // Pasife alma işlemi + if ($params['is_selected'] == false) { + $deleteThis = collect($propertyChannelRoomRatePromotionMapping) + ->where('property_promotion_id', '=', $params['property_promotion_id']) + ->where('room_rate_channel_mapping_id', '=', $params['room_rate_channel_mapping_id']) + ->where('property_id', '=', $params['property_id']) + ->keyBy('id')->keys()->toArray(); + + + if ($deleteThis) { + $updateData = [ + 'status' => 0, + 'updated_by' => fillOnUndefined($params, 'user_id'), + 'updated_at' => Carbon::now()->timestamp, + ]; + $deleteThisArray = $this->propertyPromotionMappingRepository->update($deleteThis, $updateData); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + } + + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + db::rollBack(); + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deletePropertyPromotion($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + DB::beginTransaction(); + + try { + + $propertyPromotionRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['promotion_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + + ] + ]; + $propertyPromotion = $this->propertyPromotionRepository->findByCriteria($propertyPromotionRequest); + if (!$propertyPromotion) { + throw new Exception('No promotion found.'); + } + + $propertyPromotionDeleteIds = $propertyPromotion ? collect($propertyPromotion)->pluck('id')->toArray() : null; + + + $propertyPromotionMappingRequest = [ + 'criteria' => [ + ['field' => 'property_promotion_id', 'condition' => '=', 'value' => $params['promotion_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + + ] + ]; + $propertyPromotionMapping = $this->propertyPromotionMappingRepository->findByCriteria($propertyPromotionMappingRequest, ['id']); + $propertyPromotionMappingDeleteIds = $propertyPromotionMapping ? collect($propertyPromotionMapping)->pluck('id')->toArray() : null; + + if(!empty($propertyPromotionMappingDeleteIds)) { + $propertyPromotionMappingDelete = $this->propertyPromotionMappingRepository->destroy($propertyPromotionMappingDeleteIds); + if ($propertyPromotionMappingDelete['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + $propertyPromotionDelete = $this->propertyPromotionRepository->destroy($propertyPromotionDeleteIds); + + if ($propertyPromotionDelete['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + + if($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return output($response); + + } + +} diff --git a/app/Core/Service/PropertyQuickPricingService.php b/app/Core/Service/PropertyQuickPricingService.php new file mode 100644 index 0000000..61ba58c --- /dev/null +++ b/app/Core/Service/PropertyQuickPricingService.php @@ -0,0 +1,130 @@ +propertyQuickPricingMappingRepository = $propertyQuickPricingMappingRepository; + $this->propertyQuickPricingCreateValidator = $propertyQuickPricingCreateValidator; + } + + public function createPropertyQuickPricingMapping($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyQuickPricingCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = [ + "property_id" => fillOnUndefined($param, "property_id"), + "channel_id" => fillOnUndefined($param, "channel_id"), + "room_rate_channel_mapping_id" => fillOnUndefined($param, "room_rate_channel_mapping_id"), + "action_type" => fillOnUndefined($param, "action_type"), + "price_type" => fillOnUndefined($param, "price_type", 'PER'), + "price_value" => fillOnUndefined($param, "price_value"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $insertDataResult = $this->propertyQuickPricingMappingRepository->create($insertData); + if ($insertDataResult['status'] != 'success') { + throw new Exception($insertDataResult['message']); + } + + $insertDataResult = $insertDataResult["data"]; + + $response = [ + 'status' => true, + 'data' => $insertDataResult, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = 'api-unknown_error'; + } + + return output($response); + } + + public function selectPropertyQuickPricingMapping($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyQuickPricingMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function deletePropertyQuickPricingMapping($deleteThisId) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $deleteThisId = is_array($deleteThisId) ? $deleteThisId : [$deleteThisId]; + $deleteThisArray = $this->propertyQuickPricingMappingRepository->destroy($deleteThisId); + + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + +} diff --git a/app/Core/Service/PropertyRoomAvailabilityQueueService.php b/app/Core/Service/PropertyRoomAvailabilityQueueService.php new file mode 100644 index 0000000..646e6ef --- /dev/null +++ b/app/Core/Service/PropertyRoomAvailabilityQueueService.php @@ -0,0 +1,115 @@ +propertyRoomAvailabilityQueueRepository = $propertyRoomAvailabilityQueueRepository; + + + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomAvailabilityQueueRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRoomAvailabilityQueueRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function delete($ids = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteResult = $this->propertyRoomAvailabilityQueueRepository->destroy($ids); + + if ($deleteResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult['data'] // affected row count + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + +} diff --git a/app/Core/Service/PropertyRoomAvailabilityService.php b/app/Core/Service/PropertyRoomAvailabilityService.php new file mode 100644 index 0000000..d133e04 --- /dev/null +++ b/app/Core/Service/PropertyRoomAvailabilityService.php @@ -0,0 +1,802 @@ +propertyRoomAvailabilityRepository = $propertyRoomAvailabilityRepository; + $this->propertyRoomAvailabilityAddValidator = $propertyRoomAvailabilityAddValidator; + $this->propertyRoomAvailabilityUpdateValidator = $propertyRoomAvailabilityUpdateValidator; + $this->propertyRoomAvailabilityDeleteValidator = $propertyRoomAvailabilityDeleteValidator; + $this->propertyRoomAvailabilityBulkUpdateValidator = $propertyRoomAvailabilityBulkUpdateValidator; + $this->propertyRoomAvailabilityBulkInsertValidator = $propertyRoomAvailabilityBulkInsertValidator; + $this->propertyRoomService = $propertyRoomService; + + $this->propertyChannelRepository = $propertyChannelRepository; + $this->propertyRoomRateMappingRepository = $RoomAvailabilityMappingRepository; + $this->propertyRepository = $propertyRepository; + $this->propertyRoomConnectedRepository = $propertyRoomConnectedRepository; + + + } + + + public function bulkUpdate($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $startDate = Carbon::parse($params['start_date']); + $endDate = Carbon::parse($params['end_date']); + $diffInDays = $startDate->diffInDays($endDate); + $availabilityTypeId = fillOnUndefined($params, 'availability_type_id', 1); + $propertyId = fillOnUndefined($params, 'property_id', 0); + + $includeDays = fillOnUndefined($params, 'include_days', []); + $insertData = []; + + $validationResult = $this->propertyRoomAvailabilityBulkInsertValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + if ($params["update_type"] == "availability") { + $updateColumn = "availability"; + } elseif ($params["update_type"] == "room_stop_sell") { + $updateColumn = "stop_sell"; + } + + + $updateData = [ + $updateColumn => $params['value'], + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'updated_at' => Carbon::now()->timestamp + ]; + + $selectIds = $this->selectRoomRateIds($params); + if ($selectIds['status'] != 'success') { + throw new ApiErrorException('array error'); + } + $selectIds = $selectIds['data']; + + $RoomAvailabilityMappingRequest = [ + 'whereIn' => [ + ['field' => 'id', 'value' => $selectIds['room_rate_mapping_ids']], + ] + ]; + $selectedRoomRateMapping = $this->propertyRoomRateMappingRepository->findByCriteria($RoomAvailabilityMappingRequest); + $selectedRoomRateMapping = $selectedRoomRateMapping ? $selectedRoomRateMapping : []; + $selectedRoomRateMapping = collect($selectedRoomRateMapping); + + + + $oldRoomAvailabilitiesCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'date', 'condition' => '>=', 'value' => $params['start_date']], + ['field' => 'date', 'condition' => '<=', 'value' => $params['end_date']], + ] + ]; + + $oldRoomAvailability = $this->propertyRoomAvailabilityRepository->findbyCriteria($oldRoomAvailabilitiesCriteria); + $oldRoomAvailability = $oldRoomAvailability ? $oldRoomAvailability : []; + $updateThis = []; + $deleteThis = []; + $newAvailabilityArray = []; + + foreach ($params['room_rates'] as $RoomAvailability) { + $startDate = Carbon::parse($params['start_date']); + for ($i = 0; $i <= $diffInDays; $i++) { + if ($availabilityTypeId == 1) { + $checkKey = $RoomAvailability['room_id'] . "||" . $startDate->format('Y-m-d') . "||" . $params['availability_type_id'];; + $newAvailabilityArray[$checkKey] = [ + 'property_id' => $propertyId, + 'property_room_id' => $RoomAvailability['room_id'], + 'availability_type_id' => $availabilityTypeId, + 'date' => $startDate->format('Y-m-d'), + $updateColumn => fillOnUndefined($params, 'value', 0), + ]; + } else { + foreach ($RoomAvailability['room_rate_mapping_id'] as $RoomAvailabilityMapping) { + $checkKey = $RoomAvailability['room_id'] . "|" . $RoomAvailabilityMapping . "|" . $startDate->format('Y-m-d') . "|" . $params['channel_id'] . "|" . $params['availability_type_id']; + $newAvailabilityArray[$checkKey] = [ + 'property_id' => $propertyId, + 'property_room_id' => $RoomAvailability['room_id'], + 'availability_type_id' => $availabilityTypeId, + 'date' => $startDate->format('Y-m-d'), + $updateColumn => fillOnUndefined($params, 'value', 0), + 'room_rate_mapping_id' => $RoomAvailabilityMapping, + ]; + } + } + $startDate = $startDate->addDay(); + } + } + + $oldRoomAvailabilityKeys = []; + foreach ($oldRoomAvailability as $oldAvailability) { + $oldAvailabilityKey = $oldAvailability['property_room_id'] . "|" . $oldAvailability['room_rate_mapping_id'] . "|" . $oldAvailability['date'] . "|" . $oldAvailability['channel_id'] . "|" . $oldAvailability['availability_type_id']; + $oldAvailability[$updateColumn] = isset($newAvailabilityArray[$oldAvailabilityKey]) ? $newAvailabilityArray[$oldAvailabilityKey][$updateColumn] : $oldAvailability[$updateColumn]; + + $oldRoomAvailabilityKeys[$oldAvailabilityKey][] = $oldAvailability; + } + + //Sistemdeki benzer id listesi, mysql null unique index hatası için + foreach ($oldRoomAvailabilityKeys as $availabilityKey => $oldRoomAvailability) { + + if(count($oldRoomAvailability) > 1) { + $oldRoomAvailabilityKeys[$availabilityKey] = last($oldRoomAvailability); + $otherAvailabilityRowIds = collect($oldRoomAvailability)->whereNotIn('id', [$oldRoomAvailabilityKeys[$availabilityKey]['id']])->pluck('id')->toArray(); + $oldRoomAvailabilityKeys[$availabilityKey] = last($oldRoomAvailability); + $oldRoomAvailabilityKeys[$availabilityKey]['otherIds'] = $otherAvailabilityRowIds; + } else { + $oldRoomAvailabilityKeys[$availabilityKey] = reset($oldRoomAvailability); + } + + } + + $quotaCollection = collect($oldRoomAvailabilityKeys) + ->whereIn('availability_type_id', [0, 3]); + + foreach ($params['room_rates'] as $RoomAvailability) { + $startDate = Carbon::parse($params['start_date']); + + for ($i = 0; $i <= $diffInDays; $i++) { + + if (!in_array($startDate->shortEnglishDayOfWeek, $includeDays)) { + $startDate = $startDate->addDay(); + continue; + } + + + $insertDataItem = [ + 'property_id' => $propertyId, + 'property_room_id' => $RoomAvailability['room_id'], + 'availability_type_id' => $availabilityTypeId, + 'date' => $startDate->format('Y-m-d'), + $updateColumn => fillOnUndefined($params, 'value', 0), + 'status' => fillOnUndefined($params, "status", 1), + 'created_by' => fillOnUndefined($params, "user_id", 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + if ("update_type" == "availability") { + if ($availabilityTypeId == 1 || $availabilityTypeId == 3) { + $poolKey = $RoomAvailability['room_id'] . "||" . $startDate->format('Y-m-d') . "||1"; + $poolAvailability = isset($oldRoomAvailabilityKeys[$poolKey]['availability']) ? $oldRoomAvailabilityKeys[$poolKey]['availability'] : 0; + $totalGuaranteedQuota = $quotaCollection + ->where('date', '=', $startDate->format('Y-m-d')) + ->where('property_room_id', '=', $RoomAvailability['room_id']) + ->where('availability_type_id', '=', 3) + ->sum('availability'); + if ($totalGuaranteedQuota > $poolAvailability) { + throw new ApiErrorException(" $propertyId - $RoomAvailability[room_id] - " . $startDate->format('Y-m-d') . " (" . $totalGuaranteedQuota . " < " . $poolAvailability . " ) Guaranteed availability total cannot be higher than Pool availability "); + } + } + } + + + if ($availabilityTypeId == 1) { + $insertDataItem['room_rate_mapping_id'] = null; + $checkKey = $RoomAvailability['room_id'] . "||" . $startDate->format('Y-m-d') . "||" . $params['availability_type_id'];; + if (isset($oldRoomAvailabilityKeys[$checkKey])) { + $updateThis[] = $oldRoomAvailabilityKeys[$checkKey]['id']; + } else { + $insertData[] = $insertDataItem; + } + + if(isset($oldRoomAvailabilityKeys[$checkKey]['otherIds'])) { + foreach ($oldRoomAvailabilityKeys[$checkKey]['otherIds'] as $otherId) { + $deleteThis[] = $otherId; + } + } + + } else { + $insertDataItem['channel_id'] = fillOnUndefined($params, 'channel_id', 1); + foreach ($RoomAvailability['room_rate_mapping_id'] as $RoomAvailabilityMapping) { + $insertDataItem['room_rate_mapping_id'] = $RoomAvailabilityMapping; + $checkKey = $RoomAvailability['room_id'] . "|" . $RoomAvailabilityMapping . "|" . $startDate->format('Y-m-d') . "|" . $params['channel_id'] . "|" . $params['availability_type_id']; + if (isset($oldRoomAvailabilityKeys[$checkKey])) { + $updateThis[] = $oldRoomAvailabilityKeys[$checkKey]['id']; + } else { + $insertData[] = $insertDataItem; + } + } + + } + $startDate = $startDate->addDay(); + } + } + + + + + if ($deleteThis) { + $destroyResult = $this->propertyRoomAvailabilityRepository->destroy($deleteThis); + if ($destroyResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + if ($updateThis) { + $thisUpdateArrayChunk = array_chunk($updateThis, 1000); + foreach ($thisUpdateArrayChunk as $updateIds) { + $data = $this->propertyRoomAvailabilityRepository->updateWhereIn($updateIds, $updateData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + + $thisInsertArrayChunk = array_chunk($insertData, 1000); + foreach ($thisInsertArrayChunk as $insertThis) { + $data = $this->propertyRoomAvailabilityRepository->insert($insertThis); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + if (isset($data) && $data['status'] != 'success') { + throw new ApiErrorException($data['message']); + } + + //Connected Room Case + $roomAvailabilityUpdateForConnectedRoomParams = [ + 'property_id' => $params['property_id'], + 'channel_id' => $params['channel_id'], + 'availability_type_id' => [$availabilityTypeId], + 'startDate' => $params['start_date'], + 'endDate' => $params['end_date'] + ]; + + $this->roomAvailabilityUpdateForConnectedRooms($roomAvailabilityUpdateForConnectedRoomParams); + //Connected Room Case + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectRoomRateIds($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $roomIds = []; + $RoomAvailabilityIds = []; + + + foreach ($params['room_rates'] as $RoomAvailability) { + $roomIds[] = $RoomAvailability['room_id']; + + if (isset($RoomAvailability['room_rate_mapping_id'])) { + foreach ($RoomAvailability['room_rate_mapping_id'] as $RoomAvailabilityMapping) { + $RoomAvailabilityIds[] = $RoomAvailabilityMapping; + } + } + } + $roomIds = array_unique($roomIds); + $RoomAvailabilityIds = array_unique($RoomAvailabilityIds); + $response = [ + 'status' => true, + 'data' => [ + 'room_ids' => $roomIds, + 'room_rate_mapping_ids' => $RoomAvailabilityIds, + ], + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function roomAvailabilityUpdate($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $newMappingArray = $params ? $params : []; + + $validationResult = $this->propertyRoomAvailabilityBulkUpdateValidator->validate($newMappingArray); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $newMappingArray = $newMappingArray['availability']; + + + $newMappingCollect = collect($newMappingArray); + $startDate = $newMappingCollect->min('date'); + $endDate = $newMappingCollect->max('date'); + $channelSetupTypes = $this->propertyRoomService->setChannelAvailabilityTypes(['property_id' => $params['property_id'], 'channel_id' => $params['channel_id']]); + if ($channelSetupTypes['status'] != 'success') { + throw new ApiErrorException($channelSetupTypes['message']); + } + $channelSetupTypes = $channelSetupTypes['data']; + $forGeneralId = $channelSetupTypes['forGeneralId']; + $forAvailabilityData = $channelSetupTypes['forAvailabilityData']; + $forPriceData = $channelSetupTypes['forPriceData']; + $channelWorkingAvailabilityType = $channelSetupTypes['channelAvailabilityWorkingType']; + $getPriceDataIds = collect($forPriceData)->keyBy('id')->keys()->toArray(); + $getAvailabilityDataIds = collect($forAvailabilityData)->keyBy('id')->keys()->toArray(); + $getAvailabilityDataIds = array_unique(array_merge($forGeneralId, $getAvailabilityDataIds)); + + $availabilityRequest = [ + 'property_id' => $params['property_id'], + 'start_date' => $startDate, + 'end_date' => $endDate, + 'channel_id' => $params['channel_id'], + 'availability_type_id' => $getAvailabilityDataIds, + ]; + + $availabilityArray = $this->propertyRoomService->inventoryAvailabilityData($availabilityRequest); + + + $oldMappingArray = []; + foreach ($availabilityArray as $oldAvailability) { + $mergeMappingKey = $oldAvailability['availability_type_id'] . '|' . $oldAvailability['room_id'] . '|' . $oldAvailability['room_rate_mapping_id'] . '|' . $oldAvailability['channel_id'] . '|' . $oldAvailability['date']; + $oldMappingArray[$oldAvailability['availability_type_id'] . '|' . $oldAvailability['room_id'] . '|' . $oldAvailability['room_rate_mapping_id'] . '|' . $oldAvailability['channel_id'] . '|' . $oldAvailability['date']] = [ + 'id' => $oldAvailability['id'], + 'room_id' => $oldAvailability['room_id'], + 'room_rate_mapping_id' => $oldAvailability['room_rate_mapping_id'], + 'availability_type_id' => $oldAvailability['availability_type_id'], + 'channel_id' => $oldAvailability['channel_id'], + 'date' => $oldAvailability['date'], + 'availability' => isset($newMappingArray[$mergeMappingKey]['availability']) ? $newMappingArray[$mergeMappingKey]['availability'] : $oldAvailability['availability'], + 'stop_sell' => $oldAvailability['stop_sell'], + 'old_availability' => $oldAvailability['availability'], + 'otherIds' => isset($oldAvailability['otherIds']) ? $oldAvailability['otherIds'] : [], + ]; + } + $quotaCollection = collect($oldMappingArray) + ->whereIn('availability_type_id', [0, 3]); + + $deleteThis = []; + $insertThis = []; + foreach ($newMappingArray as $key => $newMapping) { + + $channelId = $newMappingArray[$key]['room_rate_mapping_id'] != null ? fillOnUndefined($params, 'channel_id') : null; + + // check pool availability overload quota + + + $poolKey = '1|' . $newMapping['room_id'] . '|||' . $newMapping['date']; + $poolAvailability = isset($oldMappingArray[$poolKey]['availability']) ? $oldMappingArray[$poolKey]['availability'] : 0; + + // Check Guaranteed Availability - Pool Availability + if ($channelWorkingAvailabilityType == 3) { + $totalGuaranteedQuota = $quotaCollection + ->where('date', '=', $newMapping['date']) + ->where('room_id', '=', $newMapping['room_id']) + ->where('availability_type_id', '=', 3) + ->sum('availability'); + if ($totalGuaranteedQuota > $poolAvailability) { + throw new ApiErrorException('Guaranteed availability total cannot be higher than Pool availability'); + } + } + + if (isset($newMapping['availability']) && $newMapping['availability'] === "") { + $newMapping['availability'] = 0; + $newMappingArray[$key]['availability'] = 0; + } + + if (isset($newMapping['availability']) && $newMapping['availability'] === "") { + if (isset($oldMappingArray[$key])) { + $deleteThis[] = $oldMappingArray[$key]['id']; + } + } else { + + if (isset($newMappingArray[$key]['availability'])) { + $insertItemAvailability = $newMappingArray[$key]['availability']; + } elseif (isset($oldMappingArray[$key]['availability'])) { + $insertItemAvailability = $oldMappingArray[$key]['availability']; + } else { + $insertItemAvailability = 0; + } + + + if (isset($newMappingArray[$key]['stop_sell'])) { + $insertItemStopSell = $newMappingArray[$key]['stop_sell']; + } elseif (isset($oldMappingArray[$key]['stop_sell'])) { + $insertItemStopSell = $oldMappingArray[$key]['stop_sell']; + } else { + $insertItemStopSell = 0; + } + + $insertItem = [ + 'property_id' => fillOnUndefined($params, 'property_id', 0), + 'property_room_id' => fillOnUndefined($newMappingArray[$key], 'room_id', 0), + 'room_rate_mapping_id' => $newMappingArray[$key]['room_rate_mapping_id'], + 'channel_id' => $channelId, + 'availability_type_id' => fillOnUndefined($newMappingArray[$key], 'setup_type_id', null), + 'date' => fillOnUndefined($newMappingArray[$key], 'date', 0), + 'availability' => $insertItemAvailability, + 'stop_sell' => $insertItemStopSell, + 'status' => fillOnUndefined($params, "status", 1), + 'created_by' => fillOnUndefined($params, "user_id", 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + + if (isset($oldMappingArray[$key])) { + + if(isset($oldMappingArray[$key]['otherIds'])) { + foreach ($oldMappingArray[$key]['otherIds'] as $otherSimilarId) { + $deleteThis[] = $otherSimilarId; + } + } + + if ($oldMappingArray[$key]['old_availability'] != $insertItem['availability'] || $oldMappingArray[$key]['stop_sell'] != $insertItem['stop_sell']) { + $deleteThis[] = $oldMappingArray[$key]['id']; + + $insertThis[] = $insertItem; + /* + if( $newMappingArray[$key]['availability'] !== 0 ){ + $insertThis[] = $insertItem ; + } + */ + } + } else { + + $insertThis[] = $insertItem; + + /* + if($newMappingArray[$key]['availability'] !== 0){ + + $insertThis[] = $insertItem ; + } + */ + } + } + + } + sort($deleteThis); + + + if ($deleteThis) { + $deleteAvailability = $this->propertyRoomAvailabilityRepository->destroy($deleteThis); + if ($deleteAvailability['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $insertAvailability = $this->propertyRoomAvailabilityRepository->insert($insertThis); + + if ($insertAvailability['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + //Connected Room Case + $roomAvailabilityUpdateForConnectedRoomParams = [ + 'property_id' => $params['property_id'], + 'channel_id' => $params['channel_id'], + 'availability_type_id' => $getAvailabilityDataIds, + 'startDate' => $startDate, + 'endDate' => $endDate, + 'user_id' => fillOnUndefined($params, "user_id", 0) + ]; + + $this->roomAvailabilityUpdateForConnectedRooms($roomAvailabilityUpdateForConnectedRoomParams); + //Connected Room Case + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function dropPropertyChannelGuaranteedAndLimitedAvailability($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $roomAvailabilityCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'date', 'condition' => '>=', 'value' => date('Y-m-d')], + ], + 'whereIn' => [ + ['field' => 'availability_type_id', 'value' => [2, 3]] + ], + ]; + $roomAvailability = $this->propertyRoomAvailabilityRepository->findbyCriteria($roomAvailabilityCriteria, ['id']); + $roomAvailability = $roomAvailability ? $roomAvailability : []; + $deleteThis = array_column($roomAvailability, 'id'); + + if ($deleteThis) { + $destroyResult = $this->propertyRoomAvailabilityRepository->destroy($deleteThis); + if ($destroyResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->propertyRoomAvailabilityRepository->findByCriteria($param, $column); + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRoomAvailabilityRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function roomAvailabilityUpdateForConnectedRooms($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + if(is_null($params['startDate']) || is_null($params['endDate'])) { + $response['status'] = true; + return $response; + } + + $diffInDays = Carbon::parse($params['startDate'])->diffInDays(Carbon::parse($params['endDate'])); + + //Connected Room Case + $propertyRoomConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['roomDetail'] + ]; + + + $propertyRoomConnected = $this->propertyRoomConnectedRepository->findbyCriteria($propertyRoomConnectedCriteria); + $propertyRoomConnected = $propertyRoomConnected ? $propertyRoomConnected : []; + + $propertyRoomConnectedGrouped = collect($propertyRoomConnected)->groupBy('room_id')->toArray(); + + $oldRoomAvailabilityCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'date', 'condition' => '>=', 'value' => $params['startDate']], + ['field' => 'date', 'condition' => '<=', 'value' => $params['endDate']], + ] + ]; + + $oldRoomAvailability = $this->propertyRoomAvailabilityRepository->findbyCriteria($oldRoomAvailabilityCriteria); + $oldRoomAvailability = $oldRoomAvailability ? collect($oldRoomAvailability) : []; + + + foreach ($propertyRoomConnectedGrouped as $connectedRoomId => $connectedRooms) { + + $roomDetail = reset($connectedRooms)['room_detail']; + + + if ($roomDetail['is_connected_room_availability'] != 1) { + continue; + } + + //Güncellemede dikkat edilmesi gereken Oda ID leri + $connectedRoomIds = pickItemFromArray('connected_room_id', $connectedRooms); + + //dd($params,$connectedRoomIds ,$oldRoomAvailability); + + for ($i = 0; $i <= $diffInDays; $i++) { + + $date = Carbon::parse($params['startDate'])->addDays($i)->toDateString(); + + + if (empty($oldRoomAvailability)) { + continue; + } + + $issetCurrentRoomAvailability = $oldRoomAvailability + ->where('property_room_id', $connectedRoomId) + ->whereIn('availability_type_id', $params['availability_type_id']) + ->where('date', $date)->first(); + + + $roomAvailabilityFromConnectedRooms = $oldRoomAvailability + ->whereIn('property_room_id', $connectedRoomIds) + ->whereIn('availability_type_id', $params['availability_type_id']) + ->where('date', $date)->sortBy('availability'); + + $roomAvailabilityFromConnectedRoomsIds = $roomAvailabilityFromConnectedRooms->pluck('property_room_id')->toArray(); + + $intersectRooms = array_intersect($roomAvailabilityFromConnectedRoomsIds, $connectedRoomIds); + + if(count($intersectRooms) != count($connectedRoomIds)) { + continue; + } + + $roomAvailabilityFromConnectedRooms = $roomAvailabilityFromConnectedRooms->first(); + $roomAvailabilityFromConnectedRooms = fillOnUndefined($roomAvailabilityFromConnectedRooms, 'availability', 0); + + + //update + if (!empty($issetCurrentRoomAvailability)) { + + $this->propertyRoomAvailabilityRepository->update($issetCurrentRoomAvailability['id'], ['availability' => $roomAvailabilityFromConnectedRooms]); + + } else { + + $insertItem = [ + 'property_id' => $params['property_id'], + 'property_room_id' => $roomDetail['id'], + 'room_rate_mapping_id' => null, + 'channel_id' => !in_array(1,$params['availability_type_id']) ? $params['channel_id'] : null, + 'availability_type_id' => !in_array(1,$params['availability_type_id']) ? reset($params['availability_type_id']) : 1, + 'date' => $date, + 'availability' => $roomAvailabilityFromConnectedRooms, + 'stop_sell' => 0, + 'status' => 1, + 'created_by' => fillOnUndefined($params, "user_id", 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + $this->propertyRoomAvailabilityRepository->insert($insertItem); + + } + + + } + + } + //Connected Room Case + + + $response = [ + 'status' => true, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + +} diff --git a/app/Core/Service/PropertyRoomBedService.php b/app/Core/Service/PropertyRoomBedService.php new file mode 100644 index 0000000..398526f --- /dev/null +++ b/app/Core/Service/PropertyRoomBedService.php @@ -0,0 +1,260 @@ +propertyRoomBedRepository = $propertyRoomBedRepository; + $this->propertyRoomBedAddValidator = $propertyRoomBedAddValidator; + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = + [ + 'room_id' => fillOnUndefined($params, 'room_id', 0), + 'bed_group' => fillOnUndefined($params, 'bed_group', 0), + 'count' => fillOnUndefined($params, 'count', 0), + 'bed_type_id' => fillOnUndefined($params, 'bed_type_id', 0), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "created_by"), + "updated_by" => fillOnUndefined($params, "updated_by"), + ]; + + $userCreateResult = $this->propertyRoomBedRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomBedRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function validate($params){ + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $roomBedGroup = fillOnUndefined($params, 'room_bed_group', []); + $bedGroupIndex = 1; + + foreach ($roomBedGroup as $bedGroup) { + + foreach ($bedGroup as $beds) { + $insertData = + [ + 'room_id' => fillOnUndefined($params, 'room_id', null), + 'bed_group' => $bedGroupIndex, + 'count' => fillOnUndefined($beds, 'bed_type_count', null), + 'bed_type_id' => fillOnUndefined($beds, 'bed_type_id', null), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id", 0), + "updated_by" => fillOnUndefined($params, "user_id", 0) + ]; + $validationResult = $this->propertyRoomBedAddValidator->validate($insertData); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + } + } + $response['status'] = true; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + + } + + return output($response); + + } + + public function addPropertyRoomBed($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $dataValidation = $this->validate($params); + if ($dataValidation['status'] != 'success') { + throw new ApiErrorException($dataValidation['message']); + } + + DB::beginTransaction(); + $deleteRoomBedsCriteria = [ + 'criteria' => [ + ['field' => 'room_id', 'condition' => '=', 'value' => $params['room_id']] + ] + ]; + $deleteRoomBeds = $this->propertyRoomBedRepository->delete($deleteRoomBedsCriteria); + + $roomBedGroup = fillOnUndefined($params, 'room_bed_group', []); + $bedGroupIndex = 1; + + foreach ($roomBedGroup as $bedGroup) { + + foreach ($bedGroup as $beds) { + + $insertData = + [ + 'room_id' => fillOnUndefined($params, 'room_id', null), + 'bed_group' => $bedGroupIndex, + 'count' => fillOnUndefined($beds, 'bed_type_count', null), + 'bed_type_id' => fillOnUndefined($beds, 'bed_type_id', null), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id", 0), + "updated_by" => fillOnUndefined($params, "user_id", 0) + ]; + $userCreateResult = $this->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new ApiErrorException($userCreateResult['message']); + } + } + $bedGroupIndex++; + } + $response = [ + 'status' => true, + + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPropertyRoomBeds($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + if(fillOnUndefined($params, 'room_id', null) == null){ + throw new ApiErrorException(lang('room_id is required')); + } + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'room_id', 'condition' => '=', 'value' => $params['room_id']], + ], + + ]; + + $data = $this->propertyRoomBedRepository->findByCriteria($criteria, ['id', 'room_id', 'bed_group', 'count', 'bed_type_id']); + $data = $data ? $data : [] ; + $collection = collect($data) ; + + $bedGroup = $collection->unique('bed_group')->keyBy('bed_group')->keys()->all(); + + $exportArray = []; + foreach ($bedGroup as $group){ + $groupData = $collection->where('bed_group','=', $group); + $subArray = []; + foreach ($groupData as $data){ + $subArray[] = [ + 'bed_type_id' => $data['bed_type_id'], + 'bed_type_count' => $data['count'], + ]; + } + $exportArray[] = $subArray; + } + + $exportData = [ + 'room_id' => $params['room_id'], + 'room_bed_group' => $exportArray + + ]; + + $response = [ + 'status' => true, + 'data' => $exportData, + ]; + + } 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); + } + + +} diff --git a/app/Core/Service/PropertyRoomBedTypeService.php b/app/Core/Service/PropertyRoomBedTypeService.php new file mode 100644 index 0000000..a18c536 --- /dev/null +++ b/app/Core/Service/PropertyRoomBedTypeService.php @@ -0,0 +1,209 @@ +propertyRoomBedTypeRepository = $propertyRoomBedTypeRepository; + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "name" => fillOnUndefined($param, "name"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->propertyRoomBedTypeRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomBedTypeRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRoomBedTypeRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomBedTypeRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function getPropertyRoomBedTypes($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'with' => [] + ]; + + $data = $this->propertyRoomBedTypeRepository->findByCriteria($criteria, ['id', 'name', 'icon', 'language_key']); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function addPropertyRoomBedTypes($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'with' => [] + ]; + + $data = $this->propertyRoomBedTypeRepository->findByCriteria($criteria, ['id', 'name']); + + $response = [ + 'status' => true, + 'data' => $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); + } + + +} diff --git a/app/Core/Service/PropertyRoomFactMappingService.php b/app/Core/Service/PropertyRoomFactMappingService.php new file mode 100644 index 0000000..e3a3992 --- /dev/null +++ b/app/Core/Service/PropertyRoomFactMappingService.php @@ -0,0 +1,423 @@ +languageService = $languageService; + $this->propertyRoomFactMappingRepository = $propertyRoomFactMappingRepository; + $this->applicationCacheService = $applicationCacheService; + $this->propertyRoomFactMappingAddValidator = $propertyRoomFactMappingAddValidator; + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + + $userCreateResult = $this->propertyRoomFactMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]->toArray(); + + $response['status'] = 1; + $response['data'] = $userData; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomFactMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyRoomFactMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deleteById($param){ + + $userCreateResult = $this->propertyRoomFactMappingRepository->delete(); + + } + + public function removePropertyRoomFactMapping($param = []) + { + + //Todo: validations + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + //Todo: cache datası yeni bir fonksiyon yapılıp herkes oradan geçekecek + + $cacheInfo = $this->applicationCacheService->getApplicationCache([]); + if($cacheInfo['status'] != 'success'){ + throw new ApiErrorException(lang('Cache Required')); + } + $userData = $cacheInfo['data'] ; + + + $factIds = []; + foreach ($param["facts"] as $fact){ + $factIds[] = $fact["id"]; + } + + + $findCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $userData['property_id']], + ], + "whereIn"=> + [ + ["field"=>"fact_id","value"=>$factIds] + ] + ]; + + $factMappingDatas = $this->propertyRoomFactMappingRepository->findByCriteria($findCriteria); + if (!$factMappingDatas) { + throw new ApiErrorException(lang('Fact cannot loaded')); + } + + + // Todo: gönderilen idler ile veritabanında bulunan idler karşılaştırılacak. Uyumsuz data varsa hiç bir şey silinmeden error throw edilecek + + $deleteRowIds = []; + foreach ($factMappingDatas as $factMappingData){ + $ids = []; + $ids[] = $factMappingData["id"]; + $delete = $this->propertyRoomFactMappingRepository->deleteById($ids); + if (!$delete) { + throw new ApiErrorException(lang('cannot delete fact')); + } + + } + $response['status'] = 1; + $response['data'] = []; + + + + } + catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function addPropertyRoomFactMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try + { + $propertyId = $params['property_id']; + $userId = $params['user_id']; + $propertyFactData = $params['data']; + + $addPropertyRoomFactMapping = []; + foreach ($propertyFactData as $key => $param) + { + $addPropertyRoomFactMapping[] = + [ + 'property_id' => $propertyId, + 'fact_id' => fillOnUndefined($param, 'id'), + 'created_by' => $userId, + 'updated_by' => $userId, + ]; + } + + //TODO : $addPropertyRoomFactMapping için validation işlemleri yapılacaktır.(property_id ve fact id inin unique doğrulamasına bakılması unutulmamalı ) + $response = $this->propertyRoomFactMappingRepository->createAll($addPropertyRoomFactMapping); + + if ($response['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + public function updatePropertyRoomFactMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try + { + + $validationResult = $this->propertyRoomFactMappingAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $addPropertyRoomFactMapping = []; + $addFactList = collect($params['property_room_fact']) + ->where('is_selected' ,'=', true); + $addFactIds = $addFactList->keyBy("id")->keys()->toArray(); + + $removeFactList = collect($params['property_room_fact']) + ->where('is_selected' ,'=', false); + + $removeFactIds = $removeFactList->keyBy("id")->keys()->toArray(); + + $checkPropertyMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'room_id', 'condition' => '=', 'value' => $params['room_id']], + ], + "whereIn" => [ + ["field" => "fact_id", "value" => $addFactIds] + ] + ]; + $checkPropertyMappingResponse = $this->select($checkPropertyMappingRequest,['id', 'property_id', 'fact_id']); + + if($checkPropertyMappingResponse['status'] != 'success'){ + throw new ApiErrorException(lang('Mapping data not loaded')); + } + $checkPropertyMappingCollect = collect($checkPropertyMappingResponse['data']); + + foreach ($addFactList->toArray() as $key => $param) + { + + if( + !$checkPropertyMappingCollect->where('property_id', '=', $params['property_id']) + ->where('fact_id', '=', $param['id']) + ->first() + ){ + + $addPropertyRoomFactMapping[] = + [ + 'property_id' => $params['property_id'], + 'room_id' => $params['room_id'], + 'fact_id' => fillOnUndefined($param, 'id'), + 'created_by' => $params['user_id'], + 'updated_by' => $params['user_id'], + ]; + } + } + + $response = $this->propertyRoomFactMappingRepository->createAll($addPropertyRoomFactMapping); + + + if($removeFactIds){ + + $findCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'room_id', 'condition' => '=', 'value' => $params['room_id']], + ], + "whereIn" => + [ + ["field" => "fact_id", "value" => $removeFactIds] + ] + ]; + + $deletePropertyRoomFactMapping = $this->propertyRoomFactMappingRepository->findByCriteria($findCriteria); + + $deleteThisIds = []; + foreach ($deletePropertyRoomFactMapping as $deleteThisItem){ + $deleteThisIds[] = $deleteThisItem['id']; + } + + if($deleteThisIds){ + $this->propertyRoomFactMappingRepository->deleteById($deleteThisIds); + + } + } + + if ($response['status'] != 'success') { + throw new ApiErrorException(lang('Data is not added')) ; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + public function updatePropertyRoomFactIsFeature($params = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try + { + + /*$validationResult = $this->propertyRoomFactMappingAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + + $checkPropertyMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'room_id', 'condition' => '=', 'value' => $params['room_id']], + ['field' => 'fact_id', 'condition' => '=', 'value' => $params['fact_id']] + ], + 'firstRow' => true + ]; + + $checkPropertyMappingResponse = $this->select($checkPropertyMappingRequest,['id', 'property_id', 'room_id', 'fact_id']); + + if($checkPropertyMappingResponse['status'] != 'success'){ + throw new ApiErrorException(lang('Mapping data not loaded')); + } + + if(empty($checkPropertyMappingResponse['data'])){ + throw new ApiErrorException(lang('Mapping data not found')); + } + + $propertyFactMappingId = $checkPropertyMappingResponse['data']['id']; + + $updateData = [ + 'is_feature' => $params['is_feature'], + 'updated_by' => $params['user_id'] + ]; + + $updateResult = $this->update($propertyFactMappingId, $updateData); + + if ($updateResult['status'] != 'success') { + throw new ApiErrorException(lang('Mapping Fact data is not updated.')); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + + } + + +} diff --git a/app/Core/Service/PropertyRoomPhotoMappingService.php b/app/Core/Service/PropertyRoomPhotoMappingService.php new file mode 100644 index 0000000..669c791 --- /dev/null +++ b/app/Core/Service/PropertyRoomPhotoMappingService.php @@ -0,0 +1,254 @@ +languageService = $languageService; + $this->propertyRoomPhotoMappingRepository = $propertyRoomPhotoMappingRepository; + $this->applicationCacheService = $applicationCacheService; + $this->propertyRoomPhotoMappingAddValidator = $propertyRoomPhotoMappingAddValidator; + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + + $userCreateResult = $this->propertyRoomPhotoMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]->toArray(); + + $response['status'] = 1; + $response['data'] = $userData; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomPhotoMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function deleteById($param){ + + $userCreateResult = $this->propertyRoomPhotoMappingRepository->delete(); + + } + + public function updatePropertyRoomPhotoMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try + { + + $validationResult = $this->propertyRoomPhotoMappingAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $addPropertyRoomPhotoMapping = []; + $addPhotoList = collect($params['property_room_photo']) + ->where('is_selected' ,'=', true); + $addPhotoIds = $addPhotoList->keyBy("id")->keys()->toArray(); + + $removePhotoList = collect($params['property_room_photo']) + ->where('is_selected' ,'=', false); + + $removePhotoIds = $removePhotoList->keyBy("id")->keys()->toArray(); + + $checkPropertyMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'room_id', 'condition' => '=', 'value' => $params['room_id']], + ], + "whereIn" => [ + ["field" => "photo_id", "value" => $addPhotoIds] + ] + ]; + $checkPropertyMappingResponse = $this->select($checkPropertyMappingRequest,['id', 'property_id', 'photo_id']); + + if($checkPropertyMappingResponse['status'] != 'success'){ + throw new ApiErrorException(lang('Mapping data not loaded')); + } + $checkPropertyMappingCollect = collect($checkPropertyMappingResponse['data']); + + foreach ($addPhotoList->toArray() as $key => $param) + { + + if( + !$checkPropertyMappingCollect->where('property_id', '=', $params['property_id']) + ->where('photo_id', '=', $param['id']) + ->first() + ){ + + $addPropertyRoomPhotoMapping[] = + [ + 'property_id' => $params['property_id'], + 'room_id' => $params['room_id'], + 'photo_id' => fillOnUndefined($param, 'id'), + 'created_by' => $params['user_id'], + 'updated_by' => $params['user_id'], + ]; + } + } + + $response = $this->propertyRoomPhotoMappingRepository->createAll($addPropertyRoomPhotoMapping); + + if($removePhotoIds){ + + $findCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'room_id', 'condition' => '=', 'value' => $params['room_id']], + ], + "whereIn" => + [ + ["field" => "photo_id", "value" => $removePhotoIds] + ] + ]; + + $deletePropertyRoomPhotoMapping = $this->propertyRoomPhotoMappingRepository->findByCriteria($findCriteria); + + $deleteThisIds = []; + foreach ($deletePropertyRoomPhotoMapping as $deleteThisItem){ + $deleteThisIds[] = $deleteThisItem['id']; + } + + if($deleteThisIds){ + $this->propertyRoomPhotoMappingRepository->deleteById($deleteThisIds); + } + } + + if ($response['status'] != 'success') { + throw new ApiErrorException(lang('Data is not added')) ; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['data'] = ''; + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['data'] = ''; + $response['statusCode'] = 400; + } + + return output($response); + } + + + public function destroy($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $deleteCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'photo_id', 'condition' => '=', 'value' => $params['photo_id']], + ] + ]; + + $deleteData = $this->propertyRoomPhotoMappingRepository->findByCriteria($deleteCriteria, ['id']); + $deleteData = $deleteData ? $deleteData : [] ; + if($deleteData){ + $deleteIds = array_column($deleteData, 'id') ; + $destroyResult = $this->propertyRoomPhotoMappingRepository->destroy($deleteIds); + if ($destroyResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null + ]; + + } 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); + + + } + +} diff --git a/app/Core/Service/PropertyRoomRateChannelMappingService.php b/app/Core/Service/PropertyRoomRateChannelMappingService.php new file mode 100644 index 0000000..0162bc2 --- /dev/null +++ b/app/Core/Service/PropertyRoomRateChannelMappingService.php @@ -0,0 +1,340 @@ +propertyRoomRateChannelMappingRepository = $propertyRoomRateChannelMappingRepository; + $this->propertyRoomRateChannelMappingAddValidator = $propertyRoomRateChannelMappingAddValidator; + $this->propertyRoomRateChannelMappingUpdateValidator = $propertyRoomRateChannelMappingUpdateValidator; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + $this->propertyRoomRepository = $propertyRoomRepository ; + $this->propertyAvailabilityTypeRepository = $propertyAvailabilityTypeRepository ; + $this->propertyChannelMappingService = $propertyChannelMappingService ; + + + } + + + public function addPropertyRoomRateChannelMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + $validateData = + [ + 'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id'), + 'channels' => fillOnUndefined($params, 'channels'), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $validationResult = $this->propertyRoomRateChannelMappingAddValidator->validate($validateData); + + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $deleteCriteria = [ + 'criteria' => [ + ['field' => 'room_rate_mapping_id', 'condition' => '=', 'value' => $validateData['room_rate_mapping_id']], + ] + ]; + + $this->propertyRoomRateChannelMappingRepository->delete($deleteCriteria); + + + foreach ($validateData["channels"] as $fact){ + $insertData = [ + 'property_id' => $params['property_id'] , + 'room_rate_mapping_id' => $validateData['room_rate_mapping_id'] , + 'channel_id' => $fact, + 'status' => 1, + "created_by" => $validateData['created_by'] , + "updated_by" =>$validateData['updated_by'] , + + ]; + + + $userCreateResult = $this->propertyRoomRateChannelMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPropertyRoomRateChannelMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $data = $this->propertyRoomRateChannelMappingRepository->findByCriteria($criteria, ['id', 'room_rate_mapping_id', 'channel_id']); + $data = $data ? $data : []; + $rateChannelMapping = []; + foreach ($data as $mapping){ + $rateChannelMapping[$mapping['channel_id'].'|'.$mapping['room_rate_mapping_id']] = $mapping ; + } + + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ], + 'with' => ['propertyRoomRateMapping.propertyRoomRate'] + ]; + + $data = $this->propertyRoomRepository->findByCriteria($criteria, ['id','property_id','name','room_type_id','max_occupancy','max_adult','max_child','exclude_occupancy','room_size','room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number']); + $roomData = $data ? $data : []; + $channels = [] ; + foreach ($params['channels'] as $channelItem) { + $rooms = [] ; + $channel = $channelItem; + foreach ($roomData as $roomItem) { + $room = $roomItem; + unset($room['property_room_rate_mapping']); + $rates = [] ; + foreach ($roomItem['property_room_rate_mapping'] as $roomRate) { + $rate = $roomRate ; + $checkKey = $channel['id'].'|'.$roomRate['id']; + unset($rate['property_room_rate']) ; + $rate['rate_name'] = $roomRate['property_room_rate']['name']; + $rate['rate_description'] = $roomRate['property_room_rate']['name']; + $rate['min_stay'] = $roomRate['property_room_rate']['min_stay']; + $rate['max_stay'] = $roomRate['property_room_rate']['max_stay']; + $rate['is_selected'] = isset($rateChannelMapping[$checkKey]); + $rates[] = $rate; + } + $room['rates'] = $rates ; + $rooms[] = $room ; + } + $channel['rooms'] = $rooms ; + $channels[] = $channel ; + } + + $response = [ + 'status' => true, + 'data' => $channels, + ]; + + } 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); + } + + public function updatePropertyRoomRateChannelMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + $validateData = + [ + 'room_rate_channel_mapping' => fillOnUndefined($params, 'room_rate_channel_mapping'), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $validationResult = $this->propertyRoomRateChannelMappingUpdateValidator->validate($validateData); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + foreach ($params['room_rate_channel_mapping'] as $mapping) { + $hasDate = isset($mapping['has_date']) ? $mapping['has_date'] : 0 ; + $startDate = isset($mapping['start_date']) ? $mapping['start_date'] : null ; + $endDate = isset($mapping['end_date']) ? $mapping['end_date'] : null ; + + + $checkData = [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $mapping['channel_id']], + ['field' => 'room_rate_mapping_id', 'condition' => '=', 'value' => $mapping['room_rate_mapping_id']], + + ]; + + $checkParams = [ + 'property_id' => $params['property_id'], + 'channel_id' => $mapping['channel_id'], + ] ; + + $checkMappingStatus =$this->propertyChannelMappingService->checkPropertyChannelMapping($checkParams); + if ($checkMappingStatus['status'] != 'success') { + return Exception('api-unknown_error'); + } + + if($mapping['is_selected'] == true){ + $addData = [ + 'property_id' => $params['property_id'] , + 'room_rate_mapping_id' => $mapping['room_rate_mapping_id'] , + 'channel_id' => $mapping['channel_id'] , + 'has_date' => $hasDate , + 'start_date' => $startDate , + 'end_date' => $endDate , + 'status' => 1, + "created_by" => $validateData['created_by'], + "updated_by" =>$validateData['updated_by'], + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + $createStatus = $this->propertyRoomRateChannelMappingRepository->updateOrCreate($checkData, $addData ); + if ($createStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + }else { + + $softDeleteData = [ + 'status' => 0, + 'has_date' => $hasDate , + 'start_date' => $startDate , + 'end_date' => $endDate , + "updated_by" =>$validateData['updated_by'], + 'updated_at' => Carbon::now()->timestamp, + ]; + + $deleteCriteria = ['criteria' => $checkData ]; + $softDelete = $this->propertyRoomRateChannelMappingRepository->updateWhere($deleteCriteria, $softDeleteData ); + if ($softDelete['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->propertyRoomRateChannelMappingRepository->findByCriteria($param, $column); + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectChannelAvailabilityType($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->propertyAvailabilityTypeRepository->findByCriteria($param, $column); + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + +} diff --git a/app/Core/Service/PropertyRoomRateInclusionMappingService.php b/app/Core/Service/PropertyRoomRateInclusionMappingService.php new file mode 100644 index 0000000..03e7441 --- /dev/null +++ b/app/Core/Service/PropertyRoomRateInclusionMappingService.php @@ -0,0 +1,145 @@ +propertyRoomRateInclusionMappingRepository = $propertyRoomRateInclusionMappingRepository; + $this->propertyRoomRateInclusionMappingAddValidator = $propertyRoomRateInclusionMappingAddValidator; + + + } + + + public function addPropertyRoomRateInclusionMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + $validateData = + [ + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'facts' => fillOnUndefined($params, 'facts'), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $validationResult = $this->propertyRoomRateInclusionMappingAddValidator->validate($validateData); + + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $deleteCriteria = [ + 'criteria' => [ + ['field' => 'room_rate_id', 'condition' => '=', 'value' => $validateData['room_rate_id']], + ] + ]; + + $this->propertyRoomRateInclusionMappingRepository->delete($deleteCriteria); + + + foreach ($validateData["facts"] as $fact){ + $insertData = [ + 'room_rate_id' => $validateData['room_rate_id'] , + 'fact_id' => $fact, + 'status' => 1, + "created_by" => $validateData['created_by'] , + "updated_by" =>$validateData['updated_by'] , + + ]; + + + $userCreateResult = $this->propertyRoomRateInclusionMappingRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPropertyRoomRateInclusionMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'room_rate_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'room_rate_id')], + ], + 'with' => ['propertyFact'] + ]; + + $data = $this->propertyRoomRateInclusionMappingRepository->findByCriteria($criteria, ['id', 'room_rate_id', 'fact_id']); + $return = []; + foreach ($data as $item){ + if(isset($item['property_fact'])){ + $row = $item['property_fact']; + $return[] = $row ; + } + } + + $response = [ + 'status' => true, + 'data' => $return, + ]; + + } 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); + } + + + + +} diff --git a/app/Core/Service/PropertyRoomRateMappingService.php b/app/Core/Service/PropertyRoomRateMappingService.php new file mode 100644 index 0000000..e9813be --- /dev/null +++ b/app/Core/Service/PropertyRoomRateMappingService.php @@ -0,0 +1,705 @@ +propertyRoomRateMappingRepository = $propertyRoomRateMappingRepository; + $this->propertyRoomRateMappingSetupRepository = $propertyRoomRateMappingSetupRepository; + $this->propertyRoomRateMappingAddValidator = $propertyRoomRateMappingAddValidator; + $this->propertyRoomRateMappingUpdateValidator = $propertyRoomRateMappingUpdateValidator; + $this->propertyRoomRateMappingDeleteValidator = $propertyRoomRateMappingDeleteValidator; + $this->propertyRoomRateMappingAddWithSetupValidator = $propertyRoomRateMappingAddWithSetupValidator; + $this->propertyRoomRateMappingSetStatusValidator = $propertyRoomRateMappingSetStatusValidator; + $this->propertyRoomRateChannelMappingRepository = $propertyRoomRateChannelMappingRepository; + $this->propertyRoomRateMappingConnectedValidator = $propertyRoomRateMappingConnectedValidator; + + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomRateMappingAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + 'room_id' => fillOnUndefined($params, 'room_id', 0), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id', 0), + 'description' => fillOnUndefined($params, 'description', ''), + 'rack_rate' => fillOnUndefined($params, 'rack_rate', 0), + 'included_occupancy' => fillOnUndefined($params, 'included_occupancy', 0), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "created_by", 0), + "updated_by" => fillOnUndefined($params, "updated_by", 0), + ]; + + $userCreateResult = $this->propertyRoomRateMappingRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomRateMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRoomRateMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomRateMappingRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function addPropertyRoomRateMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $checkRoomRateMappingRequest = [ + 'criteria' => [ + ['field' => 'room_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'room_id')], + ['field' => 'room_rate_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'room_rate_id')], + ], + ]; + + $checkRoomRateMapping = $this->propertyRoomRateMappingRepository->findByCriteria($checkRoomRateMappingRequest); + $checkRoomRateMapping = $checkRoomRateMapping ? $checkRoomRateMapping : null; + + if ($checkRoomRateMapping) { + + throw new ApiErrorException('This data was previously added'); + } + $insertData = + [ + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'description' => fillOnUndefined($params, 'description'), + 'rack_rate' => fillOnUndefined($params, 'rack_rate'), + 'included_occupancy' => fillOnUndefined($params, 'included_occupancy'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + + $userCreateResult = $this->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new ApiErrorException($userCreateResult['message']); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function addPropertyRoomRateMappingWithSetup($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + $validationResult = $this->propertyRoomRateMappingAddWithSetupValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $checkRoomRateMappingRequest = [ + 'criteria' => [ + ['field' => 'room_rate_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'room_rate_id')], + ], + ]; + + $checkRoomRateMapping = $this->propertyRoomRateMappingRepository->findByCriteria($checkRoomRateMappingRequest); + $checkRoomRateMapping = $checkRoomRateMapping ? $checkRoomRateMapping : []; + $oldRoomRateRooms = collect($checkRoomRateMapping)->keyBy('room_id')->keys()->toArray(); + $insertRoomRateMappingSetupData = []; + foreach ($params['room_id'] as $room) { + + if (in_array($room, $oldRoomRateRooms)) { + throw new ApiErrorException('This data was previously added'); + } + $insertRoomRateMappingData = + [ + 'room_id' => $room, + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'rack_rate' => fillOnUndefined($params, 'rack_rate'), + 'included_occupancy' => fillOnUndefined($params, 'included_occupancy'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $insertRoomRateMappingResult = $this->propertyRoomRateMappingRepository->create($insertRoomRateMappingData); + if ($insertRoomRateMappingResult['status'] != 'success') { + throw new ApiErrorException($insertRoomRateMappingResult['message']); + } + + /* $insertRoomRateMapping = $insertRoomRateMappingResult['data']; + + foreach ($params['room_rate_mapping_setup'] as $mappingSetup){ + $insertRoomRateMappingSetupData[] = [ + 'room_rate_mapping_id' => $insertRoomRateMapping['id'] , + 'setup_type' => $mappingSetup['setup_type'], + 'value_type' => $mappingSetup['value_type'], + 'value' => $mappingSetup['value'], + 'status' => 1, + "created_by" => fillOnUndefined($params, "user_id") , + "updated_by" => fillOnUndefined($params, "user_id") , + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + }*/ + + + } + + /* $insertRoomRateMappingSetupResult = $this->propertyRoomRateMappingSetupRepository->insert($insertRoomRateMappingSetupData); + if ($insertRoomRateMappingSetupResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + }*/ + + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPropertyRoomRateMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'room_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'room_id')], + + ], + 'with' => ['propertyRoomRate', 'propertyRoomRateChannel'] + ]; + + $data = $this->propertyRoomRateMappingRepository->findByCriteria($criteria, ['id', 'room_id', 'room_rate_id', 'description', 'rack_rate', 'included_occupancy']); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyRoomRateMappingConnected($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'room_rate_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'room_rate_id')], + + ], + 'with' => ['propertyRoom'] + ]; + + $relationalRoomsByRate = $this->propertyRoomRateMappingRepository->findByCriteria($criteria, ['id', 'room_id', 'room_rate_id']); + $relationalRoomIds = pickItemFromArray('room_id', $relationalRoomsByRate); + + + $criteriaRoomRate = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'whereIn' => + [ + ['field' => 'room_id', "value" => $relationalRoomIds] + ], + 'with' => ['propertyRoom.propertyRoomType', 'propertyRoomRate'] + ]; + + $roomRates = $this->propertyRoomRateMappingRepository->findByCriteria($criteriaRoomRate); + + + $connectedValue = []; + foreach ($roomRates as $roomRate) { + if ($roomRate['room_rate_id'] == $params['room_rate_id'] && !empty($roomRate['connected_rate_id'])) { + + $connectedValue[$roomRate['room_id']] = [ + 'connected_rate_id' => $roomRate['connected_rate_id'], + 'is_affected_price' => $roomRate['is_affected_price'], + 'affect_price_action_type' => $roomRate['affect_price_action_type'], + 'affect_price_type' => $roomRate['affect_price_type'], + 'affect_price_value' => $roomRate['affect_price_value'], + ]; + + continue; + } + } + + $roomRateList = []; + foreach ($roomRates as $roomRate) { + + if ($roomRate['room_rate_id'] == $params['room_rate_id']) { + continue; + } + + $roomRateList[$roomRate['room_id']]['room'] = [ + 'room_id' => $roomRate['room_id'], + 'room_name' => $roomRate['property_room']['name'], + 'room_type' => $roomRate['property_room']['property_room_type']['name'], + 'status' => $roomRate['property_room']['status'], + ]; + + if (isset($connectedValue[$roomRate['room_id']]) && $connectedValue[$roomRate['room_id']]['connected_rate_id'] == $roomRate['room_rate_id']) { + $roomRateList[$roomRate['room_id']]['rates'][] = [ + 'id' => $roomRate['id'], + 'room_rate_id' => $roomRate['room_rate_id'], + 'room_rate_name' => $roomRate['property_room_rate']['name'], + 'connected_rate_id' => $connectedValue[$roomRate['room_id']]['connected_rate_id'], + 'is_affected_price' => $connectedValue[$roomRate['room_id']]['is_affected_price'], + 'affect_price_action_type' => $connectedValue[$roomRate['room_id']]['affect_price_action_type'], + 'affect_price_type' => $connectedValue[$roomRate['room_id']]['affect_price_type'], + 'affect_price_value' => $connectedValue[$roomRate['room_id']]['affect_price_value'], + ]; + } else { + $roomRateList[$roomRate['room_id']]['rates'][] = [ + 'id' => $roomRate['id'], + 'room_rate_id' => $roomRate['room_rate_id'], + 'room_rate_name' => $roomRate['property_room_rate']['name'], + 'connected_rate_id' => null, + 'is_affected_price' => null, + 'affect_price_action_type' => null, + 'affect_price_type' => null, + 'affect_price_value' => null, + ]; + } + + } + + $roomRateList = array_values($roomRateList); + + $response = [ + 'status' => true, + 'data' => $roomRateList, + ]; + + } 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); + } + + public function syncPropertyRoomRateMappingConnected($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + DB::beginTransaction(); + + try { + + $validationResult = $this->propertyRoomRateMappingConnectedValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'room_rate_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'room_rate_id')], + + ], + ]; + + $relationalRoomsByRate = $this->propertyRoomRateMappingRepository->findByCriteria($criteria); + $relationalRoomIds = pickItemFromArray('room_id', $relationalRoomsByRate); + + //ResetRoomRateConnectParam + foreach ($relationalRoomsByRate as $roomRate) { + + $resetRoomRateConnectParam = [ + 'connected_rate_id' => null, + 'is_affected_price' => null, + 'affect_price_action_type' => null, + 'affect_price_type' => null, + 'affect_price_value' => null, + ]; + + $this->propertyRoomRateMappingRepository->update($roomRate['id'], $resetRoomRateConnectParam); + + foreach ($params['connected_room_rate'] as $connectedRoomRate) { + + if ($roomRate['room_id'] == $connectedRoomRate['room_id'] && $connectedRoomRate['room_rate_id']) { + + $roomRateConnectParam = [ + 'connected_rate_id' => fillOnUndefined($connectedRoomRate, 'room_rate_id'), + 'is_affected_price' => fillOnUndefined($connectedRoomRate, 'is_affected_price'), + 'affect_price_action_type' => fillOnUndefined($connectedRoomRate, 'affect_price_action_type'), + 'affect_price_type' => fillOnUndefined($connectedRoomRate, 'affect_price_type'), + 'affect_price_value' => fillOnUndefined($connectedRoomRate, 'affect_price_value'), + ]; + + $roomRateConnect = $this->propertyRoomRateMappingRepository->update($roomRate['id'], $roomRateConnectParam); + + if ($roomRateConnect['status'] != 'success') { + throw new ApiErrorException($roomRateConnect['message']); + } + + } + } + + } + + $response = [ + 'status' => true + ]; + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return output($response); + } + + public function updatePropertyRoomRateMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomRateMappingUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $updateData = + [ + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'description' => fillOnUndefined($params, 'description'), + 'rack_rate' => fillOnUndefined($params, 'rack_rate'), + 'included_occupancy' => fillOnUndefined($params, 'included_occupancy'), + "updated_by" => fillOnUndefined($params, "user_id"), + ]; + + if (isset($params['status'])) { + $updateData['status'] = fillOnUndefined($params, "status", 0); + } + + + $updateResult = $this->update($params['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException($updateResult['message']); + } + $userData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deletePropertyRoomRateMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomRateMappingDeleteValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $updateData = + [ + 'status' => 0, + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $updateResult = $this->update($params['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException($updateResult['message']); + } + $userData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function setStatusPropertyRoomRateMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + $validationResult = $this->propertyRoomRateMappingSetStatusValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $updateData = + [ + 'status' => $params['is_selected'], + "updated_by" => fillOnUndefined($params, "user_id"), + "updated_at" => time(), + ]; + + $updateResult = $this->propertyRoomRateMappingRepository->update($params['room_rate_mapping_id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + if ($params['is_selected'] == false) { + $updateCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'room_rate_mapping_id', 'condition' => '=', 'value' => $params['room_rate_mapping_id']], + ] + ]; + + $updateData = [ + "status" => 0, + "updated_by" => $params['user_id'] + ]; + + $updateResult = $this->propertyRoomRateChannelMappingRepository->updateWhere($updateCriteria, $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + +} diff --git a/app/Core/Service/PropertyRoomRateMappingSetupService.php b/app/Core/Service/PropertyRoomRateMappingSetupService.php new file mode 100644 index 0000000..2249159 --- /dev/null +++ b/app/Core/Service/PropertyRoomRateMappingSetupService.php @@ -0,0 +1,140 @@ +propertyRoomRateMappingSetupRepository = $propertyRoomRateMappingSetupRepository; + $this->propertyRoomRateMappingSetupAddValidator = $propertyRoomRateMappingSetupAddValidator; + + + } + + + public function addPropertyRoomRateMappingSetup($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + $validateData = + [ + 'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id'), + 'setup' => fillOnUndefined($params, 'setup'), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $validationResult = $this->propertyRoomRateMappingSetupAddValidator->validate($validateData); + + + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $deleteCriteria = [ + 'criteria' => [ + ['field' => 'room_rate_mapping_id', 'condition' => '=', 'value' => $validateData['room_rate_mapping_id']], + ] + ]; + + $this->propertyRoomRateMappingSetupRepository->delete($deleteCriteria); + + + foreach ($validateData["setup"] as $item){ + $insertData = [ + 'room_rate_mapping_id' => $validateData['room_rate_mapping_id'] , + 'setup_type' => $item['setup_type'], + 'value_type' => $item['value_type'], + 'value' => $item['value'], + 'status' => 1, + "created_by" => $validateData['created_by'] , + "updated_by" =>$validateData['updated_by'] , + + ]; + + + $userCreateResult = $this->propertyRoomRateMappingSetupRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPropertyRoomRateMappingSetup($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'room_rate_mapping_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'room_rate_mapping_id')], + ] + ]; + + + $data = $this->propertyRoomRateMappingSetupRepository->findByCriteria($criteria, ['id', 'room_rate_mapping_id', 'setup_type', 'value_type', 'value']); + $response = [ + 'status' => true, + 'data' => $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); + } + + + + +} diff --git a/app/Core/Service/PropertyRoomRatePriceQueueService.php b/app/Core/Service/PropertyRoomRatePriceQueueService.php new file mode 100644 index 0000000..fa523fa --- /dev/null +++ b/app/Core/Service/PropertyRoomRatePriceQueueService.php @@ -0,0 +1,115 @@ +propertyRoomRatePriceQueueRepository = $propertyRoomRatePriceQueueRepository; + + + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomRatePriceQueueRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRoomRatePriceQueueRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function delete($ids = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deleteResult = $this->propertyRoomRatePriceQueueRepository->destroy($ids); + + if ($deleteResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult['data'] // affected row count + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + +} diff --git a/app/Core/Service/PropertyRoomRatePriceService.php b/app/Core/Service/PropertyRoomRatePriceService.php new file mode 100644 index 0000000..79ef8cb --- /dev/null +++ b/app/Core/Service/PropertyRoomRatePriceService.php @@ -0,0 +1,1183 @@ +propertyRoomRatePriceRepository = $propertyRoomRatePriceRepository; + $this->propertyRoomRatePriceAddValidator = $propertyRoomRatePriceAddValidator; + $this->propertyRoomRatePriceUpdateValidator = $propertyRoomRatePriceUpdateValidator; + $this->propertyRoomRatePriceDeleteValidator = $propertyRoomRatePriceDeleteValidator; + $this->propertyRoomRatePriceBulkInsertValidator = $propertyRoomRatePriceBulkInsertValidator; + $this->propertyRoomRatePriceBulkUpdateValidator = $propertyRoomRatePriceBulkUpdateValidator; + $this->propertyChannelRepository = $propertyChannelRepository; + $this->propertyRoomRateMappingRepository = $roomRateMappingRepository; + $this->propertyRepository = $propertyRepository; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + $this->propertyRoomConnectedRepository = $propertyRoomConnectedRepository; + $this->propertyQuickPricingService = $propertyQuickPricingService; + $this->propertyRoomRateChannelMappingRepository = $propertyRoomRateChannelMappingRepository; + + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomRatePriceAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'description' => fillOnUndefined($params, 'description'), + 'min_stay' => fillOnUndefined($params, 'min_stay'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "created_by"), + "updated_by" => fillOnUndefined($params, "updated_by"), + ]; + + $userCreateResult = $this->propertyRoomRatePriceRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomRatePriceRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRoomRatePriceRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomRatePriceRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function addPropertyRoomRatePrice($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomRatePriceAddValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + 'property_id' => fillOnUndefined($params, 'property_id', 0), + 'property_room_id' => fillOnUndefined($params, 'property_room_id', 0), + 'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id', 0), + 'offer_id' => fillOnUndefined($params, 'offer_id', 0), + 'channel_id' => fillOnUndefined($params, 'channel_id', 0), + 'min_stay' => fillOnUndefined($params, 'min_stay', 0), + 'max_stay' => fillOnUndefined($params, 'max_stay', 0), + 'stop_sell' => fillOnUndefined($params, 'stop_sell', 0), + 'booking_on_request' => fillOnUndefined($params, 'booking_on_request', 0), + 'date' => fillOnUndefined($params, 'date', null), + 'amount' => fillOnUndefined($params, 'amount', 0), + 'currency' => fillOnUndefined($params, 'currency', null), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id", 0), + "updated_by" => fillOnUndefined($params, "user_id", 0), + ]; + + + $userCreateResult = $this->propertyRoomRatePriceRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyRoomRatePrices($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ] + ]; + + $data = $this->propertyRoomRatePriceRepository->findByCriteria($criteria, ['id', 'property_id', 'name', 'description', 'min_stay']); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function updatePropertyRoomRatePrice($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomRatePriceUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $updateData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'description' => fillOnUndefined($params, 'description'), + 'min_stay' => fillOnUndefined($params, 'min_stay'), + "updated_by" => fillOnUndefined($params, "user_id"), + ]; + + if (isset($params['status'])) { + $updateData['status'] = fillOnUndefined($params, "status", 0); + } + + + $updateResult = $this->update($params['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException($updateResult['message']); + } + $userData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deletePropertyRoomRatePrice($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomRatePriceDeleteValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $updateData = + [ + 'status' => 0, + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $updateResult = $this->update($params['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException($updateResult['message']); + } + $userData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function bulkUpdate($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $startDate = Carbon::parse($params['start_date']); + $endDate = Carbon::parse($params['end_date']); + $diffInDays = $startDate->diffInDays($endDate); + $includeDays = fillOnUndefined($params, 'include_days', []); + $insertData = []; + + $validationResult = $this->propertyRoomRatePriceBulkInsertValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + // Get Channel Mapping Information + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'channel_id')], + ], + 'with' => ['channel'], + 'firstRow' => 1 + ]; + + $channelData = $this->propertyChannelMappingRepository->findByCriteria($criteria, ['id', 'property_id', 'channel_id', 'connected_channel_id', 'currency_code', 'property_availability_type_id']); + if (!$channelData) { + throw new ApiErrorException('Channel mapping not found'); + } + + $updateData = []; + switch ($params['update_type']) { + case 'rate': + case 'quick_pricing': + $updateData = ['amount' => $params['value']]; + break; + case 'min_stay': + $updateData = ['min_stay' => $params['value']]; + break; + case 'maxstay': + $updateData = ['max_stay' => $params['value']]; + break; + case 'stopsell': + $updateData = ['stop_sell' => $params['value']]; + break; + case 'bookingonrequest': + $updateData = ['booking_on_request' => $params['value']]; + break; + case 'rate_stop_sell': + $updateData = ['stop_sell' => $params['value']]; + break; + + } + + $updateData = array_merge($updateData, ['updated_by' => fillOnUndefined($params, "user_id", 0), 'updated_at' => Carbon::now()->timestamp,]); + + //min_stay min 1 + if(isset($updateData['min_stay']) && empty($updateData['min_stay'])) { + $updateData['min_stay'] = 1; + } + + $selectIds = $this->selectedRoomRateIds($params); + if ($selectIds['status'] != 'success') { + throw new ApiErrorException('array error'); + } + $selectIds = $selectIds['data']; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + + ]; + $property = $this->propertyRepository->findByCriteria($propertyRequest); + + if (!$property) { + throw new ApiErrorException(lang('Property not found')); + } + + //Quick Pricing + $propertyQuickPricingMappingCollect = []; + if ($params['update_type'] == 'quick_pricing') { + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyRoomRateChannelMapping.propertyRoomRateMapping', 'propertyRoomRateChannelMapping.propertyRoomRateMapping'] + ]; + + //Connected Channel Quick Pricing Settings + if (!empty($channelData['connected_channel_id'])) { + $requestSelectCriteria['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => $channelData['connected_channel_id']]; + } else { + $requestSelectCriteria['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']]; + } + + $columns = ['id', 'property_id', 'channel_id', 'room_rate_channel_mapping_id', 'action_type', 'price_type', 'price_value']; + $requestSelectResult = $this->propertyQuickPricingService->selectPropertyQuickPricingMapping($requestSelectCriteria, $columns); + + if ($requestSelectResult['status'] == 'success') { + $propertyQuickPricingMappingCollect = collect($requestSelectResult['data']); + } + } + + + $channelDataRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['channel_id']], + ], + 'firstRow' => 1 + ]; + $selectedChannel = $this->propertyChannelRepository->findByCriteria($channelDataRequest, ['id', 'name', 'currency_code']); + $selectedChannel = $selectedChannel ? $selectedChannel : []; + if (!$selectedChannel) { + throw new ApiErrorException('channel error'); + } + + $roomRateMappingRequest = [ + 'whereIn' => [ + ['field' => 'id', 'value' => $selectIds['room_rate_mapping_ids']], + ], + 'with' => ['propertyRoomRate'] + ]; + $selectedRoomRateMapping = $this->propertyRoomRateMappingRepository->findByCriteria($roomRateMappingRequest); + $selectedRoomRateMapping = $selectedRoomRateMapping ? $selectedRoomRateMapping : []; + $selectedRoomRateMapping = collect($selectedRoomRateMapping); + + $oldRoomRatesCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'date', 'condition' => '>=', 'value' => $params['start_date']], + ['field' => 'date', 'condition' => '<=', 'value' => $params['end_date']], + ] + ]; + + $oldRoomRatePrice = $this->propertyRoomRatePriceRepository->findbyCriteria($oldRoomRatesCriteria); + $oldRoomRatePrice = $oldRoomRatePrice ? $oldRoomRatePrice : []; + $updateThis = []; + + $oldRoomRatePriceKeys = []; + foreach ($oldRoomRatePrice as $oldPrice) { + $oldPriceKey = $oldPrice['property_room_id'] . "|" . $oldPrice['room_rate_mapping_id'] . "|" . $oldPrice['date'] . "|" . $oldPrice['channel_id'] . "|" . $oldPrice['availability_type_id']; + $oldRoomRatePriceKeys[$oldPriceKey] = $oldPrice['id']; + } + + + foreach ($params['room_rates'] as $roomRate) { + foreach ($roomRate['room_rate_mapping_id'] as $roomRateMappingId) { + + if ($params['update_type'] == 'quick_pricing') { + + //Connected Channel Quick Pricing Settings + if ( !empty($channelData['connected_channel_id'])) { + $quickPricing = $propertyQuickPricingMappingCollect->where('property_id', $params['property_id']) + ->where('channel_id', $channelData['connected_channel_id'])->where('property_room_rate_channel_mapping.room_rate_mapping_id', $roomRateMappingId) + ->first(); + + } else { + $quickPricing = $propertyQuickPricingMappingCollect->where('property_id', $params['property_id']) + ->where('channel_id', $params['channel_id'])->where('property_room_rate_channel_mapping.room_rate_mapping_id', $roomRateMappingId) + ->first(); + } + + if (empty($quickPricing)) { + continue; + } + } + + $startDate = Carbon::parse($params['start_date']); + for ($i = 0; $i <= $diffInDays; $i++) { + if (!in_array($startDate->shortEnglishDayOfWeek, $includeDays)) { + $startDate = $startDate->addDay(); + continue; + } + + $currentRoomRateMapping = $selectedRoomRateMapping->where('id', '=', $roomRateMappingId)->first(); + $currentRoomRate = $currentRoomRateMapping['property_room_rate']; + + $insertDataStopSell = 0; + if ($params['update_type'] == 'rate_stop_sell') { + $insertDataStopSell = fillOnUndefined($params, 'value', 0); + } + + $insertDataMinStay = 0; + if ($params['update_type'] == 'min_stay') { + $insertDataMinStay = fillOnUndefined($params, 'value', 1); + } + + $insertDataAmount = 0; + if ($params['update_type'] == 'rate') { + $insertDataAmount = fillOnUndefined($params, 'value', 0); + } + + if ($params['update_type'] == 'quick_pricing') { + + $amount = fillOnUndefined($params, 'value', 0); + + $amountCalculated = $amount; + + $amountAffected = 0; + if ($quickPricing['price_type'] == 'PER') { + $amountAffected = ($amount * $quickPricing['price_value']) / 100; + } elseif ($quickPricing['price_type'] == 'FIX') { + $amountAffected = $quickPricing['price_value']; + } + + if ($quickPricing['action_type'] == 'INC') { + $amountCalculated = $amountCalculated + $amountAffected; + } + + if ($quickPricing['action_type'] == 'DEC') { + $amountCalculated = $amountCalculated - $amountAffected; + } + + $insertDataAmount = $amountCalculated; + } + + $insertDataItem = [ + 'property_id' => fillOnUndefined($params, 'property_id', 0), + 'property_room_id' => fillOnUndefined($roomRate, 'room_id', 0), + 'room_rate_mapping_id' => $roomRateMappingId, + 'channel_id' => fillOnUndefined($params, 'channel_id', 0), + 'availability_type_id' => fillOnUndefined($params, 'availability_type_id', 1), + 'min_stay' => $insertDataMinStay, + 'max_stay' => 0, + 'stop_sell' => $insertDataStopSell, + 'booking_on_request' => 0, + 'date' => $startDate->format('Y-m-d'), + 'amount' => $insertDataAmount, + 'currency' => $channelData['currency_code'], + 'status' => 1, + 'created_by' => fillOnUndefined($params, "user_id", 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + + $checkKey = $roomRate['room_id'] . "|" . $roomRateMappingId . "|" . $startDate->format('Y-m-d') . "|" . $params['channel_id'] . "|" . $params['availability_type_id']; + if (isset($oldRoomRatePriceKeys[$checkKey])) { + if ($params['update_type'] == 'quick_pricing') { + $amountKey = (string)$insertDataAmount; + $updateThis[$amountKey][] = $oldRoomRatePriceKeys[$checkKey]; + } else { + $updateThis[] = $oldRoomRatePriceKeys[$checkKey]; + } + + } else { + $insertData[] = $insertDataItem; + } + + $startDate = $startDate->addDay(); + } + + } + } + + if ($updateThis) { + + if ($params['update_type'] == 'quick_pricing') { + + foreach ($updateThis as $updateDataQuickPricingAmount => $updateIds) { + $updateDataCalculated = $updateData; + $updateDataCalculated['amount'] = $updateDataQuickPricingAmount; + $data = $this->propertyRoomRatePriceRepository->updateWhereIn($updateIds, $updateDataCalculated); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + } else { + + $thisUpdateArrayChunk = array_chunk($updateThis, 1000); + foreach ($thisUpdateArrayChunk as $updateIds) { + $data = $this->propertyRoomRatePriceRepository->updateWhereIn($updateIds, $updateData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + } + + } + + if (in_array($params['update_type'], ['rate', 'rate_stop_sell', 'min_stay', 'quick_pricing'])) { + $thisInsertArrayChunk = array_chunk($insertData, 1000); + foreach ($thisInsertArrayChunk as $insertThis) { + $data = $this->propertyRoomRatePriceRepository->insert($insertThis); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + + //Connected Room Case + $roomRateUpdateForConnectedRoomParams = [ + 'quickPricing' => fillOnUndefined($params, 'quickPricing', false), + 'property_id' => $params['property_id'], + 'channel_id' => $params['channel_id'], + 'currency_code' => $channelData['currency_code'], + 'channel_availability_type_id' => $channelData['property_availability_type_id'], + 'startDate' => $params['start_date'], + 'endDate' => $params['end_date'], + 'user_id' => fillOnUndefined($params, "user_id", 0) + ]; + + $this->roomRateUpdateForConnectedRooms($roomRateUpdateForConnectedRoomParams); + //Connected Room Case + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectedRoomRateIds($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $roomIds = []; + $roomRateIds = []; + $channelIds = []; + foreach ($params['room_rates'] as $roomRate) { + $roomIds[] = $roomRate['room_id']; + foreach ($roomRate['room_rate_mapping_id'] as $roomRateMapping) { + $roomRateIds[] = $roomRateMapping; + } + } + $roomIds = array_unique($roomIds); + $roomRateIds = array_unique($roomRateIds); + $response = [ + 'status' => true, + 'data' => [ + 'room_ids' => $roomIds, + 'room_rate_mapping_ids' => $roomRateIds + ], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function formElementsToArray($params = []) + { + + $newMappingArray = []; + $availabilityArray = []; + + foreach ($params['rates'] as $key => $value) { + $keyExportArray = explode('_', $key); + $theDate = Carbon::parse($keyExportArray[4])->format('Y-m-d'); + $keyExportArray[3] = $keyExportArray[3] != 0 ? $keyExportArray[3] : null; + $channelId = $keyExportArray[3] != 0 ? $params['channel_id'] : null; + if ($keyExportArray[0] == 'AVA') { + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['setup_type_id'] = $keyExportArray[1]; + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['room_id'] = $keyExportArray[2]; + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3] != 0 ? $keyExportArray[3] : null; + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['date'] = $theDate; + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['availability'] = $value; + } elseif ($keyExportArray[0] == 'PRC') { + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['setup_type_id'] = $keyExportArray[1]; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_id'] = $keyExportArray[2]; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3]; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['date'] = $theDate; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['amount'] = $value; + } elseif ($keyExportArray[0] == 'STS') { + if ($keyExportArray[3] != null) { + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['setup_type_id'] = $keyExportArray[1]; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_id'] = $keyExportArray[2]; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3]; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['date'] = $theDate; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['stop_sell'] = $value; + } + + if ($keyExportArray[3] == null) { + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['setup_type_id'] = $keyExportArray[1]; + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['room_id'] = $keyExportArray[2]; + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3] != 0 ? $keyExportArray[3] : null; + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['date'] = $theDate; + $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['stop_sell'] = $value; + } + + } elseif ($keyExportArray[0] == 'MNS') { + if ($keyExportArray[3] != null) { + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['setup_type_id'] = $keyExportArray[1]; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_id'] = $keyExportArray[2]; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3]; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['date'] = $theDate; + $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['min_stay'] = $value; + } + } + + } + + return ['availability' => $availabilityArray, 'rates' => $newMappingArray]; + + } + + public function roomRateUpdate($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $newMappingArray = $params ? $params : []; + $validationResult = $this->propertyRoomRatePriceBulkUpdateValidator->validate($newMappingArray); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $newMappingArray = $newMappingArray['rates']; + + $newMappingCollect = collect($newMappingArray); + $startDate = $newMappingCollect->min('date'); + $endDate = $newMappingCollect->max('date'); + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + + ]; + $property = $this->propertyRepository->findByCriteria($propertyRequest); + + if (!$property) { + throw new ApiErrorException(lang('Property not found')); + } + + // Get Channel Mapping Information + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'channel_id')], + ], + 'with' => ['channel'], + 'firstRow' => 1 + ]; + + $channelData = $this->propertyChannelMappingRepository->findByCriteria($criteria, ['id', 'property_id', 'channel_id', 'currency_code', 'property_availability_type_id']); + if (!$channelData) { + throw new ApiErrorException('Channel mapping not found'); + } + + $oldRoomRatePrice = []; + if (!empty($newMappingArray)) { + $oldRoomRatesCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'date', 'condition' => '>=', 'value' => $startDate], + ['field' => 'date', 'condition' => '<=', 'value' => $endDate], + ] + ]; + + $oldRoomRatePrice = $this->propertyRoomRatePriceRepository->findbyCriteria($oldRoomRatesCriteria); + } + + $oldMappingArray = []; + foreach ($oldRoomRatePrice as $oldPrice) { + $oldMappingArray[$oldPrice['availability_type_id'] . '|' . $oldPrice['property_room_id'] . '|' . $oldPrice['room_rate_mapping_id'] . '|' . $oldPrice['date']] = [ + 'id' => $oldPrice['id'], + 'room_id' => $oldPrice['property_room_id'], + 'room_rate_mapping_id' => $oldPrice['room_rate_mapping_id'], + 'availability_type_id' => $oldPrice['availability_type_id'], + 'channel_id' => $oldPrice['channel_id'], + 'date' => $oldPrice['date'], + 'amount' => $oldPrice['amount'], + 'stop_sell' => $oldPrice['stop_sell'], + 'min_stay' => $oldPrice['min_stay'] + ]; + } + + $deleteThis = []; + $insertThis = []; + + + foreach ($newMappingArray as $key => $newMapping) { + + if (isset($newMapping['amount']) && $newMapping['amount'] === "") { + $newMapping['amount'] = 0; + } + + if (isset($newMapping['amount']) && $newMapping['amount'] === "") { + if (isset($oldMappingArray[$key])) { + $deleteThis[] = $oldMappingArray[$key]['id']; + } + } else { + + if (isset($newMappingArray[$key]['amount'])) { + $insertAmountData = fillOnUndefined($newMappingArray[$key], 'amount', 0); + + if ($newMappingArray[$key]['amount'] == '') { + $newMappingArray[$key]['amount'] = 0; + $insertAmountData = 0; + } + } + + + if (!isset($newMapping['amount'])) { + if (!isset($oldMappingArray[$key])) { + $insertAmountData = 0; + } else { + $insertAmountData = $oldMappingArray[$key]['amount']; + } + + } + + $insertStopSellData = fillOnUndefined($newMappingArray[$key], 'stop_sell', 0); + if (!isset($newMapping['stop_sell'])) { + if (isset($oldMappingArray[$key])) { + $insertStopSellData = $oldMappingArray[$key]['stop_sell']; + } else { + $insertStopSellData = 0; + } + } + + $insertMinStayData = fillOnUndefined($newMappingArray[$key], 'min_stay', 1); + $insertMinStayData = empty($insertMinStayData) ? 1 : $insertMinStayData; + if (!isset($newMapping['min_stay'])) { + if (isset($oldMappingArray[$key])) { + $insertMinStayData = $oldMappingArray[$key]['min_stay']; + } else { + $insertMinStayData = 1; + } + } + + + $insertItem = [ + 'property_id' => fillOnUndefined($params, 'property_id', 0), + 'property_room_id' => fillOnUndefined($newMappingArray[$key], 'room_id', 0), + 'room_rate_mapping_id' => fillOnUndefinedAndEmpty($newMappingArray[$key], 'room_rate_mapping_id', 0), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + 'availability_type_id' => fillOnUndefined($newMappingArray[$key], 'setup_type_id', null), + 'min_stay' => $insertMinStayData, + 'max_stay' => fillOnUndefined($newMappingArray[$key], 'max_stay', 0), + 'stop_sell' => $insertStopSellData, + 'booking_on_request' => fillOnUndefined($newMappingArray[$key], 'booking_on_request', 0), + 'date' => fillOnUndefined($newMappingArray[$key], 'date', 0), + 'amount' => $insertAmountData, + 'currency' => $channelData['currency_code'], + 'status' => fillOnUndefined($params, "status", 1), + 'created_by' => fillOnUndefined($params, "user_id", 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + if (isset($oldMappingArray[$key])) { + if ( + $oldMappingArray[$key]['amount'] != $insertItem['amount'] || $oldMappingArray[$key]['stop_sell'] != $insertItem['stop_sell'] || $oldMappingArray[$key]['min_stay'] != $insertItem['min_stay'] + ) { + $deleteThis[] = $oldMappingArray[$key]['id']; + if ($insertItem['amount'] !== 0) { + $insertThis[] = $insertItem; + } + } + } else { + $insertThis[] = $insertItem; + } + + } + + } + + sort($deleteThis); + + if ($deleteThis) { + $deleteRoomRatePrices = $this->propertyRoomRatePriceRepository->destroy($deleteThis); + if ($deleteRoomRatePrices['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $insertRoomRatePrices = $this->propertyRoomRatePriceRepository->insert($insertThis); + if ($insertRoomRatePrices['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + + //Connected Room Case + $roomRateUpdateForConnectedRoomParams = [ + 'quickPricing' => fillOnUndefined($params, 'quickPricing', false), + 'property_id' => $params['property_id'], + 'channel_id' => $params['channel_id'], + 'currency_code' => $channelData['currency_code'], + 'channel_availability_type_id' => $channelData['property_availability_type_id'], + 'startDate' => $startDate, + 'endDate' => $endDate, + 'user_id' => fillOnUndefined($params, "user_id", 0) + ]; + + + $this->roomRateUpdateForConnectedRooms($roomRateUpdateForConnectedRoomParams); + //Connected Room Case + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + protected function roomRateUpdateForConnectedRooms($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + if (is_null($params['startDate']) || is_null($params['endDate'])) { + $response['status'] = true; + return $response; + } + + $diffInDays = Carbon::parse($params['startDate'])->diffInDays(Carbon::parse($params['endDate'])); + + //Connected Room Case + $propertyRoomConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['roomDetail'] + ]; + + $propertyRoomConnected = $this->propertyRoomConnectedRepository->findbyCriteria($propertyRoomConnectedCriteria); + $propertyRoomConnected = $propertyRoomConnected ? $propertyRoomConnected : []; + + $propertyRoomConnectedGrouped = collect($propertyRoomConnected)->groupBy('room_id')->toArray(); + + + $propertyRoomRateChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'channel_id')], + ], + 'with' => ['propertyRoomRateMapping', 'propertyRoomRateQuickPricingMapping'] + ]; + + $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingRepository->findByCriteria($propertyRoomRateChannelMappingCriteria, ['*']); + $propertyRoomRateChannelMappingCollect = collect($propertyRoomRateChannelMapping); + + $oldRoomRatesCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'date', 'condition' => '>=', 'value' => $params['startDate']], + ['field' => 'date', 'condition' => '<=', 'value' => $params['endDate']], + ], + 'with' => ['roomRateMapping'] + ]; + + $oldRoomRatePriceUpdated = $this->propertyRoomRatePriceRepository->findbyCriteria($oldRoomRatesCriteria); + $oldRoomRatePriceUpdated = $oldRoomRatePriceUpdated ? collect($oldRoomRatePriceUpdated) : []; + + + foreach ($propertyRoomConnectedGrouped as $connectedRoomId => $connectedRooms) { + + $roomDetail = reset($connectedRooms)['room_detail']; + + if ($roomDetail['is_connected_room_price'] != 1) { + continue; + } + + //güncellenmesi gereken id bulalım + $connectedRoomIds = pickItemFromArray('connected_room_id', $connectedRooms); + + $availableRoomRates = $propertyRoomRateChannelMappingCollect->where('property_room_rate_mapping.room_id', $connectedRoomId); + $availableRoomRates = $availableRoomRates ? $availableRoomRates->toArray() : []; + + foreach ($availableRoomRates as $availableRoomRate) { + + + for ($i = 0; $i <= $diffInDays; $i++) { + + $date = Carbon::parse($params['startDate'])->addDays($i)->toDateString(); + + $issetCurrentRoomRatePrice = $oldRoomRatePriceUpdated->where('availability_type_id', $params['channel_availability_type_id']) + ->where('property_room_id', $connectedRoomId) + ->where('room_rate_mapping_id', $availableRoomRate['room_rate_mapping_id']) + ->where('date', $date)->first(); + + $roomPriceFromConnectedRooms = $oldRoomRatePriceUpdated + ->where('room_rate_mapping.room_rate_id', $availableRoomRate['property_room_rate_mapping']['room_rate_id']) + ->whereIn('property_room_id', $connectedRoomIds) + ->where('date', $date) + ->sum('amount'); + + + if (isset($params['quickPricing']) && $params['quickPricing']) { + + $amount = $roomPriceFromConnectedRooms; + $amountCalculated = $roomPriceFromConnectedRooms; + + $quickPricing = $availableRoomRate['property_room_rate_quick_pricing_mapping']; + + $amountAffected = 0; + if ($quickPricing['price_type'] == 'PER') { + $amountAffected = ($amount * $quickPricing['price_value']) / 100; + } elseif ($quickPricing['price_type'] == 'FIX') { + $amountAffected = $quickPricing['price_value']; + } + + if ($quickPricing['action_type'] == 'INC') { + $amountCalculated = $amountCalculated + $amountAffected; + } + + if ($quickPricing['action_type'] == 'DEC') { + $amountCalculated = $amountCalculated - $amountAffected; + } + + $roomPriceFromConnectedRooms = $amountCalculated; + + } + + + //update + if (!empty($issetCurrentRoomRatePrice)) { + + $this->propertyRoomRatePriceRepository->update($issetCurrentRoomRatePrice['id'], ['amount' => $roomPriceFromConnectedRooms]); + + } else { + + $insertItem = [ + 'property_id' => $params['property_id'], + 'property_room_id' => $availableRoomRate['property_room_rate_mapping']['room_id'], + 'room_rate_mapping_id' => $availableRoomRate['property_room_rate_mapping']['id'], + 'channel_id' => $params['channel_id'], + 'availability_type_id' => $params['channel_availability_type_id'], + 'min_stay' => 0, + 'max_stay' => 0, + 'stop_sell' => 0, + 'booking_on_request' => 0, + 'date' => $date, + 'amount' => $roomPriceFromConnectedRooms, + 'currency' => $params['currency_code'], + 'status' => 1, + 'created_by' => fillOnUndefined($params, "user_id", 0), + 'updated_by' => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + + $this->propertyRoomRatePriceRepository->insert($insertItem); + + } + + + } + + + } + + + } + //Connected Room Case + + + $response = [ + 'status' => true, + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + +} diff --git a/app/Core/Service/PropertyRoomRateService.php b/app/Core/Service/PropertyRoomRateService.php new file mode 100644 index 0000000..fe19659 --- /dev/null +++ b/app/Core/Service/PropertyRoomRateService.php @@ -0,0 +1,573 @@ +propertyRoomRateRepository = $propertyRoomRateRepository; + $this->propertyRoomRateAddValidator = $propertyRoomRateAddValidator; + $this->propertyRoomRateUpdateValidator = $propertyRoomRateUpdateValidator; + $this->propertyRoomRateDeleteValidator = $propertyRoomRateDeleteValidator; + $this->propertyRoomRateAddWithInclusionValidator = $propertyRoomRateAddWithInclusionValidator; + $this->propertyRoomRateInclusionMappingRepository = $propertyRoomRateInclusionMappingRepository; + $this->propertyFactService = $propertyFactService; + $this->languageService = $languageService; + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomRateAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'description' => fillOnUndefined($params, 'description'), + 'accommodation_type' => fillOnUndefined($params, 'accommodation_type'), + 'min_stay' => fillOnUndefined($params, 'min_stay'), + 'max_stay' => fillOnUndefined($params, 'max_stay'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "created_by"), + "updated_by" => fillOnUndefined($params, "updated_by"), + ]; + + $userCreateResult = $this->propertyRoomRateRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + $getApplicationLanguages = $this->languageService->getApplicationLanguages(); + if ($getApplicationLanguages['status'] != 'success') { + throw new ApiErrorException($getApplicationLanguages['message']); + } + $applicationLanguages = $getApplicationLanguages['data']; + + try { + + $data = $this->propertyRoomRateRepository->findByCriteria($param, $column); + + if ($data) { + $descriptionLangContents = json_decode($data['description'], 1); + $responseLangDescription = []; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangDescription[] = [ + 'language_code' => $langKey, + 'description' => isset($descriptionLangContents[$langKey]) ? $descriptionLangContents[$langKey] : null + ]; + } + + $data['description'] = $responseLangDescription; + } + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRoomRateRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomRateRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function addPropertyRoomRate($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $description = []; + foreach (fillOnUndefined($params, "description", []) as $desc) { + $description[$desc['language_code']] = $desc['description']; + } + + $insertData = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'accommodation_type' => fillOnUndefined($params, 'accommodation_type'), + 'min_stay' => fillOnUndefined($params, 'min_stay'), + 'max_stay' => fillOnUndefined($params, 'max_stay'), + "description" => !empty($description) ? json_encode($description) : null, + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + + $userCreateResult = $this->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new ApiErrorException($userCreateResult['message']); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function addPropertyRoomRateWithInclusion($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + $validateData = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => trim(fillOnUndefined($params, 'name')), + 'accommodation_type' => fillOnUndefined($params, 'accommodation_type'), + /*'min_stay' => fillOnUndefined($params, 'min_stay'), + 'max_stay' => fillOnUndefined($params, 'max_stay'),*/ + 'facts' => fillOnUndefined($params, 'facts'), + + ]; + $validationResult = $this->propertyRoomRateAddWithInclusionValidator->validate($validateData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $description = []; + foreach (fillOnUndefined($params, "description", []) as $desc) { + $description[$desc['language_code']] = $desc['description']; + } + + + $insertData = [ + 'property_id' => fillOnUndefined($validateData, 'property_id'), + 'name' => fillOnUndefined($validateData, 'name'), + 'accommodation_type' => fillOnUndefined($validateData, 'accommodation_type'), + "description" => !empty($description) ? json_encode($description) : null, + /*'min_stay' => fillOnUndefined($validateData, 'min_stay'), + 'max_stay' => fillOnUndefined($validateData, 'max_stay'),*/ + "status" => fillOnUndefined($validateData, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + + $userCreateResult = $this->propertyRoomRateRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $roomRateData = $userCreateResult['data']; + $insertData = []; + foreach ($params["facts"] as $fact) { + $insertData[] = [ + 'room_rate_id' => $roomRateData['id'], + 'fact_id' => $fact, + 'status' => 1, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + } + + + $userCreateResult = $this->propertyRoomRateInclusionMappingRepository->insert($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $roomRateData, + ]; + + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPropertyRoomRates($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $getApplicationLanguages = $this->languageService->getApplicationLanguages(); + if ($getApplicationLanguages['status'] != 'success') { + throw new ApiErrorException($getApplicationLanguages['message']); + } + $applicationLanguages = $getApplicationLanguages['data']; + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'with' => ['propertyRoomRateInclusionMapping.propertyFact', 'propertyRoomRateAccommodation'] + ]; + + $data = $this->propertyRoomRateRepository->findByCriteria($criteria, ['id', 'property_id', 'name', 'description', 'accommodation_type', 'min_stay', 'max_stay']); + + $return = []; + foreach ($data as $item) { + $dataRow = $item; + if ($item['property_room_rate_inclusion_mapping']) { + $factList = collect($item['property_room_rate_inclusion_mapping'])->map(function ($value) { + return $value['property'] = [ + 'room_rate_id' => $value['room_rate_id'], + 'fact_id' => $value['fact_id'], + 'name' => $value['property_fact']['name'], + 'icon' => $value['property_fact']['icon'], + 'language_key' => $value['property_fact']['language_key'] + ]; + + })->toArray(); + $dataRow['property_room_rate_inclusion_mapping'] = $factList; + } + + + $descriptionLangContents = json_decode($dataRow['description'], 1); + $responseLangDescription = []; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangDescription[] = [ + 'language_code' => $langKey, + 'description' => isset($descriptionLangContents[$langKey]) ? $descriptionLangContents[$langKey] : null + ]; + } + + $dataRow['description'] = $responseLangDescription; + + $return[] = $dataRow; + } + $response = [ + 'status' => true, + 'data' => $return, + ]; + + } 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); + } + + public function updatePropertyRoomRate($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + $validationResult = $this->propertyRoomRateUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $description = []; + foreach (fillOnUndefined($params, "description", []) as $desc) { + $description[$desc['language_code']] = $desc['description']; + } + + $updateData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'accommodation_type' => fillOnUndefined($params, 'accommodation_type'), + 'min_stay' => fillOnUndefined($params, 'min_stay'), + "description" => !empty($description) ? json_encode($description) : null, + "updated_by" => fillOnUndefined($params, "user_id"), + ]; + + if (isset($params['status'])) { + $updateData['status'] = fillOnUndefined($params, "status", 0); + } + + $criteria = [ + 'criteria' => [ + ['field' => 'room_rate_id', 'condition' => '=', 'value' => $params['id']] + ] + ]; + + $data = $this->propertyRoomRateInclusionMappingRepository->findByCriteria($criteria, ['id']); + $deleteThis = collect($data)->keyBy('id')->keys()->toArray(); + + if ($deleteThis) { + $deleteThisArray = $this->propertyRoomRateInclusionMappingRepository->destroy($deleteThis); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $updateResult = $this->update($params['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $updateResult["data"]; + + if (!empty($params["facts"])) { + $insertData = []; + foreach ($params["facts"] as $fact) { + if ($fact) { + $insertData[] = [ + 'room_rate_id' => $params['id'], + 'fact_id' => $fact, + 'status' => 1, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + } + } + $userCreateResult = $this->propertyRoomRateInclusionMappingRepository->insert($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function deletePropertyRoomRate($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomRateDeleteValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $updateData = + [ + 'status' => 0, + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $updateResult = $this->update($params['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException($updateResult['message']); + } + $userData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyAccommodationTypes($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $requestFact = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'sub_category_id' => 9, + ]; + $subCategoryFacts = $this->propertyFactService->getSubCategoryFacts($requestFact); + if ($subCategoryFacts['status'] != 'success') { + throw new ApiErrorException($subCategoryFacts['message']); + } + // todo burada propertnin sahip olduğu tüm accommodation geliyordu + // $propertyAccommodationList = collect($subCategoryFacts['data'])->where('is_selected', '=', true)->values()->toArray(); + $propertyAccommodationList = $subCategoryFacts['data']; + $response = [ + 'status' => true, + 'data' => $propertyAccommodationList, + ]; + + } 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); + } + +} diff --git a/app/Core/Service/PropertyRoomService.php b/app/Core/Service/PropertyRoomService.php new file mode 100644 index 0000000..95e021b --- /dev/null +++ b/app/Core/Service/PropertyRoomService.php @@ -0,0 +1,2082 @@ +propertyRoomRepository = $propertyRoomRepository; + $this->propertyChannelMappingRepository = $propertyChannelMappingRepository; + $this->propertyRoomBedRepository = $propertyRoomBedRepository; + $this->propertyRoomRatePriceRepository = $propertyRoomRatePriceRepository; + $this->propertyRoomAvailabilityRepository = $propertyRoomAvailabilityRepository; + $this->propertyRoomAddValidator = $propertyRoomAddValidator; + $this->propertyRoomAndBedAddValidator = $propertyRoomAndBedAddValidator; + $this->propertyRoomUpdateValidator = $propertyRoomUpdateValidator; + $this->propertyRoomDeleteValidator = $propertyRoomDeleteValidator; + $this->propertyRoomInventoryValidator = $propertyRoomInventoryValidator; + $this->propertyRepository = $propertyRepository; + $this->propertyRoomTypeService = $propertyRoomTypeService; + $this->propertyRoomViewMappingRepository = $propertyRoomViewMappingRepository; + $this->propertyRoomAndBedUpdateValidator = $propertyRoomAndBedUpdateValidator; + $this->languageService = $languageService; + $this->propertyAvailabilityTypeRepository = $propertyAvailabilityTypeRepository; + $this->propertyRoomConnectedRepository = $propertyRoomConnectedRepository; + + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $description = []; + foreach (fillOnUndefined($params, "description", []) as $title) { + $description[$title['language_code']] = $title['description']; + } + + $insertData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'room_type_id' => fillOnUndefined($params, 'room_type_id'), + 'max_occupancy' => fillOnUndefined($params, 'max_occupancy'), + 'max_adult' => fillOnUndefined($params, 'max_adult'), + 'max_child' => fillOnUndefined($params, 'max_child'), + 'room_size' => fillOnUndefined($params, 'room_size'), + 'room_size_type' => fillOnUndefined($params, 'room_size_type'), + 'room_type_count' => fillOnUndefined($params, 'room_type_count', 1), + 'room_count' => fillOnUndefined($params, 'room_count'), + 'bathroom_count' => fillOnUndefined($params, 'bathroom_count'), + 'toilet_count' => fillOnUndefined($params, 'toilet_count'), + 'lounge_count' => fillOnUndefined($params, 'lounge_count'), + 'max_child_number' => fillOnUndefined($params, 'max_child_number'), + 'description' => json_encode($description), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "created_by"), + "updated_by" => fillOnUndefined($params, "updated_by"), + ]; + + $userCreateResult = $this->propertyRoomRepository->create($insertData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRoomRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function addPropertyRoom($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'room_type_id' => fillOnUndefined($params, 'room_type_id'), + 'max_occupancy' => fillOnUndefined($params, 'max_occupancy'), + 'max_adult' => fillOnUndefined($params, 'max_adult'), + 'max_child' => fillOnUndefined($params, 'max_child'), + 'occupancy_lock' => fillOnUndefined($params, 'occupancy_lock'), + 'room_size' => fillOnUndefined($params, 'room_size'), + 'room_size_type' => fillOnUndefined($params, 'room_size_type'), + 'room_type_count' => fillOnUndefined($params, 'room_type_count', 1), + 'room_count' => fillOnUndefined($params, 'room_count'), + 'bathroom_count' => fillOnUndefined($params, 'bathroom_count'), + 'toilet_count' => fillOnUndefined($params, 'toilet_count'), + 'lounge_count' => fillOnUndefined($params, 'lounge_count'), + 'max_child_number' => fillOnUndefined($params, 'max_child_number'), + 'description' => fillOnUndefined($params, 'description'), + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $userCreateResult = $this->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new ApiErrorException($userCreateResult['message']); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyRooms($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyRoomType', 'propertyRoomBedGroup.propertyRoomBedType'] + ]; + + $data = $this->propertyRoomRepository->findByCriteria($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number']); + + $newRoomMapping = []; + foreach ($data as $room) { + $bedGroups = collect($room['property_room_bed_group'])->keyBy('bed_group')->keys(); + $group = []; + foreach ($bedGroups as $bedGroup) { + $thisGroup = collect($room['property_room_bed_group'])->where('bed_group', '=', $bedGroup)->map(function ($bedItem) use ($bedGroup) { + $responseMapping = $bedItem; + $responseMapping['bed_type_name'] = $bedItem['property_room_bed_type']['name']; + unset($responseMapping['property_room_bed_type']); + return $responseMapping; + })->toArray(); + $newItem = []; + foreach ($thisGroup as $item) { + $newItem[] = $item; + } + $group[] = $newItem; + } + $room['property_room_bed_group'] = $group; + $newRoomMapping[] = $room; + + } + + $response = [ + 'status' => true, + 'data' => $newRoomMapping, + ]; + + } 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); + } + + public function updatePropertyRoom($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $description = []; + foreach (fillOnUndefined($params, "description", []) as $title) { + $description[$title['language_code']] = $title['description']; + } + $updateData = + [ + 'name' => fillOnUndefined($params, 'name'), + 'room_type_id' => fillOnUndefined($params, 'room_type_id'), + 'max_occupancy' => fillOnUndefined($params, 'max_occupancy'), + 'max_adult' => fillOnUndefined($params, 'max_adult'), + 'max_child' => fillOnUndefined($params, 'max_child'), + 'occupancy_lock' => fillOnUndefined($params, 'occupancy_lock'), + 'exclude_occupancy' => json_encode($params['exclude_occupancy'], true), + 'room_size' => fillOnUndefined($params, 'room_size', null), + 'room_size_type' => fillOnUndefined($params, 'room_size_type', null), + 'room_type_count' => fillOnUndefined($params, 'room_type_count', 1), + 'room_count' => fillOnUndefined($params, 'room_count', null), + 'bathroom_count' => fillOnUndefined($params, 'bathroom_count', null), + 'toilet_count' => fillOnUndefined($params, 'toilet_count', null), + 'lounge_count' => fillOnUndefined($params, 'lounge_count', null), + 'max_child_number' => fillOnUndefined($params, 'max_child_number', null), + 'description' => json_encode($description), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $updateResult = $this->update($params['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException($updateResult['message']); + } + $userData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deletePropertyRoom($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyRoomDeleteValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $updateData = + [ + 'status' => 0, + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $updateResult = $this->update($params['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException($updateResult['message']); + } + $userData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyRoomRateChannel($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + //['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => [ + 'propertyRoomType', 'propertyRoomRateMapping.propertyRoomRateChannel', 'propertyRoomRateMapping.connectedRate', + 'propertyRoomRateMapping.propertyRoomRate', 'propertyRoomBedGroup.propertyRoomBedType', + 'propertyRoomViewMapping.propertyRoomViewType', 'propertyRoomConnected' + ] + ]; + + $columns = [ + 'id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'exclude_occupancy', 'room_size', + 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', + 'max_child_number', 'description', 'is_connected_room', 'is_connected_room_price', 'is_connected_room_availability', + 'occupancy_lock', 'status' + ]; + + $roomData = $this->propertyRoomRepository->findByCriteria($criteria, $columns); + + foreach ($roomData as $roomKey => $room) { + $roomData[$roomKey]['is_connected_room'] = $room['is_connected_room'] == 1 ? true : false; + $roomData[$roomKey]['is_connected_room_price'] = $room['is_connected_room_price'] == 1 ? true : false; + $roomData[$roomKey]['is_connected_room_availability'] = $room['is_connected_room_availability'] == 1 ? true : false; + } + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ], + 'with' => ['channel'] + ]; + + $channelData = $this->propertyChannelMappingRepository->findByCriteria($criteria, ['id', 'property_id', 'channel_id']); + + $getApplicationLanguages = $this->languageService->getApplicationLanguages(); + if ($getApplicationLanguages['status'] != 'success') { + throw new ApiErrorException($getApplicationLanguages['message']); + } + $applicationLanguages = $getApplicationLanguages['data']; + + $return = []; + foreach ($roomData as $room) { + + $item = $room; + $descriptionLangContents = json_decode($item['description'], 1); + $responseLangDescription = []; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangDescription[] = [ + 'language_code' => $langKey, + 'description' => isset($descriptionLangContents[$langKey]) ? $descriptionLangContents[$langKey] : null + ]; + } + $item['description'] = $responseLangDescription; + $item['property_room_bed_group'] = []; + $item['exclude_occupancy'] = json_decode($room['exclude_occupancy']); + if (isset($room['property_room_rate_mapping'])) { + $roomRateMappings = $room['property_room_rate_mapping']; + $mapping = []; + + foreach ($roomRateMappings as $roomRateMapping) { + + if (isset($roomRateMapping['property_room_rate_channel'])) { + + $newChannelData = []; + $roomRateChannels = $roomRateMapping['property_room_rate_channel']; + $rateChannelKey = []; + foreach ($roomRateChannels as $roomRateChannel) { + if ($roomRateChannel['status'] == 1) { + $rateChannelKey[$roomRateChannel['channel_id'] . '|' . $roomRateChannel['room_rate_mapping_id']] = $roomRateChannel['id']; + } + } + + foreach ($channelData as $channel) { + $newChannelData[] = [ + 'id' => $channel['channel']['id'], + 'name' => $channel['channel']['name'], + 'channel_category_id' => $channel['channel']['channel_category_id'], + 'country_code' => $channel['channel']['country_code'], + 'currency_code' => $channel['channel']['currency_code'], + 'logo' => $channel['channel']['logo'], + 'is_checked' => isset($rateChannelKey[$channel['channel_id'] . '|' . $roomRateMapping['id']]), + ]; + } + $roomRateMapping['property_room_rate_channel'] = $newChannelData; + } + $roomRateMapping['name'] = $roomRateMapping['property_room_rate']['name']; + $roomRateMapping['min_stay'] = $roomRateMapping['property_room_rate']['min_stay']; + $roomRateMapping['max_stay'] = $roomRateMapping['property_room_rate']['max_stay']; + unset($roomRateMapping['property_room_rate']); + $mapping[] = $roomRateMapping; + + } + + $item['property_room_rate_mapping'] = $mapping; + + } + $bedGroups = collect($room['property_room_bed_group'])->keyBy('bed_group')->keys(); + $group = []; + foreach ($bedGroups as $bedGroup) { + $thisGroup = collect($room['property_room_bed_group'])->where('bed_group', '=', $bedGroup)->map(function ($bedItem) use ($bedGroup) { + $responseMapping = $bedItem; + $responseMapping['bed_type_name'] = $bedItem['property_room_bed_type']['name']; + $responseMapping['bed_type_language_key'] = $bedItem['property_room_bed_type']['language_key']; + unset($responseMapping['property_room_bed_type']); + return $responseMapping; + })->toArray(); + $newItem = []; + foreach ($thisGroup as $bedItem) { + $newItem[] = $bedItem; + } + $group[] = $newItem; + } + $item['property_room_bed_group'] = $group; + $item['property_room_view_mapping'] = collect($room['property_room_view_mapping'])->map(function ($value) { + + return $value['property_room_view_type']; + + })->all(); + + + $return[] = $item; + } + + + $response = [ + 'status' => true, + 'data' => $return, + ]; + + } 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); + } + + public function getPropertyRoomInventory($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $validationResult = $this->propertyRoomInventoryValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + + ]; + $property = $this->propertyRepository->findByCriteria($propertyRequest); + + if (!$property) { + throw new ApiErrorException(lang('Property not found')); + } + + // Get Channel Mapping Information + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'channel_id')], + ], + 'with' => ['channel'], + 'firstRow' => 1 + ]; + + $channelData = $this->propertyChannelMappingRepository->findByCriteria($criteria, ['id', 'property_id', 'channel_id', 'currency_code']); + if (!$channelData) { + throw new ApiErrorException('Channel mapping not found'); + } + + + $inventoryGetParams = $params; + $inventoryGetParams['channelData'] = $channelData; + $roomRatePrices = $this->getInventory($inventoryGetParams); + + if ($roomRatePrices['status'] != 'success') { + throw new ApiErrorException($roomRatePrices['message']); + } + + $response = [ + 'status' => true, + 'data' => $roomRatePrices['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); + } + + + public function checkPropertyRoomMapping($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $request = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ], + 'whereIn' => [ + ['field' => 'id', 'value' => $param['room_ids']], + ], + 'count' => 1 + ]; + $data = $this->propertyRoomRepository->findByCriteria($request); + if ($data < count($param['room_ids'])) { + throw new ApiErrorException('Property - Room Mapping not validate.'); + } + $response = [ + 'status' => true, + 'data' => $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); + } + + public function addPropertyRoomAndBed($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + // check room name, it is null set room type name. + if ($params['name'] == null) { + $propertyRoomTypeCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['room_type_id']], + ], + 'firstRow' => true, + ]; + $propertyType = $this->propertyRoomTypeService->select($propertyRoomTypeCriteria); + $params['name'] = $propertyType['data']['name']; + } + + $validationResult = $this->propertyRoomAndBedAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $description = []; + foreach (fillOnUndefined($params, "description", []) as $title) { + $description[$title['language_code']] = $title['description']; + } + + + if (!$params['is_connected_room']) { + $params['is_connected_room_price'] = null; + $params['is_connected_room_availability'] = null; + $params['connected_rooms'] = []; + } + + $insertRoomData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'room_type_id' => fillOnUndefined($params, 'room_type_id'), + 'max_occupancy' => fillOnUndefined($params, 'max_occupancy'), + 'max_adult' => fillOnUndefined($params, 'max_adult'), + 'max_child' => fillOnUndefined($params, 'max_child'), + 'occupancy_lock' => fillOnUndefined($params, 'occupancy_lock'), + 'exclude_occupancy' => json_encode($params['exclude_occupancy'], true), + 'room_size' => fillOnUndefined($params, 'room_size', null), + 'room_size_type' => fillOnUndefined($params, 'room_size_type', null), + 'room_type_count' => fillOnUndefined($params, 'room_type_count', 1), + 'room_count' => fillOnUndefined($params, 'room_count', null), + 'bathroom_count' => fillOnUndefined($params, 'bathroom_count', null), + 'toilet_count' => fillOnUndefined($params, 'toilet_count', null), + 'lounge_count' => fillOnUndefined($params, 'lounge_count', null), + 'max_child_number' => fillOnUndefined($params, 'max_child_number', null), + 'description' => json_encode($description), + 'is_connected_room' => fillOnUndefined($params, 'is_connected_room', null) ? 1 : null, + 'is_connected_room_price' => fillOnUndefined($params, 'is_connected_room_price', null) ? 1 : null, + 'is_connected_room_availability' => fillOnUndefined($params, 'is_connected_room_availability', null) ? 1 : null, + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $insertRoomDataResult = $this->propertyRoomRepository->create($insertRoomData); + + if ($insertRoomDataResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $roomId = $insertRoomDataResult['data']['id']; + $roomBedGroup = fillOnUndefined($params, 'room_bed_group', []); + $bedGroupIndex = 1; + $insertBedData = []; + + + $isBedGroupInsert = true; + + if (count($roomBedGroup) == 1) { + if (count($roomBedGroup[0]) == 1) { + if (empty($roomBedGroup[0][0]['bed_type_id'])) { + $isBedGroupInsert = false; + } + + } + } + + + if ($isBedGroupInsert) { + + $roomBedGroups = []; + collect($roomBedGroup)->map(function ($value) use (&$roomBedGroups) { + $roomBedGroups[] = collect($value) + ->groupBy('bed_type_id') + ->map(function ($val) { + return $val->count(); + })->toArray(); + }); + + foreach ($roomBedGroups as $bedGroup) { + foreach ($bedGroup as $bedTypeId => $bedTypeCount) { + if (!empty($bedTypeId)) { + $insertBedData[] = + [ + 'room_id' => $roomId, + 'bed_group' => $bedGroupIndex, + 'count' => $bedTypeCount, + 'bed_type_id' => $bedTypeId, + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id", 0), + "updated_by" => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + } + } + $bedGroupIndex++; + } + $insertBedResult = $this->propertyRoomBedRepository->insert($insertBedData); + if ($insertBedResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + + if (!empty($params['room_view_type'])) { + + foreach ($params['room_view_type'] as $roomViewType) { + + $datas[] = [ + 'room_id' => $roomId, + 'room_view_type_id' => $roomViewType, + "created_by" => fillOnUndefined($params, "user_id", 0), + "updated_by" => fillOnUndefined($params, "user_id", 0), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $propertyRoomViewMappingResult = $this->propertyRoomViewMappingRepository->createAll($datas); + + if ($propertyRoomViewMappingResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + //Connected Rooms + if ($params['is_connected_room']) { + $syncPropertyConnectedRoomParam = [ + 'property_id' => $params['property_id'], + 'room_id' => $roomId, + 'connected_rooms' => $params['connected_rooms'], + 'user_id' => fillOnUndefined($params, 'user_id') + ]; + + $syncPropertyConnectedRoom = $this->syncPropertyConnectedRoom($syncPropertyConnectedRoomParam); + + if ($syncPropertyConnectedRoom['status'] != 'success') { + throw new Exception($syncPropertyConnectedRoom['message']); + } + } + + //$userData = $insertBedResult["data"]; + $response = [ + 'status' => true, + //'data' => $userData, + ]; + + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function updatePropertyRoomAndBed($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + // check room name, it is null set room type name. + if ($params['name'] == null) { + $propertyRoomTypeCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['room_type_id']], + ], + 'firstRow' => true, + ]; + $propertyType = $this->propertyRoomTypeService->select($propertyRoomTypeCriteria); + $params['name'] = $propertyType['data']['name']; + } + + + $validationResult = $this->propertyRoomAndBedUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $description = []; + foreach (fillOnUndefined($params, "description", []) as $title) { + $description[$title['language_code']] = $title['description']; + } + + if (!$params['is_connected_room']) { + $params['is_connected_room_price'] = null; + $params['is_connected_room_availability'] = null; + $params['connected_rooms'] = []; + } + + $insertRoomData = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'room_type_id' => fillOnUndefined($params, 'room_type_id'), + 'max_occupancy' => fillOnUndefined($params, 'max_occupancy'), + 'max_adult' => fillOnUndefined($params, 'max_adult'), + 'max_child' => fillOnUndefined($params, 'max_child'), + 'occupancy_lock' => fillOnUndefined($params, 'occupancy_lock'), + 'exclude_occupancy' => json_encode($params['exclude_occupancy'], true), + 'room_size' => fillOnUndefined($params, 'room_size', null), + 'room_size_type' => fillOnUndefined($params, 'room_size_type', null), + 'room_type_count' => fillOnUndefined($params, 'room_type_count', 1), + 'room_count' => fillOnUndefined($params, 'room_count', null), + 'bathroom_count' => fillOnUndefined($params, 'bathroom_count', null), + 'toilet_count' => fillOnUndefined($params, 'toilet_count', null), + 'lounge_count' => fillOnUndefined($params, 'lounge_count', null), + 'max_child_number' => fillOnUndefined($params, 'max_child_number', null), + 'is_connected_room' => fillOnUndefined($params, 'is_connected_room', null) ? 1 : null, + 'is_connected_room_price' => fillOnUndefined($params, 'is_connected_room_price', null) ? 1 : null, + 'is_connected_room_availability' => fillOnUndefined($params, 'is_connected_room_availability', null) ? 1 : null, + 'description' => json_encode($description), + "status" => fillOnUndefined($params, "status", 1), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $insertRoomDataResult = $this->propertyRoomRepository->update(fillOnUndefined($params, 'room_id'), $insertRoomData); + + if ($insertRoomDataResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $roomId = $insertRoomDataResult['data']['id']; + $roomBedGroup = fillOnUndefined($params, 'room_bed_group', []); + $bedGroupIndex = 1; + $insertBedData = []; + + + $roomBedCriteria = [ + 'criteria' => [ + ['field' => 'room_id', 'condition' => '=', 'value' => $params['room_id']], + ] + ]; + $roomBeds = $this->propertyRoomBedRepository->findByCriteria($roomBedCriteria); + + $roomBeds = collect($roomBeds)->keyBy('id')->keys()->all(); + if ($roomBeds) { + $destroyStatus = $this->propertyRoomBedRepository->destroy($roomBeds); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + $roomViewCriteria = [ + 'criteria' => [ + ['field' => 'room_id', 'condition' => '=', 'value' => $params['room_id']], + ] + ]; + $roomViews = $this->propertyRoomViewMappingRepository->findByCriteria($roomViewCriteria); + $roomViews = collect($roomViews)->keyBy('id')->keys()->all(); + + if ($roomViews) { + $destroyStatus = $this->propertyRoomViewMappingRepository->destroy($roomViews); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + $isBedGroupInsert = true; + + if (count($roomBedGroup) == 1) { + if (count($roomBedGroup[0]) == 1) { + if (empty($roomBedGroup[0][0]['bed_type_id'])) { + $isBedGroupInsert = false; + } + + } + } + + + if ($isBedGroupInsert) { + + $roomBedGroups = []; + collect($roomBedGroup)->map(function ($value) use (&$roomBedGroups) { + $roomBedGroups[] = collect($value) + ->groupBy('bed_type_id') + ->map(function ($val) { + return $val->count(); + })->toArray(); + }); + + foreach ($roomBedGroups as $bedGroup) { + foreach ($bedGroup as $bedTypeId => $bedTypeCount) { + if (!empty($bedTypeId)) { + $insertBedData[] = + [ + 'room_id' => $roomId, + 'bed_group' => $bedGroupIndex, + 'count' => $bedTypeCount, + 'bed_type_id' => $bedTypeId, + "status" => fillOnUndefined($params, "status", 1), + "created_by" => fillOnUndefined($params, "user_id", 0), + "updated_by" => fillOnUndefined($params, "user_id", 0), + 'created_at' => Carbon::now()->timestamp, + 'updated_at' => Carbon::now()->timestamp, + ]; + } + } + $bedGroupIndex++; + } + + $insertBedResult = $this->propertyRoomBedRepository->insert($insertBedData); + if ($insertBedResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + + if (!empty($params['room_view_type'])) { + + foreach ($params['room_view_type'] as $roomViewType) { + + $datas[] = [ + 'room_id' => $roomId, + 'room_view_type_id' => $roomViewType, + "created_by" => fillOnUndefined($params, "user_id", 0), + "updated_by" => fillOnUndefined($params, "user_id", 0), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $propertyRoomViewMappingResult = $this->propertyRoomViewMappingRepository->insert($datas); + + if ($propertyRoomViewMappingResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } + + + //Connected Rooms + $syncPropertyConnectedRoomParam = [ + 'property_id' => $params['property_id'], + 'room_id' => $roomId, + 'connected_rooms' => $params['connected_rooms'], + 'user_id' => fillOnUndefined($params, 'user_id') + ]; + + + $syncPropertyConnectedRoom = $this->syncPropertyConnectedRoom($syncPropertyConnectedRoomParam); + + if ($syncPropertyConnectedRoom['status'] != 'success') { + throw new Exception($syncPropertyConnectedRoom['message']); + } + + + //$userData = $insertBedResult["data"]; + $response = [ + 'status' => true, + //'data' => $userData, + ]; + + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + } + + public function getPropertyRoomAndBed($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'id', 'condition' => '=', 'value' => $params['room_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyRoomBedGroup', 'propertyRoomViewMapping'], + 'firstRow' => true + ]; + + $data = $this->propertyRoomRepository->findByCriteria($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number', 'description']); + $room = $data; + + $getApplicationLanguages = $this->languageService->getApplicationLanguages(); + if ($getApplicationLanguages['status'] != 'success') { + throw new ApiErrorException($getApplicationLanguages['message']); + } + $applicationLanguages = $getApplicationLanguages['data']; + $descriptionLangContents = json_decode($data['description'], 1); + $responseLangDescription = []; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangDescription[] = [ + 'language_code' => $langKey, + 'description' => isset($descriptionLangContents[$langKey]) ? $descriptionLangContents[$langKey] : null + ]; + } + $room['description'] = $responseLangDescription; + + + $bedGroups = collect($room['property_room_bed_group'])->keyBy('bed_group')->keys(); + $group = []; + + foreach ($bedGroups as $bedGroup) { + $bedTypes = []; + $thisGroup = collect($room['property_room_bed_group'])->where('bed_group', '=', $bedGroup)->map(function ($bedItem) use ($bedGroup) { + $responseMapping = $bedItem; + return $responseMapping; + })->toArray(); + + foreach ($thisGroup as $groupItem) { + for ($i = 1; $i <= $groupItem['count']; $i++) { + $bedTypes[] = ['bed_type_id' => $groupItem['bed_type_id']]; + } + } + $group[] = $bedTypes; + } + $room['property_room_bed_group'] = $group; + $room['property_room_view_mapping'] = collect($room['property_room_view_mapping'])->keyBy('room_view_type_id')->keys()->all(); + + $newRoomMapping = $room; + + $response = [ + 'status' => true, + 'data' => $newRoomMapping, + ]; + + } 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); + } + + public function getChannelRoomRateMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'with' => ['propertyRoomType', 'propertyRoomRateMapping.propertyRoomRateChannel', 'propertyRoomRateMapping.propertyRoomRate'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $roomData = $this->propertyRoomRepository->findByCriteria($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'exclude_occupancy', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number']); + + $return = []; + foreach ($roomData as $room) { + $item = $room; + $item['exclude_occupancy'] = json_decode($room['exclude_occupancy']); + if ($room['property_room_rate_mapping']) { + $roomRateMappings = $room['property_room_rate_mapping']; + $mapping = []; + foreach ($roomRateMappings as $roomRateMapping) { + $propertyRoomRateChannel = null; + if (isset($roomRateMapping['property_room_rate_channel'])) { + $propertyRoomRateChannel = collect($roomRateMapping['property_room_rate_channel']) + ->where('channel_id', '=', $params['channel_id']) + ->where('room_rate_mapping_id', '=', $roomRateMapping['id']) + ->first(); + } + $mappingStatus = false; + if ($propertyRoomRateChannel) { + if ($propertyRoomRateChannel['status'] == 1) { + $mappingStatus = true; + } + } + $roomRateMapping['name'] = $roomRateMapping['property_room_rate']['name']; + $roomRateMapping['min_stay'] = $roomRateMapping['property_room_rate']['min_stay']; + $roomRateMapping['max_stay'] = $roomRateMapping['property_room_rate']['max_stay']; + $roomRateMapping['is_selected'] = $mappingStatus; + $roomRateMapping['has_date'] = $propertyRoomRateChannel['has_date'] == 0 ? false : true; + $roomRateMapping['end_date'] = $propertyRoomRateChannel['end_date']; + $roomRateMapping['start_date'] = $propertyRoomRateChannel['start_date']; + unset($roomRateMapping['property_room_rate']); + unset($roomRateMapping['property_room_rate_channel']); + $mapping[] = $roomRateMapping; + } + $item['property_room_rate_mapping'] = $mapping; + $return[] = $item; + } + + } + + $collection = collect($return); + $sorted = $collection->sortBy('id')->values()->all(); + + $response = [ + 'status' => true, + 'data' => $sorted, + ]; + + } 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); + } + + public function inventoryRoomList($params = []) + { + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + isset($params['room_id']) ? ['field' => 'id', 'condition' => '=', 'value' => $params['room_id']] : null, + ], + 'with' => ['propertyRoomRateMapping.propertyRoomRate', 'propertyRoomRateMapping.propertyRoomRateChannel'], + "orderBy" => [ + ["field" => "id", "value" => "ASC"] + ], + ]; + + $roomData = $this->propertyRoomRepository->findByCriteria($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number']); + + return $roomData ? $roomData : []; + + } + + public function inventoryRoomRateData($params = []) + { + + $roomRatesCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'date', 'condition' => '>=', 'value' => $params['start_date']], + ['field' => 'date', 'condition' => '<=', 'value' => $params['end_date']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ], + 'whereIn' => [ + ['field' => 'availability_type_id', 'value' => $params['availability_type_id']] + ] + ]; + + $roomRatePrice = $this->propertyRoomRatePriceRepository->findbyCriteria($roomRatesCriteria, ['id', 'property_id', 'property_room_id', 'room_rate_mapping_id', 'availability_type_id', 'channel_id', 'min_stay', 'max_stay', 'stop_sell', 'booking_on_request', 'date', 'amount', 'currency']); + $roomRatePrice = $roomRatePrice ? $roomRatePrice : []; + $priceArray = []; + foreach ($roomRatePrice as $price) { + $priceArray[$price['property_room_id'] . '|' . $price['room_rate_mapping_id'] . '|' . $price['availability_type_id'] . '|' . $price['date']] = $price; + } + + return $priceArray; + + } + + public function inventoryAvailabilityData($params = []) + { + + $availabilityTypeIds = implode(",", $params['availability_type_id']); + + $roomAvailability = []; + if(!is_null($params['start_date']) && !is_null($params['end_date'])) { + $roomAvailabilityCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'date', 'condition' => '>=', 'value' => $params['start_date']], + ['field' => 'date', 'condition' => '<=', 'value' => $params['end_date']], + ], + 'criteriaWhereRaw' => + [ + [ + 'query' => " ( `availability_type_id` = 3 OR ( `channel_id` = ? OR `channel_id` IS NULL ) AND `availability_type_id` IN ( " . $availabilityTypeIds . " ) )", + "binds" => [$params['channel_id']] + ] + ], + + ]; + + $roomAvailability = $this->propertyRoomAvailabilityRepository->findbyCriteria($roomAvailabilityCriteria); + } + + $availabilityArray = []; + foreach ($roomAvailability as $availability) { + + $availabilityArrayKey = $availability['property_room_id'] . '|' . $availability['room_rate_mapping_id'] . '|' . $availability['availability_type_id'] . '|' . $availability['channel_id'] . '|' . $availability['date']; + + $availabilityArray[$availabilityArrayKey][] = [ + 'id' => $availability['id'], + 'room_id' => $availability['property_room_id'], + 'room_rate_mapping_id' => $availability['room_rate_mapping_id'], + 'availability_type_id' => $availability['availability_type_id'], + 'channel_id' => $availability['channel_id'], + 'date' => $availability['date'], + 'availability' => $availability['availability'], + 'stop_sell' => $availability['stop_sell'], + ]; + } + + //Sistemdeki benzer id listesi, mysql null unique index hatası için + foreach ($availabilityArray as $availabilityKey => $availability) { + + if(count($availability) > 1) { + + $availabilityArray[$availabilityKey] = last($availability); + $otherAvailabilityRowIds = collect($availability)->whereNotIn('id', [$availabilityArray[$availabilityKey]['id']])->pluck('id')->toArray(); + $availabilityArray[$availabilityKey] = last($availability); + $availabilityArray[$availabilityKey]['otherIds'] = $otherAvailabilityRowIds; + } else { + $availabilityArray[$availabilityKey] = reset($availability); + } + + } + + return $availabilityArray; + + } + + public function getInventory($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $startDate = fillOnUndefinedAndEmpty($params, 'start_date', date('Y-m-d')); + $endDate = fillOnUndefinedAndEmpty($params, 'end_date', Carbon::parse($startDate)->addDay(15)->format('Y-m-d')); + + if ($endDate < $startDate) { + throw new ApiErrorException('date error'); + } + $diffInDays = Carbon::parse($startDate)->diffInDays($endDate); + + if ($diffInDays > 365) { + throw new ApiErrorException('Up to 1 yearly price updates can be made.'); //TODO: param extend + } + + $channelSetupTypes = $this->setChannelAvailabilityTypes(['property_id' => $params['property_id'], 'channel_id' => $params['channel_id']]); + if ($channelSetupTypes['status'] != 'success') { + throw new ApiErrorException($channelSetupTypes['message']); + } + $channelSetupTypes = $channelSetupTypes['data']; + $forGeneralId = $channelSetupTypes['forGeneralId']; + $forAvailabilityData = $channelSetupTypes['forAvailabilityData']; + $forPriceData = $channelSetupTypes['forPriceData']; + $getPriceDataIds = collect($forPriceData)->keyBy('id')->keys()->toArray(); + $getAvailabilityDataIds = collect($forAvailabilityData)->keyBy('id')->keys()->toArray(); + $getAvailabilityDataIds = array_unique(array_merge($forGeneralId, $getAvailabilityDataIds)); + + $generalAvailabilityCheckId = $forGeneralId[0]; + + + $roomData = $this->inventoryRoomList($params); + $priceRequest = [ + 'property_id' => $params['property_id'], + 'start_date' => $startDate, + 'end_date' => $endDate, + 'channel_id' => $params['channel_id'], + 'availability_type_id' => $getPriceDataIds, + ]; + $priceArray = $this->inventoryRoomRateData($priceRequest); + + $availabilityRequest = [ + 'property_id' => $params['property_id'], + 'start_date' => $startDate, + 'end_date' => $endDate, + 'channel_id' => $params['channel_id'], + 'availability_type_id' => $getAvailabilityDataIds, + ]; + + $availabilityArray = $this->inventoryAvailabilityData($availabilityRequest); + $quotaCollection = collect($availabilityArray) + ->where('availability_type_id', '=', 3); + + + $currencyCode = $params['channelData']['currency_code']; + + $return = []; + $keysForDevelopment = []; + + // Fill Rooms + foreach ($roomData as $room) { + $item = $room; + + $roomGeneralAvailability = collect($availabilityArray) + ->where('room_id', '=', $room['id']) + ->where('availability_type_id', '=', 1) + ->where('room_rate_mapping_id', '=', null) + ->where('channel_id', '=', null) + ->toArray(); + + // Fill General availability Keys + $startDate = Carbon::parse($params['start_date']); + $roomAvailability = []; + $quotaAvailability = []; + $roomStopSell = []; + for ($i = 0; $i <= $diffInDays; $i++) { + $checkKey = $room['id'] . '||' . $generalAvailabilityCheckId . "||" . $startDate->format('Y-m-d'); + $responseKey = 'AVA_' . $generalAvailabilityCheckId . '_' . $room['id'] . '_' . '0' . '_' . $startDate->format('Ymd'); + $responseKeyStopSell = 'STS_' . $generalAvailabilityCheckId . '_' . $room['id'] . '_' . '0' . '_' . $startDate->format('Ymd'); + + $usedAvailability = $quotaCollection + ->where('date', '=', $startDate->format('Y-m-d')) + ->where('room_id', '=', $room['id']) + ->sum('availability'); + + $remainingAvailability = isset($roomGeneralAvailability[$checkKey]['availability']) ? ($roomGeneralAvailability[$checkKey]['availability'] - $usedAvailability) : 0; + + if (isset($roomGeneralAvailability[$checkKey])) { + $roomAvailability[$startDate->format('Y-m-d')] = [ + 'key' => $responseKey, + 'value' => $roomGeneralAvailability[$checkKey]['availability'], + 'stop_sell' => $roomGeneralAvailability[$checkKey]['stop_sell'] + ]; + + $roomStopSell[$startDate->format('Y-m-d')] = [ + 'key' => $responseKeyStopSell, + 'value' => $roomGeneralAvailability[$checkKey]['stop_sell'], + ]; + + $keysForDevelopment[$responseKey] = $roomGeneralAvailability[$checkKey]['availability']; + $keysForDevelopment[$responseKeyStopSell] = $roomGeneralAvailability[$checkKey]['stop_sell']; + } else { + $roomAvailability[$startDate->format('Y-m-d')] = [ + 'key' => $responseKey, + 'value' => null, + 'stop_sell' => 0 + ]; + + $roomStopSell[$startDate->format('Y-m-d')] = [ + 'key' => $responseKeyStopSell, + 'value' => 0, + ]; + + $keysForDevelopment[$responseKey] = rand(1, 10); + $keysForDevelopment[$responseKeyStopSell] = 0; + } + + $responseRMKey = 'RMA_' . $generalAvailabilityCheckId . '_' . $room['id'] . '_' . '0' . '_' . $startDate->format('Ymd'); + $quotaAvailability[$startDate->format('Y-m-d')] = [ + 'key' => $responseRMKey, + 'value' => $remainingAvailability, + ]; + + + $startDate = $startDate->addDay(); + } + $item['room_availability'] = $roomAvailability; + $item['remaining_availability'] = $quotaAvailability; + $item['room_stop_sell'] = $roomStopSell; + + // Fill Room Rate Array + if (isset($room['property_room_rate_mapping'])) { + $roomRateMappings = $room['property_room_rate_mapping']; + if (isset($params['room_rate_mapping_id'])) { + $roomRateMappings = collect($roomRateMappings) + ->where('id', '=', $params['room_rate_mapping_id']) + ->toArray(); + } + $mapping = []; + foreach ($roomRateMappings as $roomRateMapping) { + + $checkMappingStatus = collect($roomRateMapping['property_room_rate_channel']) + ->where('channel_id', '=', $params['channel_id']) + ->where('status', '=', 1) + ->first(); + + if ($checkMappingStatus) { + $roomRateMapping['name'] = $roomRateMapping['property_room_rate']['name']; + $roomRateMapping['min_stay'] = $roomRateMapping['property_room_rate']['min_stay']; + $roomRateMapping['max_stay'] = $roomRateMapping['property_room_rate']['max_stay']; + $roomRateMapping['currency_code'] = $currencyCode; + unset($roomRateMapping['property_room_rate']); + foreach ($forPriceData as $roomRateAvailabilityCheckId) { + $startDate = Carbon::parse($params['start_date']); + $prices[$roomRateAvailabilityCheckId['id']]['name'] = $roomRateAvailabilityCheckId['name']; + $prices[$roomRateAvailabilityCheckId['id']]['language_key'] = $roomRateAvailabilityCheckId['language_key']; + for ($i = 0; $i <= $diffInDays; $i++) { + $checkKey = $roomRateMapping['room_id'] . "|" . $roomRateMapping['id'] . "|" . $roomRateAvailabilityCheckId['id'] . "|" . $startDate->format('Y-m-d'); + $responsePRCKey = 'PRC_' . $roomRateAvailabilityCheckId['id'] . '_' . $roomRateMapping['room_id'] . '_' . $roomRateMapping['id'] . '_' . $startDate->format('Ymd'); + $responseSTSKey = 'STS_' . $roomRateAvailabilityCheckId['id'] . '_' . $roomRateMapping['room_id'] . '_' . $roomRateMapping['id'] . '_' . $startDate->format('Ymd'); + $responseMNSKey = 'MNS_' . $roomRateAvailabilityCheckId['id'] . '_' . $roomRateMapping['room_id'] . '_' . $roomRateMapping['id'] . '_' . $startDate->format('Ymd'); + + // Fill Price Keys + if (isset($priceArray[$checkKey])) { + $prices[$roomRateAvailabilityCheckId['id']]['price'][$startDate->format('Y-m-d')] = [ + 'key' => $responsePRCKey, + 'value' => $priceArray[$checkKey]['amount'], + 'stop_sell' => $priceArray[$checkKey]['stop_sell'], + ]; + + $prices[$roomRateAvailabilityCheckId['id']]['stop_sell'][$startDate->format('Y-m-d')] = [ + 'key' => $responseSTSKey, + 'value' => $priceArray[$checkKey]['stop_sell'], + ]; + + $prices[$roomRateAvailabilityCheckId['id']]['min_stay'][$startDate->format('Y-m-d')] = [ + 'key' => $responseMNSKey, + 'value' => $priceArray[$checkKey]['min_stay'], + ]; + + $keysForDevelopment[$responsePRCKey] = $priceArray[$checkKey]['amount']; + } else { + $prices[$roomRateAvailabilityCheckId['id']]['price'][$startDate->format('Y-m-d')] = [ + 'key' => $responsePRCKey, + 'value' => null, + 'stop_sell' => 0 + ]; + + $prices[$roomRateAvailabilityCheckId['id']]['stop_sell'][$startDate->format('Y-m-d')] = [ + 'key' => $responseSTSKey, + 'value' => 0, + ]; + + $prices[$roomRateAvailabilityCheckId['id']]['min_stay'][$startDate->format('Y-m-d')] = [ + 'key' => $responseMNSKey, + 'value' => 1, + ]; + + $keysForDevelopment[$responsePRCKey] = rand(1, 10); + } + + $startDate = $startDate->addDay(); + } + } + $roomRateMapping['prices'] = $prices; + $availabilities = []; + $prices = []; + foreach ($forAvailabilityData as $roomRateAvailabilityCheckId) { + $startDate = Carbon::parse($params['start_date']); + $availabilities[$roomRateAvailabilityCheckId['id']]['name'] = $roomRateAvailabilityCheckId['name']; + $availabilities[$roomRateAvailabilityCheckId['id']]['language_key'] = $roomRateAvailabilityCheckId['language_key']; + for ($i = 0; $i <= $diffInDays; $i++) { + $checkKey = $roomRateMapping['room_id'] . "|" . $roomRateMapping['id'] . "|" . $roomRateAvailabilityCheckId['id'] . "|" . $params['channel_id'] . "|" . $startDate->format('Y-m-d'); + $responseAVAKey = 'AVA_' . $roomRateAvailabilityCheckId['id'] . '_' . $roomRateMapping['room_id'] . '_' . $roomRateMapping['id'] . '_' . $startDate->format('Ymd'); + + // Fill Availability Keys + if (isset($availabilityArray[$checkKey])) { + $availabilities[$roomRateAvailabilityCheckId['id']]['availability'][$startDate->format('Y-m-d')] = [ + 'key' => $responseAVAKey, + 'value' => $availabilityArray[$checkKey]['availability'], + ]; + $keysForDevelopment[$responseAVAKey] = $availabilityArray[$checkKey]['availability']; + } else { + $availabilities[$roomRateAvailabilityCheckId['id']]['availability'][$startDate->format('Y-m-d')] = [ + 'key' => $responseAVAKey, + 'value' => null, + ]; + $keysForDevelopment[$responseAVAKey] = rand(1, 10); + } + $startDate = $startDate->addDay(); + } + } + $roomRateMapping['availabilities'] = $availabilities; + $mapping[] = $roomRateMapping; + } + + + } + $item['property_room_rate_mapping'] = $mapping; + if ($mapping) { + $return[] = $item; + } + } + + } + // log::debug(json_encode($keysForDevelopment)); + $response = [ + 'status' => true, + 'data' => $return, + ]; + + } 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); + } + + public function setChannelAvailabilityTypes($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + // Get Channel Setup + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'channel_id')], + ], + 'firstRow' => 1 + ]; + + $channelSetupData = $this->propertyChannelMappingRepository->findByCriteria($criteria); + if (!$channelSetupData) { + throw new ApiErrorException('Channel mapping not found'); + } + + $propertyChannelSetupTypeId = $channelSetupData['property_availability_type_id']; + // Get All Availability types + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $allAvailabilityTypes = $this->propertyAvailabilityTypeRepository->findByCriteria($criteria); + if (!$allAvailabilityTypes) { + throw new ApiErrorException('Availability types not found'); + } + + switch ($propertyChannelSetupTypeId) { + default : + $forGeneralId = [1]; + $forAvailabilityId = []; + $forPriceId = [1]; + break; + case 2: + $forGeneralId = [1]; + $forAvailabilityId = [2]; + $forPriceId = [2]; + break; + case 3: + $forGeneralId = [1]; + $forAvailabilityId = [3]; + $forPriceId = [1, 3]; + break; + } + + $forGeneralData = $forGeneralId ? collect($allAvailabilityTypes)->whereIn('id', $forGeneralId)->toArray() : []; + $forAvailabilityData = $forGeneralId ? collect($allAvailabilityTypes)->whereIn('id', $forAvailabilityId)->toArray() : []; + $forPriceData = $forGeneralId ? collect($allAvailabilityTypes)->whereIn('id', $forPriceId)->toArray() : []; + + $returnData = [ + 'forGeneralId' => $forGeneralId, + 'forGeneralData' => $forGeneralData, + 'forAvailabilityData' => $forAvailabilityData, + 'forPriceData' => $forPriceData, + 'channelAvailabilityWorkingType' => $propertyChannelSetupTypeId + ]; + + $response = [ + 'status' => true, + 'data' => $returnData, + ]; + + + } 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); + + + } + + public function roomIncludeOccupancies($params = []) + { + $occupancies = []; + for ($i = 1; $i <= $params['max_adult']; $i++) { + for ($j = 0; $j <= $params['max_child']; $j++) { + $paxTotal = $i + $j; + if ($paxTotal > $params['max_occupancy']) { + continue; + } + $occupancy = str_repeat('A', $i) . str_repeat('C', $j); + if (array_search($occupancy, $params['exclude_occupancy']) > -1) { + continue; + } + $occupancies[] = $occupancy; + } + } + return $occupancies; + } + + public function getGroupInventory($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $startDate = fillOnUndefinedAndEmpty($params, 'start_date', date('Y-m-d')); + $endDate = fillOnUndefinedAndEmpty($params, 'end_date', Carbon::parse($startDate)->addDay(15)->format('Y-m-d')); + + if ($endDate < $startDate) { + throw new ApiErrorException('date error'); + } + $diffInDays = Carbon::parse($startDate)->diffInDays($endDate); + if ($diffInDays > 15) { + throw new ApiErrorException('date error'); + } + + + $roomData = $this->inventoryRoomList($params); + + $priceRequest = [ + 'property_id' => $params['property_id'], + 'start_date' => $startDate, + 'end_date' => $endDate, + 'channel_id' => $params['channel_id'], + 'availability_type_id' => 1, + ]; + + $availabilityRequest = [ + 'property_id' => $params['property_id'], + 'start_date' => $startDate, + 'end_date' => $endDate, + 'channel_id' => $params['channel_id'], + 'availability_type_id' => 1, + ]; + + $availabilityArray = $this->inventoryAvailabilityData($availabilityRequest); + $quotaCollection = collect($availabilityArray) + ->where('availability_type_id', '=', 3); + + + $currencyCode = $params['channelData']['currency_code']; + + $return = []; + $keysForDevelopment = []; + + // Fill Rooms + foreach ($roomData as $room) { + $item = $room; + + $roomGeneralAvailability = collect($availabilityArray) + ->where('room_id', '=', $room['id']) + ->where('availability_type_id', '=', 1) + ->where('room_rate_mapping_id', '=', null) + ->where('channel_id', '=', null) + ->toArray(); + + // Fill General availability Keys + $startDate = Carbon::parse($params['start_date']); + $roomAvailability = []; + $quotaAvailability = []; + $roomStopSell = []; + for ($i = 0; $i <= $diffInDays; $i++) { + $checkKey = $room['id'] . '||' . $generalAvailabilityCheckId . "||" . $startDate->format('Y-m-d'); + $responseKey = 'AVA_' . $generalAvailabilityCheckId . '_' . $room['id'] . '_' . '0' . '_' . $startDate->format('Ymd'); + $responseKeyStopSell = 'STS_' . $generalAvailabilityCheckId . '_' . $room['id'] . '_' . '0' . '_' . $startDate->format('Ymd'); + + $usedAvailability = $quotaCollection + ->where('date', '=', $startDate->format('Y-m-d')) + ->where('room_id', '=', $room['id']) + ->sum('availability'); + + $remainingAvailability = isset($roomGeneralAvailability[$checkKey]['availability']) ? ($roomGeneralAvailability[$checkKey]['availability'] - $usedAvailability) : 0; + + if (isset($roomGeneralAvailability[$checkKey])) { + $roomAvailability[$startDate->format('Y-m-d')] = [ + 'key' => $responseKey, + 'value' => $roomGeneralAvailability[$checkKey]['availability'], + 'stop_sell' => $roomGeneralAvailability[$checkKey]['stop_sell'] + ]; + + $roomStopSell[$startDate->format('Y-m-d')] = [ + 'key' => $responseKeyStopSell, + 'value' => $roomGeneralAvailability[$checkKey]['stop_sell'], + ]; + + $keysForDevelopment[$responseKey] = $roomGeneralAvailability[$checkKey]['availability']; + $keysForDevelopment[$responseKeyStopSell] = $roomGeneralAvailability[$checkKey]['stop_sell']; + } else { + $roomAvailability[$startDate->format('Y-m-d')] = [ + 'key' => $responseKey, + 'value' => null, + 'stop_sell' => 0 + ]; + + $roomStopSell[$startDate->format('Y-m-d')] = [ + 'key' => $responseKeyStopSell, + 'value' => 0, + ]; + + $keysForDevelopment[$responseKey] = rand(1, 10); + $keysForDevelopment[$responseKeyStopSell] = 0; + } + + $responseRMKey = 'RMA_' . $generalAvailabilityCheckId . '_' . $room['id'] . '_' . '0' . '_' . $startDate->format('Ymd'); + $quotaAvailability[$startDate->format('Y-m-d')] = [ + 'key' => $responseRMKey, + 'value' => $remainingAvailability, + ]; + + + $startDate = $startDate->addDay(); + } + $item['room_availability'] = $roomAvailability; + $item['remaining_availability'] = $quotaAvailability; + $item['room_stop_sell'] = $roomStopSell; + + // Fill Room Rate Array + if (isset($room['property_room_rate_mapping'])) { + $roomRateMappings = $room['property_room_rate_mapping']; + if (isset($params['room_rate_mapping_id'])) { + $roomRateMappings = collect($roomRateMappings) + ->where('id', '=', $params['room_rate_mapping_id']) + ->toArray(); + } + $mapping = []; + foreach ($roomRateMappings as $roomRateMapping) { + + $checkMappingStatus = collect($roomRateMapping['property_room_rate_channel']) + ->where('channel_id', '=', $params['channel_id']) + ->where('status', '=', 1) + ->first(); + + if ($checkMappingStatus) { + $roomRateMapping['name'] = $roomRateMapping['property_room_rate']['name']; + $roomRateMapping['min_stay'] = $roomRateMapping['property_room_rate']['min_stay']; + $roomRateMapping['max_stay'] = $roomRateMapping['property_room_rate']['max_stay']; + $roomRateMapping['currency_code'] = $currencyCode; + unset($roomRateMapping['property_room_rate']); + foreach ($forPriceData as $roomRateAvailabilityCheckId) { + $startDate = Carbon::parse($params['start_date']); + $prices[$roomRateAvailabilityCheckId['id']]['name'] = $roomRateAvailabilityCheckId['name']; + $prices[$roomRateAvailabilityCheckId['id']]['language_key'] = $roomRateAvailabilityCheckId['language_key']; + for ($i = 0; $i <= $diffInDays; $i++) { + $checkKey = $roomRateMapping['room_id'] . "|" . $roomRateMapping['id'] . "|" . $roomRateAvailabilityCheckId['id'] . "|" . $startDate->format('Y-m-d'); + $responsePRCKey = 'PRC_' . $roomRateAvailabilityCheckId['id'] . '_' . $roomRateMapping['room_id'] . '_' . $roomRateMapping['id'] . '_' . $startDate->format('Ymd'); + $responseSTSKey = 'STS_' . $roomRateAvailabilityCheckId['id'] . '_' . $roomRateMapping['room_id'] . '_' . $roomRateMapping['id'] . '_' . $startDate->format('Ymd'); + $responseMNSKey = 'MNS_' . $roomRateAvailabilityCheckId['id'] . '_' . $roomRateMapping['room_id'] . '_' . $roomRateMapping['id'] . '_' . $startDate->format('Ymd'); + + // Fill Price Keys + if (isset($priceArray[$checkKey])) { + $prices[$roomRateAvailabilityCheckId['id']]['price'][$startDate->format('Y-m-d')] = [ + 'key' => $responsePRCKey, + 'value' => $priceArray[$checkKey]['amount'], + 'stop_sell' => $priceArray[$checkKey]['stop_sell'], + ]; + + $prices[$roomRateAvailabilityCheckId['id']]['stop_sell'][$startDate->format('Y-m-d')] = [ + 'key' => $responseSTSKey, + 'value' => $priceArray[$checkKey]['stop_sell'], + ]; + + $prices[$roomRateAvailabilityCheckId['id']]['min_stay'][$startDate->format('Y-m-d')] = [ + 'key' => $responseMNSKey, + 'value' => $priceArray[$checkKey]['min_stay'], + ]; + + $keysForDevelopment[$responsePRCKey] = $priceArray[$checkKey]['amount']; + } else { + $prices[$roomRateAvailabilityCheckId['id']]['price'][$startDate->format('Y-m-d')] = [ + 'key' => $responsePRCKey, + 'value' => null, + 'stop_sell' => 0 + ]; + + $prices[$roomRateAvailabilityCheckId['id']]['stop_sell'][$startDate->format('Y-m-d')] = [ + 'key' => $responseSTSKey, + 'value' => 0, + ]; + + $prices[$roomRateAvailabilityCheckId['id']]['min_stay'][$startDate->format('Y-m-d')] = [ + 'key' => $responseMNSKey, + 'value' => 1, + ]; + + $keysForDevelopment[$responsePRCKey] = rand(1, 10); + } + + $startDate = $startDate->addDay(); + } + } + $roomRateMapping['prices'] = $prices; + $availabilities = []; + $prices = []; + foreach ($forAvailabilityData as $roomRateAvailabilityCheckId) { + $startDate = Carbon::parse($params['start_date']); + $availabilities[$roomRateAvailabilityCheckId['id']]['name'] = $roomRateAvailabilityCheckId['name']; + $availabilities[$roomRateAvailabilityCheckId['id']]['language_key'] = $roomRateAvailabilityCheckId['language_key']; + for ($i = 0; $i <= $diffInDays; $i++) { + $checkKey = $roomRateMapping['room_id'] . "|" . $roomRateMapping['id'] . "|" . $roomRateAvailabilityCheckId['id'] . "|" . $params['channel_id'] . "|" . $startDate->format('Y-m-d'); + $responseAVAKey = 'AVA_' . $roomRateAvailabilityCheckId['id'] . '_' . $roomRateMapping['room_id'] . '_' . $roomRateMapping['id'] . '_' . $startDate->format('Ymd'); + + // Fill Availability Keys + if (isset($availabilityArray[$checkKey])) { + $availabilities[$roomRateAvailabilityCheckId['id']]['availability'][$startDate->format('Y-m-d')] = [ + 'key' => $responseAVAKey, + 'value' => $availabilityArray[$checkKey]['availability'], + ]; + $keysForDevelopment[$responseAVAKey] = $availabilityArray[$checkKey]['availability']; + } else { + $availabilities[$roomRateAvailabilityCheckId['id']]['availability'][$startDate->format('Y-m-d')] = [ + 'key' => $responseAVAKey, + 'value' => null, + ]; + $keysForDevelopment[$responseAVAKey] = rand(1, 10); + } + $startDate = $startDate->addDay(); + } + } + $roomRateMapping['availabilities'] = $availabilities; + $mapping[] = $roomRateMapping; + } + + + } + $item['property_room_rate_mapping'] = $mapping; + if ($mapping) { + $return[] = $item; + } + } + + } + // log::debug(json_encode($keysForDevelopment)); + $response = [ + 'status' => true, + 'data' => $return, + ]; + + } 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); + } + + + public function getParamsForPdfInvenyory($params = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $criteria = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => $params['token']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $mapping = $this->propertyChannelMappingRepository->findbyCriteria($criteria); + if (!isset($mapping)) { + throw new ApiErrorException('Channel mapping not found'); + } + + + $startDate = date('Y-m') . "-01"; + + $return = [ + 'property_id' => $mapping['property_id'], + 'channel_id' => $mapping['channel_id'], + 'start_date' => $startDate, + 'end_date' => Carbon::parse($startDate)->addMonth(6)->format('Y-m-d'), + ]; + + + $response = [ + 'status' => true, + 'data' => $return, + ]; + + } 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); + + } + + public function syncPropertyConnectedRoom($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $deletePropertyRoomConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ] + ]; + + $deletePropertyRoomConnected = $this->propertyRoomConnectedRepository->delete($deletePropertyRoomConnectedCriteria); + + if (!empty($params['connected_rooms'])) { + + if (in_array($params['room_id'], $params['connected_rooms'])) { + throw new ApiErrorException('The room itself cannot be defined as a connected room.'); + } + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'whereIn' => [ + ['field' => 'id', 'value' => $params['connected_rooms']], + ] + ]; + + $rooms = $this->propertyRoomRepository->findByCriteria($criteria, ['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'room_size', 'room_size_type', 'room_type_count', 'room_count', 'bathroom_count', 'toilet_count', 'lounge_count', 'max_child_number', 'description']); + + if (count($rooms) != count($params['connected_rooms'])) { + throw new ApiErrorException('Connected rooms are not available for this property.'); + } + + foreach ($rooms as $connectedRoom) { + + $connectedRoomCreateParam = [ + 'property_id' => $params['property_id'], + 'room_id' => $params['room_id'], + 'connected_room_id' => $connectedRoom['id'], + 'created_by' => $params['user_id'], + 'updated_by' => $params['user_id'], + ]; + + $connectedRoomCreate = $this->propertyRoomConnectedRepository->create($connectedRoomCreateParam); + + if ($connectedRoomCreate['status'] != 'success') { + throw new Exception('Could not add connecting rooms.'); + } + + } + + } + + $response = [ + 'status' => true + ]; + + } 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); + } + +} diff --git a/app/Core/Service/PropertyRoomSizeTypeService.php b/app/Core/Service/PropertyRoomSizeTypeService.php new file mode 100644 index 0000000..0a5664d --- /dev/null +++ b/app/Core/Service/PropertyRoomSizeTypeService.php @@ -0,0 +1,44 @@ +propertyRoomSizeTypeRepository = $propertyRoomSizeTypeRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomSizeTypeRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } +} diff --git a/app/Core/Service/PropertyRoomTypeService.php b/app/Core/Service/PropertyRoomTypeService.php new file mode 100644 index 0000000..a91a1a8 --- /dev/null +++ b/app/Core/Service/PropertyRoomTypeService.php @@ -0,0 +1,252 @@ +propertyRoomTypeRepository = $propertyRoomTypeRepository; + $this->propertyRoomRepository = $propertyRoomRepository; + + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "name" => fillOnUndefined($param, "name"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->propertyRoomTypeRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomTypeRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRoomTypeRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyRoomTypeRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + public function getPropertyRoomTypes($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + ]; + + $data = $this->propertyRoomTypeRepository->findByCriteria($criteria, ['id', 'name', 'language_key']); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyMappedRoomTypes($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $propertyRoomCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ] + ]; + $propertyRoomData = $this->propertyRoomRepository->findByCriteria($propertyRoomCriteria, ['id', 'name', 'room_type_id']); + $propertyRoomData = $propertyRoomData ? $propertyRoomData : []; + $mappedRoomTypes = collect($propertyRoomData)->keyBy("room_type_id")->keys()->toArray(); + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'whereIn' => [ + ['field' => 'id', 'value' => $mappedRoomTypes] + ], + ]; + + $roomTypeData = $this->propertyRoomTypeRepository->findByCriteria($criteria, ['id', 'name', 'language_key']); + + $response = [ + 'status' => true, + 'data' => $roomTypeData, + ]; + + } 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); + } + + public function addPropertyRoomTypes($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + ]; + + $data = $this->propertyRoomTypeRepository->findByCriteria($criteria, ['id', 'name']); + + $response = [ + 'status' => true, + 'data' => $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); + } + + +} diff --git a/app/Core/Service/PropertyRoomViewTypeService.php b/app/Core/Service/PropertyRoomViewTypeService.php new file mode 100644 index 0000000..dc3180b --- /dev/null +++ b/app/Core/Service/PropertyRoomViewTypeService.php @@ -0,0 +1,58 @@ +propertyRoomViewTypeRepository = $propertyRoomViewTypeRepository; + + } + + public function getPropertyRoomViewTypes($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRoomViewTypes = $this->propertyRoomViewTypeRepository->findByCriteria($params, ['id', 'name', 'language_key']); + + if (empty($propertyRoomViewTypes)) { + throw new ApiErrorException(lang('Property Room View Type Not Found.')); + } + + $response = [ + 'status' => true, + 'data' => $propertyRoomViewTypes, + ]; + + } 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); + } + + +} diff --git a/app/Core/Service/PropertyService.php b/app/Core/Service/PropertyService.php new file mode 100644 index 0000000..6bad724 --- /dev/null +++ b/app/Core/Service/PropertyService.php @@ -0,0 +1,690 @@ +request = $request; + $this->userPropertyMappingService = $userPropertyMappingService; + $this->propertyAdditionalInfoService = $propertyAdditionalInfoService; + $this->propertyRepository = $propertyRepository; + $this->propertySearchValidator = $propertySearchValidator; + $this->propertyCreateValidator = $propertyCreateValidator; + $this->permissionService = $permissionService; + $this->propertyUpdateValidator = $propertyUpdateValidator; + $this->languageService = $languageService; + $this->notificationService = $notificationService; + $this->restClient = $restClient; + $this->minimumAgePolicies = [ + [ + 'value' => 0, + 'language_key' => 'enw-input-all_ages' + ], + + ]; + $this->propertyLanguageSpokenRepository = $propertyLanguageSpokenRepository; + + for ($i = 1; $i <= 18; $i++) { + $this->minimumAgePolicies[] = [ + 'value' => $i, + 'language_key' => "{$i}+", + ]; + } + } + + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->propertyRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyData = + [ + "name" => fillOnUndefined($param, "name"), + "country" => fillOnUndefined($param, "country"), + "property_type_id" => fillOnUndefined($param, "property_type_id"), + "chain_id" => fillOnUndefined($param, "chain_id"), + "rating" => fillOnUndefined($param, "rating"), + "official_name" => fillOnUndefined($param, "official_name"), + "tax_office" => fillOnUndefined($param, "tax_office"), + "tax_number" => fillOnUndefined($param, "tax_number"), + "currency_type" => fillOnUndefined($param, "currency_type"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + + ]; + + $validationResult = $this->propertyCreateValidator->validate($propertyData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $propertyCreateResult = $this->propertyRepository->create($propertyData); + + if ($propertyCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $propertyCreateResult["data"] + ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->propertyRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getProperty($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + //['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyLanguageSpoken'], + 'firstRow' => true + + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $propertyData = $this->select($propertyRequest, $getPropertyFields); + + $propertyLanguageSpokenArray = collect($propertyData['data']['property_language_spoken'])->keyBy('language_code')->keys()->all(); + $languageSpokenRequest['selected_languages'] = $propertyLanguageSpokenArray; + + $languageSpokenData = $this->languageService->propertyLanguageSpoken($languageSpokenRequest); + if ($languageSpokenData['status'] != 'success') { + throw new ApiErrorException($languageSpokenData['message']); + } + + if ($propertyData['data']['chain_id'] === NULL) { + $propertyData['data']['chain_id'] = self::CHAIN_ID; + } + $return['property_info'] = $propertyData['data']; + unset($return['property_info']['property_language_spoken']); + $return['property_info']['has_locale_name'] = false; + $return['property_language_spoken'] = $languageSpokenData['data']; + + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + + if (isset($propertyAdditionalInfo['data'])) { + $return['additional_info'] = $propertyAdditionalInfo['data']; + + if (isset($return['additional_info']['locale_name']) && $return['additional_info']['locale_name']) { + $return['property_info']['has_locale_name'] = true; + } + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['get_property' => $return, 'minimum_age_policies' => $this->minimumAgePolicies]]; + + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function getPropertyList($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'user_id', 'condition' => '=', 'value' => $params['user_id']], + ], + 'with' => ['property.defaultPropertyPhoto'] + ]; + $propertyData = $this->userPropertyMappingService->select($propertyRequest); + + if ($propertyData['status'] != 'success') { + throw new ApiErrorException(lang('Property data not loaded')); + } + + $propertyList = collect($propertyData['data'])->map(function ($value) use ($params) { + + $menuParams = [ + 'user_id' => $params['user_id'], + 'property_id' => $value['property']['id'], + 'locale' => $params['locale'] + ]; + + if (is_array($value['property']) && $value['property']['status'] != 0) { + $defaultPhoto = isset($value['property']['default_property_photo']) ? $value['property']['default_property_photo'] : null; + + $photoUrlThumbFilePath = '/assets/img/placeholder.png'; + if (isset($defaultPhoto['photo_name'])) { + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$value['property']['id']}" . "/{$defaultPhoto['photo_name']}_200x200.{$defaultPhoto['file_ext']}"; + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$value['property']['id']}" . "/{$defaultPhoto['photo_name']}_200x200.{$defaultPhoto['file_ext']}"; + } else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$value['property']['id']}" . "/{$defaultPhoto['photo_name']}_thumbnail.{$defaultPhoto['file_ext']}"; + } + } + + + return $value['property'] = [ + 'id' => $value['property']['id'], + 'name' => $value['property']['name'], + 'default_photo' => $photoUrlThumbFilePath, + 'property_menu' => $this->permissionService->getMenuTreeForUser($menuParams) + ]; + } + })->toArray(); + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['property_list' => $propertyList]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function propertyUpdate($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + DB::beginTransaction(); + $return = []; + + $propertyData = fillOnUndefined($params, 'property_info'); + + $validationResult = $this->propertyUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $updateData = []; + $validateKeys = ['name', 'property_type_id', 'chain_id', 'rating', 'currency_type', 'currency_type', 'checkin_time', 'checkout_time', 'official_name', 'tax_office', 'tax_number', 'country', 'content_code']; + foreach ($propertyData as $key => $value) { + + if (!in_array($key, $validateKeys)) { + throw new ApiErrorException(lang('Error Columns')); + } + $updateData[$key] = $value; + } + + if ($updateData) { + $updateData['updated_by'] = $params['user_id']; + $updateData['updated_at'] = time(); + } + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'firstRow' => true + ]; + + $propertyData = $this->propertyRepository->findByCriteria($propertyRequest, ['status']); + + if (!empty($propertyData)) { + if ($propertyData['status'] === 2) { + $updateData['status'] = 1; + } + } + + $propertyUpdateResult = $this->update($params['property_id'], $updateData); + + if ($propertyUpdateResult['status'] != 'success') { + throw new ApiErrorException($propertyUpdateResult['message']); + } + + $return['property_info'] = $propertyUpdateResult['data']; + $return['property_info']['has_locale_name'] = false; + + + $propertyLanguageSpokeRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + ]; + $propertyLanguageEraseData = $this->propertyLanguageSpokenRepository->findByCriteria($propertyLanguageSpokeRequest, ['id']); + $propertyLanguageEraseData = $propertyLanguageEraseData ? $propertyLanguageEraseData : []; + $deleteThisIds = collect($propertyLanguageEraseData)->keyBy('id')->keys()->all(); + + if ($deleteThisIds) { + $deleteThisArray = $this->propertyLanguageSpokenRepository->destroy($deleteThisIds); + if ($deleteThisArray['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $insertPropertyLanguageSpoken = []; + + foreach ($params['property_language_spoken'] as $langCode) { + + $insertItem = [ + 'property_id' => $params['property_id'], + 'language_code' => $langCode, + 'status' => 1, + 'created_by' => $params['user_id'], + 'updated_by' => $params['user_id'], + 'created_at' => time(), + 'updated_at' => time(), + ]; + $insertPropertyLanguageSpoken[] = $insertItem; + + } + + $insertRoomRatePrices = $this->propertyLanguageSpokenRepository->insert($insertPropertyLanguageSpoken); + if ($insertRoomRatePrices['status'] != 'success') { + throw new ApiErrorException($insertRoomRatePrices['message']); + } + + + $updateAdditionalInfoData = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'additional_info' => fillOnUndefined($params, 'additional_info'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $params['user_id'], + ]; + + $updateAdditionalInfo = $this->propertyAdditionalInfoService->updateOrCreatePropertyAdditionalInfo($updateAdditionalInfoData); + + if ($updateAdditionalInfo['status'] != 'success') { + throw new ApiErrorException($updateAdditionalInfo['message']); + } + + if (!$params['has_locale_name']) { + + $deleteRequest = [ + 'key_array' => ['locale_name_language', 'locale_name'], + 'property_id' => $params['property_id'] + ]; + $this->propertyAdditionalInfoService->deletePropertyAdditionalInfos($deleteRequest); + } + + + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + + + if (isset($propertyAdditionalInfo['data'])) { + $return['additional_info'] = $propertyAdditionalInfo['data']; + + if (isset($return['additional_info']['locale_name']) && $return['additional_info']['locale_name_language']) { + $return['property_info']['has_locale_name'] = true; + } + + } + + $languageSpokenRequest['selected_languages'] = $params['property_language_spoken'] ? $params['property_language_spoken'] : []; + + $languageSpokenData = $this->languageService->propertyLanguageSpoken($languageSpokenRequest); + if ($languageSpokenData['status'] != 'success') { + throw new ApiErrorException($languageSpokenData['message']); + } + $return['property_language_spoken'] = $languageSpokenData['data']; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['get_property' => $return]]; + DB::commit(); + + } catch + (ApiErrorException $e) { + DB::rollBack(); + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + //PUSH NOTIFICATION + $newBookingNotificationParam = ['property_id' => $params['property_id']]; + $this->notificationService->sendLogNotification($newBookingNotificationParam); + //PUSH NOTIFICATION + } + + + return output($response); + } + + public function getPropertyDetail($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + $return = []; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyType', 'propertyChain'], + 'firstRow' => true + + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type']; + $propertyData = $this->select($propertyRequest, $getPropertyFields); + + $return = $propertyData['data']; + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + + if (isset($propertyAdditionalInfo['data'])) { + $return['additional_info'] = $propertyAdditionalInfo['data']; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function kommoApiPost($method, $payloadJson, $token = null) + { + + $response = ['status' => false, 'message' => '', 'data' => null]; + try { + + $headers['Content-Type'] = 'application/json'; + $headers['Authorization'] = 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImE5ZWMxNDUwMjM0NDQ5NWZiZjY0YjVmNTBkZWQzODMzYWQyMGM3NGZmYWIyZWIyMmE4NTg5ZjBlMjk5Njg4NWQzYzI1MGUyMmEzMzM0Njc2In0.eyJhdWQiOiJiM2E4ZmI2NC0yODRkLTQ2ODctYTFhMC0yMGMwMDY0MWEwN2YiLCJqdGkiOiJhOWVjMTQ1MDIzNDQ0OTVmYmY2NGI1ZjUwZGVkMzgzM2FkMjBjNzRmZmFiMmViMjJhODU4OWYwZTI5OTY4ODVkM2MyNTBlMjJhMzMzNDY3NiIsImlhdCI6MTc2NjY3NDY2MiwibmJmIjoxNzY2Njc0NjYyLCJleHAiOjE4OTMzNjk2MDAsInN1YiI6IjE0NDY4NTAwIiwiZ3JhbnRfdHlwZSI6IiIsImFjY291bnRfaWQiOjM1NDc3MTE5LCJiYXNlX2RvbWFpbiI6ImtvbW1vLmNvbSIsInZlcnNpb24iOjIsInNjb3BlcyI6WyJjcm0iLCJmaWxlcyIsImZpbGVzX2RlbGV0ZSIsIm5vdGlmaWNhdGlvbnMiLCJwdXNoX25vdGlmaWNhdGlvbnMiXSwiaGFzaF91dWlkIjoiNmZlNzhmYjYtNDQ4ZC00MGNiLWE2ZDYtNzg5Y2MyYTIyNzBkIiwidXNlcl9mbGFncyI6MCwiYXBpX2RvbWFpbiI6ImFwaS1nLmtvbW1vLmNvbSJ9.dUcX6NUXRZOK0MhywNloo_5o6dFfjTLSDj9LinVNS2c66NHlsEmWRg2h8kP0NB75VeiYVP624kQMMMztR4trcjQuvvNElBWEw5kCAlIUMD4IZDmyduSvZ6JGYnC24ukVnH6voMW7uN3wyGgBrI3EOsrExAZwP10sQZBQNDqeUBb8ZWFXMaBiFeWEpoZpcaG_DMNgD3BEYLeYSsjSwUsOOyaLpKroJktrYnFjIgdH4qhoImpXmM0rL3z_VXpz85X-7sVp7vLwsyZH48yrReDIL1RQu__sIQVPr40_k8XbQ2vLQLYSFjT52Hajh98IdGpb7DgQUEKVF0z_hByDXo5PJQ'; + + $serviceUrl = 'https://extranetwork.kommo.com/api/v4/' . $method; + + $result = $this->restClient->post($serviceUrl, + [ + 'headers' => $headers, + 'body' => json_encode($payloadJson) + ] + ); + + + $getResponseBody = $result->getBody()->getContents(); + $getResponse = json_decode($getResponseBody, 1); + + $response = [ + 'status' => true, + 'data' => $getResponse, + //'message' => $getResponse['message'] + ]; + + } catch (RequestException $e) { + $getResponse = json_decode($e->getResponse()->getBody()->getContents(), 1); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $getResponse['message']; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function kommoCreateLead($params) + { + + $response = ['status' => false, 'message' => '', 'data' => null]; + try { + + $kommoCompanyParam = [ + [ + "name" => $params['property'], + "custom_fields_values" => [ + [ + "field_code" => "PHONE", + "values" => [ + [ + "value" => $params['phone_number'] + ] + ] + ], + [ + "field_code" => "EMAIL", + "values" => [ + [ + "value" => $params['email'] + ] + ] + ] + ] + ] + ]; + + $kommoCompany = $this->kommoApiPost('companies', $kommoCompanyParam); + $kommoCompany = $kommoCompany['status'] == 'success' && !empty($kommoCompany['status']) ? $kommoCompany['data'] : null; + $kommoCompanyId = reset($kommoCompany['_embedded']['companies'])['id']; + + $kommoContactParam = [ + [ + "name" => $params['name_surname'], + "custom_fields_values" => [ + [ + "field_code" => "PHONE", + "values" => [ + [ + "value" => $params['phone_number'] + ] + ] + ], + [ + "field_code" => "EMAIL", + "values" => [ + [ + "value" => $params['email'] + ] + ] + ] + ] + ] + ]; + + $kommoContact = $this->kommoApiPost('contacts', $kommoContactParam); + $kommoContact = $kommoContact['status'] == 'success' && !empty($kommoContact['status']) ? $kommoContact['data'] : null; + $kommoContactId = reset($kommoContact['_embedded']['contacts'])['id']; + + $kommoLeadParam = [ + [ + "name" => $params['property'], + "pipeline_id" => 12343583, + "status_id" => 95388747, + "_embedded" => [ + "companies" => [ + [ + "id" => $kommoCompanyId + ] + ], + "contacts" => [ + [ + "id" => $kommoContactId + ] + ] + ], + "custom_fields_values" => [ + [ + "field_id" => 2522776, + "values" => [ + [ + "value" => "Web Form" + ] + ] + ] + ] + ] + ]; + + $kommoLead = $this->kommoApiPost('leads', $kommoLeadParam); + $kommoLead = $kommoLead['status'] == 'success' && !empty($kommoLead['status']) ? $kommoLead['data'] : null; + + + $response = [ + 'status' => true, + 'data' => $kommoLead + ]; + + + } catch (RequestException $e) { + $getResponse = json_decode($e->getResponse()->getBody()->getContents(), 1); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $getResponse['message']; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + +} diff --git a/app/Core/Service/PropertySummaryService.php b/app/Core/Service/PropertySummaryService.php new file mode 100644 index 0000000..5decad7 --- /dev/null +++ b/app/Core/Service/PropertySummaryService.php @@ -0,0 +1,255 @@ +propertyRepository = $propertyRepository; + $this->bookingRepository = $bookingRepository; + $this->propertySummaryRepository = $propertySummaryRepository; + + $commissionChannelCategoryMapping = [ + 2 => 'commission_offline', + 3 => 'commission', + 4 => 'commission_channel', + ]; + + $this->commissionChannelCategoryMapping = $commissionChannelCategoryMapping; + + $this->exchangeCurrencyCode = 'EUR'; + } + + public function calculatePeriodCommission($propertyId, $type, $periodStartDate, $periodFinishDate) + { + + $propertyDetailCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $propertyId], + //['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyBookingEngines.channel'], + 'firstRow' => true + ]; + + $propertyDetail = $this->propertyRepository->findByCriteria($propertyDetailCriteria); + + $propertyCreateTime = Carbon::parse($propertyDetail['invoice_start_date'])->toDateString(); + + if ($propertyCreateTime > $periodFinishDate) { + throw new ApiErrorException('Property not ready for transaction: ' . $propertyCreateTime . ' - ' . $propertyDetail['name']); + } + + $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' => 'status', 'condition' => '=', 'value' => 1], + ], + 'whereIn' => + [ + ['field' => 'status', 'value' => [1, 3]] + ] + ]; + + if ($type == 1) { + $bookingListCriteria['criteria'][] = ['field' => 'checkout_date', 'condition' => '>=', 'value' => $periodStartDate]; + $bookingListCriteria['criteria'][] = ['field' => 'checkout_date', 'condition' => '<', 'value' => $periodFinishDate]; + } + + if ($type == 2) { + $bookingListCriteria['criteria'][] = ['field' => 'reservation_time', 'condition' => '>=', 'value' => Carbon::parse($periodStartDate, 'UTC')->timestamp]; + $bookingListCriteria['criteria'][] = ['field' => 'reservation_time', 'condition' => '<', 'value' => Carbon::parse($periodFinishDate, 'UTC')->timestamp]; + } + + $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] = [ + 'count' => count($bookingList), + '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, + ] + ]; + + } + + + } + + + $channelBaseCount = 0; + $channelBaseTotal = 0; + $channelBaseTotalCommission = 0; + foreach ($channelBaseCommission as $channelBase) { + $channelBaseValue = reset($channelBase); + $channelBaseCount += $channelBaseValue['count']; + $channelBaseTotal += $channelBaseValue['exchangeData']['total']; + $channelBaseTotalCommission += $channelBaseValue['exchangeData']['commission']; + } + + $commission = [ + 'channelBaseCount' => $channelBaseCount, + 'channelBaseTotal' => $channelBaseTotal, + 'channelBaseTotalCommission' => $channelBaseTotalCommission, + ]; + + return $commission; + } + + + public function summaryWithPeriod($propertyId, $period, $type) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + $periodStartDate = Carbon::parse($period . '-01')->toDateString(); + $periodFinishDate = 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'); + } + + + $conditions = [ + 'type' => $type, + 'commission' => $propertyDetail['commission'], + 'commission_payment' => $propertyDetail['commission_payment'], + 'commission_offline' => $propertyDetail['commission_offline'], + 'commission_channel' => $propertyDetail['commission_channel'], + ]; + + $periodCommission = $this->calculatePeriodCommission($propertyId, $type, $periodStartDate, $periodFinishDate); + + $propertySummaryCreateParam = [ + 'property_id' => $propertyId, + 'period' => $period, + 'count' => $periodCommission['channelBaseCount'], + 'commission' => (float)number_format($periodCommission['channelBaseTotalCommission'], '2', '.', ''), + 'total' => (float)number_format($periodCommission['channelBaseTotal'], '2', '.', ''), + 'currency' => $this->exchangeCurrencyCode, + 'type' => $type, + 'status' => 1, + ]; + + + $propertySummaryCheckCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'period', 'condition' => '=', 'value' => $period], + ['field' => 'type', 'condition' => '=', 'value' => $type], + ], + 'firstRow' => true + ]; + + $propertySummaryCheck = $this->propertySummaryRepository->findByCriteria($propertySummaryCheckCriteria); + + if ($propertySummaryCheck) { + $propertySummaryCreate = $this->propertySummaryRepository->update($propertySummaryCheck['id'], $propertySummaryCreateParam); + } else { + $propertySummaryCreate = $this->propertySummaryRepository->create($propertySummaryCreateParam); + } + + if ($propertySummaryCreate['status'] != 'success') { + throw new ApiErrorException($propertySummaryCreate['message']); + } + + + $response = [ + 'status' => true, + 'data' => $propertySummaryCreate['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); + } + +} diff --git a/app/Core/Service/PropertyTypeService.php b/app/Core/Service/PropertyTypeService.php new file mode 100644 index 0000000..f99862e --- /dev/null +++ b/app/Core/Service/PropertyTypeService.php @@ -0,0 +1,95 @@ +propertyTypeRepository = $propertyTypeRepository; + + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyTypeRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyTypes($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'orderBy' => [ + ["field" => "name", "value" => "ASC"] + ], + ]; + + $searchResult = $this->select($searchCriteria, ['id', 'name', 'language_key']); + if($searchResult['status'] != 'success'){ + throw new Exception($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_type' =>$return ] ]; + } catch + (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + + + +} diff --git a/app/Core/Service/PropertyWebAboutUsService.php b/app/Core/Service/PropertyWebAboutUsService.php new file mode 100644 index 0000000..3fc8326 --- /dev/null +++ b/app/Core/Service/PropertyWebAboutUsService.php @@ -0,0 +1,207 @@ +propertyWebAboutUsRepository = $propertyWebAboutUsRepository; + $this->languageService = $languageService; + + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebAboutUsRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $propertyCreateResult = $this->propertyWebAboutUsRepository->insert($params); + + if ($propertyCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $propertyCreateResult["data"] + ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function getPropertyWebAboutUs($param = [], $column = ['*']){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + + $getApplicationLanguages = $this->languageService->getApplicationLanguages(); + if($getApplicationLanguages['status'] != 'success'){ + throw new ApiErrorException($getApplicationLanguages['message']); + } + $applicationLanguages = $getApplicationLanguages['data'] ; + + $requestPropertyWebAboutUs = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_web_id')], + ['field' => 'status', 'condition' => '=', 'value' => fillOnUndefined($param, 'mode')] + ] + ]; + + $propertyWebAboutUs = $this->select($requestPropertyWebAboutUs, $column); + + if($propertyWebAboutUs['status'] != 'success'){ + throw new ApiErrorException($propertyWebAboutUs['message']); + } + + $propertyWebAboutUs = !empty($propertyWebAboutUs['data']) ? collect($propertyWebAboutUs['data'])->keyBy('language_code')->toArray() : []; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code'] ; + $responseAboutUs[] = [ + 'language_code' => $langKey, + 'value' => isset($propertyWebAboutUs[$langKey]) ? $propertyWebAboutUs[$langKey]['value'] : null + ]; + } + + $return = $responseAboutUs; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => $return ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function updatePropertyWebAboutUs($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $return = []; + + + $getDeletePropertyWebAboutRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')] + ] + ]; + + $status = null; + if($params['action'] === "publish"){ + $status = 1; + }else if($params['action'] === "preview"){ + $getDeletePropertyWebAboutRequest['criteria'][] = ['field' => 'status', 'condition' => '=', 'value' => 2]; + $status = 2; + } + + $deleteWebAboutUsId = $this->propertyWebAboutUsRepository->findByCriteria($getDeletePropertyWebAboutRequest, ['id']) ; + $deleteWebAboutUsId = array_column($deleteWebAboutUsId, 'id') ; + + if (!empty($deleteWebAboutUsId)) { + $destroyStatus = $this->propertyWebAboutUsRepository->destroy($deleteWebAboutUsId); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $createPropertyWebAboutUsData = []; + $aboutUsDatas = isset($params['about_us']) && !empty($params['about_us']) ? $params['about_us'] : []; + foreach ($aboutUsDatas as $key => $aboutUsData){ + if($aboutUsData['value']){ + $createPropertyWebAboutUsData[$key]['property_id'] = fillOnUndefined($params,'property_id'); + $createPropertyWebAboutUsData[$key]['property_web_id'] = fillOnUndefined($params,'property_web_id'); + $createPropertyWebAboutUsData[$key]['language_code'] = $aboutUsData['language_code']; + $createPropertyWebAboutUsData[$key]['value'] = $aboutUsData['value']; + $createPropertyWebAboutUsData[$key]['status'] = $status; + $createPropertyWebAboutUsData[$key]['created_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebAboutUsData[$key]['updated_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebAboutUsData[$key]['created_at'] = time(); + $createPropertyWebAboutUsData[$key]['updated_at'] = time(); + } + } + + $propertyWebAboutUsResult = $this->create($createPropertyWebAboutUsData); + if ($propertyWebAboutUsResult['status'] != 'success'){ + throw new ApiErrorException($propertyWebAboutUsResult['message']); + } + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_about_us' => $return ] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + + +} diff --git a/app/Core/Service/PropertyWebColorMappingService.php b/app/Core/Service/PropertyWebColorMappingService.php new file mode 100644 index 0000000..905d531 --- /dev/null +++ b/app/Core/Service/PropertyWebColorMappingService.php @@ -0,0 +1,217 @@ +propertyWebColorMappingRepository = $propertyWebColorMappingRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebColorMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function createPropertyWebColorMapping($param = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + // TODO validation yazılmalı! + + $propertyWebMenuMappingResult = $this->propertyWebColorMappingRepository->insert($param); + + if ($propertyWebMenuMappingResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => NULL + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyWebColorMapping($params, $select = ['*']){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_web_id', 'condition' => '=', 'value' => $params['property_web_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $searchResult = $this->select($searchCriteria, $select); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_color_mapping' => $return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + + public function updatePropertyWebColorMapping($params = []){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + + $getDeletePropertyWebColorRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')] + ] + ]; + + $status = null; + if($params['action'] === "publish"){ + $status = 1; + }else if($params['action'] === "preview"){ + + $getDeletePropertyWebColorRequest['criteria'][] = ['field' => 'status', 'condition' => '=', 'value' => 2]; + $status = 2; + } + + $deleteWebColorId = $this->propertyWebColorMappingRepository->findByCriteria($getDeletePropertyWebColorRequest, ['id']) ; + $deleteWebColorId = array_column($deleteWebColorId, 'id') ; + + if (!empty($deleteWebColorId)) { + $destroyStatus = $this->propertyWebColorMappingRepository->destroy($deleteWebColorId); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $myWebColors = !empty($params['colors']) ? $params['colors'] : []; + + $createPropertyWebColorMappingData = []; + foreach ($myWebColors as $key => $myWebColor){ + $createPropertyWebColorMappingData[$key]['property_id'] = fillOnUndefined($params,'property_id'); + $createPropertyWebColorMappingData[$key]['property_web_id'] = fillOnUndefined($params,'property_web_id'); + $createPropertyWebColorMappingData[$key]['color_code'] = $myWebColor['color_code']; + $createPropertyWebColorMappingData[$key]['order_number'] = $myWebColor['color_number']; + $createPropertyWebColorMappingData[$key]['status'] = $status; + $createPropertyWebColorMappingData[$key]['created_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebColorMappingData[$key]['updated_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebColorMappingData[$key]['created_at'] = time(); + $createPropertyWebColorMappingData[$key]['updated_at'] = time(); + } + + $propertyWebColorMappingResult = $this->createPropertyWebColorMapping($createPropertyWebColorMappingData); + if ($propertyWebColorMappingResult['status'] != 'success'){ + throw new ApiErrorException($propertyWebColorMappingResult['message']); + } + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_menu_mapping' => $return ] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function getPropertyWebColorMappingWithStatus($params, $select = ['*']){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_web_id', 'condition' => '=', 'value' => $params['property_web_id']], + ['field' => 'status', 'condition' => '=', 'value' => $params['status']] + ] + ]; + + $searchResult = $this->select($searchCriteria, $select); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_color_mapping' => $return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + +} diff --git a/app/Core/Service/PropertyWebContentService.php b/app/Core/Service/PropertyWebContentService.php new file mode 100644 index 0000000..4dd59ff --- /dev/null +++ b/app/Core/Service/PropertyWebContentService.php @@ -0,0 +1,340 @@ +languageService = $languageService; + $this->propertyWebContentRepository = $propertyWebContentRepository; + $this->propertyWebContentCategoryRepository = $propertyWebContentCategoryRepository; + $this->propertyPhotoValidator = $propertyPhotoValidator; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = [ + "property_id" => fillOnUndefined($param, "property_id"), + "content_category_id" => fillOnUndefined($param, "content_category_id"), + "title" => fillOnUndefined($param, "title"), + "language_code" => fillOnUndefined($param, "language_code"), + "image" => fillOnUndefined($param, "image"), + "content" => fillOnUndefined($param, "content"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $insertData['slug'] = str_slug($insertData['title']); + + //TODO: be activate validator + /*$validationResult = $this->propertyContentCreateValidator->validate($insertData); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $userCreateResult = $this->propertyWebContentRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response['status'] = 1; + $response['data'] = $userData; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebContentRepository->findByCriteria($param, $column); + + if (!$data) { + throw new ApiErrorException(lang('Data is not found.')); + } + + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectWebContentCategory($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebContentCategoryRepository->findByCriteria($param, $column); + + if (!$data) { + throw new ApiErrorException(lang('Data is not found.')); + } + + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $param['slug'] = str_slug($param['title']); + + + $chekDataParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $id], + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ], + 'firstRow' => true + ]; + $chekData = $this->propertyWebContentRepository->findByCriteria($chekDataParam); + if (empty($chekData)) { + throw new Exception('You are not authorized to perform this action.'); + } + + $updateResult = $this->propertyWebContentRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function delete($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $chekDataParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $id], + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ], + 'firstRow' => true + ]; + $chekData = $this->propertyWebContentRepository->findByCriteria($chekDataParam); + if (empty($chekData)) { + throw new Exception('You are not authorized to perform this action.'); + } + + $deleteResult = $this->propertyWebContentRepository->destroy([$id]); + if ($deleteResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function uploadPhoto($params = []) + { + + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + try { + $validateParams = [ + 'photo' => $params['photo'] + ]; + + $uploadedFile = $params['photo']; + $validationResult = $this->propertyPhotoValidator->validate($validateParams); + if ($validationResult->errors()->first()) { + throw new ApiErrorException($validationResult->errors()->all()); + } + + $uploadedOriginalImage = Image::make($uploadedFile); + $imageWidth = $uploadedOriginalImage->width(); + $imageHeight = $uploadedOriginalImage->height(); + + $fileExt = $uploadedFile->getClientOriginalExtension(); + $fileSize = (int)ceil(Image::make($uploadedFile)->filesize() / 1024); + + $imageExtension = 'jpg'; + $fileExt = $fileExt != $imageExtension ? $imageExtension : $fileExt; + $quality = 80; + + $urlPath = '/property-web-content/' . $params['property_id'] . "/"; + $filePath = Config::get('app.fileSystemDriver') . $urlPath; + $fileName = md5(Carbon::now()->format('dmyHi') . '-' . md5($uploadedFile->getClientOriginalName())); + + if (!File::exists($filePath)) { + File::makeDirectory($filePath, 0777, true); + } + + $originalImageMustResize = ($imageHeight > 2000 || $imageWidth > 2000); + + if ($imageHeight < 768) { + throw new ApiErrorException('image size error: minimum height must be 768px'); + } + + if ($imageHeight > $imageWidth) { + + $ratio = $imageHeight / 768; + //$ratio200 = 768/200; + $resizeWidth = intval($imageWidth / $ratio); + //$resizeThumbWidth = intval($resizeWidth/$ratio200); + + if ($originalImageMustResize) { + $imageHRatio = $imageHeight / 2000; + $imageResizedOriginalWidth = intval($imageWidth / $imageHRatio); + $image = $uploadedOriginalImage->fit($imageResizedOriginalWidth, 2000)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality); + } else { + $image = $uploadedOriginalImage->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality); + } + $image->fit($resizeWidth, 768)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_medium' . '.' . $fileExt); + $image->fit(300, 300)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_thumbnail' . '.' . $fileExt); + } else { + + $ratio = $imageHeight / 768; + $resizeWidth = intval($imageWidth / $ratio); + + $ratioThumbnail = 768 / 300; + $resizeThumbWidth = intval($resizeWidth / $ratioThumbnail); + + if ($originalImageMustResize) { + $imageWRatio = $imageWidth / 2000; + $imageResizedOriginalHeight = intval($imageHeight / $imageWRatio); + $image = $uploadedOriginalImage->fit(2000, $imageResizedOriginalHeight)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality); + } else { + $image = $uploadedOriginalImage->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality); + } + + if ($resizeWidth > 1599) { + $image->fit(1600, 768)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_medium' . '.' . $fileExt); + $image->fit(300, 300)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_thumbnail' . '.' . $fileExt); + } else { + $image->fit($resizeWidth, 768)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_medium' . '.' . $fileExt); + $image->fit(300)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_thumbnail' . '.' . $fileExt); + } + + } + + $response = [ + 'status' => true, + 'data' => [ + //'photo_path' => $urlPath, + //'photo_name' => $fileName, + 'image' => $fileName . '.' . $fileExt, + //'file_size' => $fileSize, + //'file_ext' => $fileExt, + 'image_resolution' => $imageWidth . 'x' . $imageHeight, + 'imageUrl' => [ + 'default' => Config::get('app.imageUrl') . '/property-web-content/' . $params['property_id'] . '/' . $fileName . '.' . $fileExt, + 'thumb' => Config::get('app.imageUrl') . '/property-web-content/' . $params['property_id'] . '/' . $fileName . '_thumbnail.' . $fileExt, + 'fixed' => Config::get('app.imageUrl') . '/property-web-content/' . $params['property_id'] . '/' . $fileName . '_medium.' . $fileExt, + ] + + ] + ]; + + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['status'] = false; + } + + return output($response); + } +} diff --git a/app/Core/Service/PropertyWebLanguageMappingService.php b/app/Core/Service/PropertyWebLanguageMappingService.php new file mode 100644 index 0000000..cced69a --- /dev/null +++ b/app/Core/Service/PropertyWebLanguageMappingService.php @@ -0,0 +1,186 @@ +propertyWebLanguageMappingRepository = $propertyWebLanguageMappingRepository; + $this->languageService = $languageService; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebLanguageMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function createPropertyWebLanguageMapping($param = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + // TODO validation yazılmalı! + + $propertyWebMenuMappingResult = $this->propertyWebLanguageMappingRepository->insert($param); + + if ($propertyWebMenuMappingResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => NULL + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyWebLanguageMapping($params, $select = ['*']){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_web_id', 'condition' => '=', 'value' => $params['property_web_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $searchResult = $this->select($searchCriteria, $select); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_language_mapping' => $return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + + public function updatePropertyWebLanguageMapping($params = []){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + + $getDeletePropertyWebLanguageRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')] + ] + ]; + + $status = null; + if($params['action'] === "publish"){ + $status = 1; + }else if($params['action'] === "preview"){ + + $getDeletePropertyWebLanguageRequest['criteria'][] = ['field' => 'status', 'condition' => '=', 'value' => 2]; + $status = 2; + } + + $deleteWebLanguageId = $this->propertyWebLanguageMappingRepository->findByCriteria($getDeletePropertyWebLanguageRequest, ['id']) ; + $deleteWebLanguageId = array_column($deleteWebLanguageId, 'id') ; + + if (!empty($deleteWebLanguageId)) { + $destroyStatus = $this->propertyWebLanguageMappingRepository->destroy($deleteWebLanguageId); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + $myWebLanguages = !empty($params['languages']) ? $params['languages'] : []; + + $createPropertyWebLanguageMappingData = []; + foreach ($myWebLanguages as $key => $myWebLanguage){ + $createPropertyWebLanguageMappingData[$key]['property_id'] = fillOnUndefined($params,'property_id'); + $createPropertyWebLanguageMappingData[$key]['property_web_id'] = fillOnUndefined($params,'property_web_id'); + $createPropertyWebLanguageMappingData[$key]['language_code'] = $myWebLanguage; + $createPropertyWebLanguageMappingData[$key]['status'] = $status; + $createPropertyWebLanguageMappingData[$key]['created_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebLanguageMappingData[$key]['updated_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebLanguageMappingData[$key]['created_at'] = time(); + $createPropertyWebLanguageMappingData[$key]['updated_at'] = time(); + } + + + $propertyWebLanguageMappingResult = $this->createPropertyWebLanguageMapping($createPropertyWebLanguageMappingData); + if ($propertyWebLanguageMappingResult['status'] != 'success'){ + throw new ApiErrorException($propertyWebLanguageMappingResult['message']); + } + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_language_mapping' => $return ] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyWebLogService.php b/app/Core/Service/PropertyWebLogService.php new file mode 100644 index 0000000..80b3109 --- /dev/null +++ b/app/Core/Service/PropertyWebLogService.php @@ -0,0 +1,209 @@ +propertyWebLogRepository = $propertyWebLogRepository; + + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebLogRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $propertyCreateResult = $this->propertyWebLogRepository->create($params); + + if ($propertyCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $propertyCreateResult["data"] + ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $updateResult = $this->propertyWebLogRepository->update($id, $param); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function createPropertyWebLog($params){ + + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + + try + { + $searchCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'ip', 'condition' => '=', 'value' => $params['ip']], + ['field' => 'web_id', 'condition' => '=', 'value' => $params['web_id']] + ], + 'firstRow' => true + ]; + + $searchResult = $this->select($searchCriteria, ['id', 'web_id']); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + + // Create + if(empty($searchResult['data'])){ + + $createData = [ + 'web_id' => fillOnUndefined($params, "web_id"), + 'ip' => fillOnUndefined($params, "ip"), + 'country_code' => fillOnUndefined($params, "country_code"), + 'device_type' => $params['isDesktop'] ? 'web' : 'mobile', + 'created_at' => time(), + 'updated_at' => time() + ]; + $createStatus = $this->create($createData) ; + + if($createStatus['status'] != 'success'){ + throw new Exception($createStatus['message']); + } + + }else{ + // Update + $updateData = [ + 'web_id' => fillOnUndefined($params, "web_id"), + 'ip' => fillOnUndefined($params, "ip"), + 'country_code' => fillOnUndefined($params, "country_code"), + 'device_type' => $params['isDesktop'] ? 'web' : 'mobile', + 'created_at' => time(), + 'updated_at' => time() + ]; + $updateStatus = $this->update($searchResult['data']['id'], $updateData) ; + + if($updateStatus['status'] != 'success'){ + throw new Exception($updateStatus['message']); + } + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $searchResult ]; + + }catch (ApiErrorException $e) + { + $response = ['status' => 0, 'statusCode' => 500 ,'message' => $e->getMessage(), 'data' => null]; + } + catch (Exception $e){ + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + + return output($response); + } + + public function getPropertyWebLog($param = [], $column = ['*']){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + + + $searchResult = $this->select($param, $column); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_logs' => $return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + + + +} diff --git a/app/Core/Service/PropertyWebMenuMappingService.php b/app/Core/Service/PropertyWebMenuMappingService.php new file mode 100644 index 0000000..6b0d805 --- /dev/null +++ b/app/Core/Service/PropertyWebMenuMappingService.php @@ -0,0 +1,221 @@ +propertyWebMenuMappingRepository = $propertyWebMenuMappingRepository; + $this->propertyWebMenuService = $propertyWebMenuService; + $this->propertyPlaceService = $propertyPlaceService ; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebMenuMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function createPropertyWebMenuMapping($param = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + // TODO validation yazılmalı! + + $propertyWebMenuMappingResult = $this->propertyWebMenuMappingRepository->insert($param); + + if ($propertyWebMenuMappingResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => NULL + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyWebMenuMapping($params, $select = ['*']){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_web_id', 'condition' => '=', 'value' => $params['property_web_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1] + ] + ]; + + $searchResult = $this->select($searchCriteria, $select); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_menu_mapping' => $return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + + public function updatePropertyWebMenuMapping($params = []){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + + $getDeletePropertyWebMenuRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')] + ] + ]; + + $status = null; + if($params['action'] === "publish"){ + $status = 1; + }else if($params['action'] === "preview"){ + + $getDeletePropertyWebMenuRequest['criteria'][] = ['field' => 'status', 'condition' => '=', 'value' => 2]; + $status = 2; + } + + + $deleteWebMenuId = $this->propertyWebMenuMappingRepository->findByCriteria($getDeletePropertyWebMenuRequest, ['id']) ; + $deleteWebMenuId = array_column($deleteWebMenuId, 'id') ; + + + if (!empty($deleteWebMenuId)) { + $destroyStatus = $this->propertyWebMenuMappingRepository->destroy($deleteWebMenuId); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $webMenus = $this->propertyWebMenuService->getPropertyWebMenu(); + if ($webMenus['status'] != 'success') { + throw new ApiErrorException($webMenus['message']); + } + + $placeRequest = [ + 'property_id' => $params['property_id'] + ] ; + $myPlaces = $this->propertyPlaceService->webMenuPlaceCategoriesAndPlaces($placeRequest); + if ($myPlaces['status'] != 'success') { + throw new ApiErrorException($myPlaces['message']); + } + $myPlaces = $myPlaces['data'] ; + $myWebMenus = $webMenus['data']['property_web_menus'] ; + $myWebMenus = array_merge($myWebMenus, $myPlaces) ; + $webMenus = collect($myWebMenus); + $menus = collect($params['menus']); + + + $createPropertyWebMenuMappingData = []; + foreach ($menus as $menu){ + + $checkMenu = $webMenus->where('menu_type', '=', $menu['menu_type'])->where('id', '=', $menu['id'])->first(); + if (!$checkMenu) { + throw new ApiErrorException('api-unknown-error'); + } + + $createPropertyWebMenuMappingData[] = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'property_web_menu_id' => $menu['id'], + 'order_number' => $menu['order_number'], + 'type' => $menu['menu_type'], + 'menu_code' => fillOnUndefined($menu, 'menu_code', null), + 'status' => $status, + 'created_by' => fillOnUndefined($params, 'user_id'), + 'updated_by' => fillOnUndefined($params, 'user_id'), + 'created_at' => time(), + 'updated_at' => time(), + ] ; + + + } + + $propertyWebMenuMappingResult = $this->createPropertyWebMenuMapping($createPropertyWebMenuMappingData); + if ($propertyWebMenuMappingResult['status'] != 'success'){ + throw new ApiErrorException($propertyWebMenuMappingResult['message']); + } + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_menu_mapping' => $return ] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyWebMenuService.php b/app/Core/Service/PropertyWebMenuService.php new file mode 100644 index 0000000..4a2bea8 --- /dev/null +++ b/app/Core/Service/PropertyWebMenuService.php @@ -0,0 +1,91 @@ +propertyWebMenuRepository = $propertyWebMenuRepository; + + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebMenuRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyWebMenu(){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]], + 'orderBy' => [["field" => "order_number", "value" => "ASC"]], + ]; + + $searchResult = $this->select($searchCriteria, ['id', 'name', 'language_key', 'route', 'type', 'order_number', 'icon', 'alias']); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $webMenus = collect($searchResult['data'])->map(function ($value){ + $return = $value ; + $return['menu_type'] = 'MAIN' ; + return $return ; + })->values()->all() ; + $return = $webMenus; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_menus' => $return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + + + +} diff --git a/app/Core/Service/PropertyWebMetaService.php b/app/Core/Service/PropertyWebMetaService.php new file mode 100644 index 0000000..bcd84d6 --- /dev/null +++ b/app/Core/Service/PropertyWebMetaService.php @@ -0,0 +1,222 @@ +propertyWebMetaRepository = $propertyWebMetaRepository; + $this->propertyWebMetaTagRepository = $propertyWebMetaTagRepository; + $this->propertyWebMetaMappingRepository = $propertyWebMetaMappingRepository; + } + + public function select($params = [], $column = ['*']) + { + $response = ['status' => true, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebMetaRepository->findByCriteria($params, $column); + $response = [ + 'status' => 1, + 'data' => $data, + ]; + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['status'] = -1; + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectTag($param = [], $column = ['*']) + { + $response = ['status' => true, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebMetaTagRepository->findByCriteria($param, $column); + $response = [ + 'status' => 1, + 'data' => $data, + ]; + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['status'] = -1; + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectMapping($params = [], $column = ['*']) + { + $response = ['status' => true, 'message' => '', 'data' => null]; + + try { + $propertyId = $params['property_id'] ?? null; + if ($propertyId) { + $params['criteria'][] = ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId]; + } + + $filter = $params['filter'] ?? null; + if ($filter) { + if (isset($filter['property_web_meta_id'])) { + $params['criteria'][] = ['field' => 'property_web_meta_id', 'condition' => '=', 'value' => $filter['property_web_meta_id']]; + } + if (isset($filter['property_web_meta_tag_id'])) { + $params['criteria'][] = ['field' => 'property_web_meta_tag_id', 'condition' => '=', 'value' => $filter['property_web_meta_tag_id']]; + } + if (isset($filter['code'])) { + $params['criteria'][] = ['field' => 'code', 'condition' => '=', 'value' => $filter['code']]; + } + } + + $params['with'] = ['propertyWebMeta:id,name,language_key,order_number', 'propertyWebMetaTag:id,name,language_key,order_number']; + $data = $this->propertyWebMetaMappingRepository->findByCriteria($params, $column); + $response = [ + 'status' => 1, + 'data' => $data, + ]; + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['status'] = -1; + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function syncTag($params = []) + { + $response = ['status' => true, 'message' => '', 'data' => null]; + + try { + $propertyId = $params['property_id'] ?? null; + $propertyWebId = $params['property_web_id'] ?? null; + $propertyWebMetaTagId = $params['property_web_meta_tag_id'] ?? null; + $propertyWebMetaId = $params['property_web_meta_id'] ?? null; + $code = $params['code'] ?? null; + $text = $params['text'] ?? null; + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'property_web_id', 'condition' => '=', 'value' => $propertyWebId], + ['field' => 'property_web_meta_tag_id', 'condition' => '=', 'value' => $propertyWebMetaTagId], + ['field' => 'property_web_meta_id', 'condition' => '=', 'value' => $propertyWebMetaId], + ['field' => 'code', 'condition' => '=', 'value' => $code], + ], + 'firstRow' => 1 + ]; + + // Önce kayıt var mı kontrol ediyoruz + $existing = $this->propertyWebMetaMappingRepository->findByCriteria($criteria); + + if (!empty($existing)) { + // Kayıt var + if (is_null($text)) { + // Text boş ise silme + $id = $existing['id']; + $deleteResult = $this->propertyWebMetaMappingRepository->destroy([$id]); + if ($deleteResult['status'] != 'success') { + throw new Exception($deleteResult['message']); + } + $response = [ + 'status' => 1, + 'message' => 'Record deleted successfully.', + 'data' => null, + ]; + } else { + // Text dolu ise güncelleme + $id = $existing['id']; + $data = [ + 'code' => $code, + 'text' => is_array($text) ? json_encode($text) : $text, + 'status' => true, + 'updated_at' => time() + ]; + $updateResult = $this->propertyWebMetaMappingRepository->update($id, $data); + if ($updateResult['status'] != 'success') { + throw new Exception($updateResult['message']); + } + $response = [ + 'status' => 1, + 'message' => 'Record updated successfully.', + 'data' => $data, + ]; + } + } else { + // Kayıt yok + if (!is_null($text)) { + // Text dolu ise oluşturma + $data = [ + 'property_id' => $propertyId, + 'property_web_id' => $propertyWebId, + 'property_web_meta_tag_id' => $propertyWebMetaTagId, + 'property_web_meta_id' => $propertyWebMetaId, + 'code' => $code, + 'text' => is_array($text) ? json_encode($text) : $text, + 'status' => true, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id") + ]; + + $createResult = $this->propertyWebMetaMappingRepository->create($data); + if ($createResult['status'] != 'success') { + throw new Exception($createResult['message']); + } + + $response = [ + 'status' => 1, + 'message' => 'Record created successfully.', + 'data' => $data, + ]; + } else { + // Kayıt yok ve text de null ise işlem yapmaya gerek yok (veya başarılı dönülebilir) + $response = [ + 'status' => 1, + 'message' => 'Record not found and text is null, no action taken.', + 'data' => null, + ]; + } + } + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['status'] = -1; + $response['message'] = $e->getMessage(); + } + + return output($response); + } +} diff --git a/app/Core/Service/PropertyWebPhotoMappingService.php b/app/Core/Service/PropertyWebPhotoMappingService.php new file mode 100644 index 0000000..957cdca --- /dev/null +++ b/app/Core/Service/PropertyWebPhotoMappingService.php @@ -0,0 +1,90 @@ +propertyWebPhotoMappingRepository = $propertyWebPhotoMappingRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebPhotoMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + + + + public function destroy($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $deleteCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_photo_id', 'condition' => '=', 'value' => $params['photo_id']], + ] + ]; + + $deleteData = $this->propertyWebPhotoMappingRepository->findByCriteria($deleteCriteria, ['id']); + $deleteData = $deleteData ? $deleteData : [] ; + if($deleteData){ + $deleteIds = array_column($deleteData, 'id') ; + $destroyResult = $this->propertyWebPhotoMappingRepository->destroy($deleteIds); + if ($destroyResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $response = [ + 'status' => true, + 'data' => null + ]; + + } 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); + + + } + +} diff --git a/app/Core/Service/PropertyWebPlaceMappingService.php b/app/Core/Service/PropertyWebPlaceMappingService.php new file mode 100644 index 0000000..0e6de8c --- /dev/null +++ b/app/Core/Service/PropertyWebPlaceMappingService.php @@ -0,0 +1,182 @@ +propertyWebPlaceMappingRepository = $propertyWebPlaceMappingRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebPlaceMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function createPropertyWebPlaceMapping($param = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + // TODO validation yazılmalı! + + $propertyWebPlaceMappingResult = $this->propertyWebPlaceMappingRepository->insert($param); + + if ($propertyWebPlaceMappingResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => NULL + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyWebRoomMapping($params, $select = ['*']){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_web_id', 'condition' => '=', 'value' => $params['property_web_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $searchResult = $this->select($searchCriteria, $select); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_room_mapping' => $return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + + public function updatePropertyWebPlaceMapping($params = []){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + + $getDeletePropertyWebPlaceRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')] + ] + ]; + + $status = null; + if($params['action'] === "publish"){ + $status = 1; + }else if($params['action'] === "preview"){ + + $getDeletePropertyWebPlaceRequest['criteria'][] = ['field' => 'status', 'condition' => '=', 'value' => 2]; + $status = 2; + } + + + $deleteWebPlaceId = $this->propertyWebPlaceMappingRepository->findByCriteria($getDeletePropertyWebPlaceRequest, ['id']) ; + $deleteWebPlaceId = array_column($deleteWebPlaceId, 'id') ; + + + if (!empty($deleteWebPlaceId)) { + $destroyStatus = $this->propertyWebPlaceMappingRepository->destroy($deleteWebPlaceId); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $myWebPlaces = !empty($params['places']) ? $params['places'] : []; + + $createPropertyWebPlaceMappingData = []; + foreach ($myWebPlaces as $key => $myWebPlace){ + $createPropertyWebPlaceMappingData[$key]['property_id'] = fillOnUndefined($params,'property_id'); + $createPropertyWebPlaceMappingData[$key]['property_web_id'] = fillOnUndefined($params,'property_web_id'); + $createPropertyWebPlaceMappingData[$key]['property_place_id'] = $myWebPlace; + $createPropertyWebPlaceMappingData[$key]['status'] = $status; + $createPropertyWebPlaceMappingData[$key]['created_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebPlaceMappingData[$key]['updated_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebPlaceMappingData[$key]['created_at'] = time(); + $createPropertyWebPlaceMappingData[$key]['updated_at'] = time(); + } + + $propertyWebPlaceMappingResult = $this->createPropertyWebPlaceMapping($createPropertyWebPlaceMappingData); + if ($propertyWebPlaceMappingResult['status'] != 'success'){ + throw new ApiErrorException($propertyWebPlaceMappingResult['message']); + } + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_place_mapping' => $return ] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyWebPopupService.php b/app/Core/Service/PropertyWebPopupService.php new file mode 100644 index 0000000..8b424b5 --- /dev/null +++ b/app/Core/Service/PropertyWebPopupService.php @@ -0,0 +1,399 @@ +languageService = $languageService; + $this->propertyWebPopupRepository = $propertyWebPopupRepository; + $this->propertyPhotoValidator = $propertyPhotoValidator; + $this->propertyWebRepository = $propertyWebRepository; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $codeParams = []; + $codeParams[] = fillOnUndefined($param, "property_id"); + $codeParams[] = fillOnUndefined($param, "title"); + $codeParams[] = fillOnUndefined($param, "language_code"); + $codeParams[] = fillOnUndefined($param, "start_date"); + $codeParams[] = fillOnUndefined($param, "end_date"); + $code = md5(implode('-', $codeParams)); + + $insertData = [ + "code" => $code, + "property_id" => fillOnUndefined($param, "property_id"), + "title" => fillOnUndefined($param, "title"), + "language_code" => fillOnUndefined($param, "language_code"), + "image" => fillOnUndefined($param, "image"), + "start_date" => fillOnUndefined($param, "start_date"), + "end_date" => fillOnUndefined($param, "end_date"), + "url" => fillOnUndefined($param, "url"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + //TODO: be activate validator + /*$validationResult = $this->propertyContentCreateValidator->validate($insertData); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $createResult = $this->propertyWebPopupRepository->create($insertData); + if ($createResult['status'] != 'success') { + throw new ApiErrorException('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $createResult["data"]; + + //Domain Cache Clean + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], 'firstRow' => true + ]; + $propertyWebData = $this->propertyWebRepository->findByCriteria($propertyRequest, ['id', 'domain']); + + if ($propertyWebData) { + $checkDomainStatusCacheKey = 'myweb:' . md5('checkDomainStatus-' . $propertyWebData['domain'] . '-' . Carbon::now()->toDateString()); + if (!empty(Redis::executeRaw(['KEYS', $checkDomainStatusCacheKey]))) { + Redis::executeRaw(['DEL', $checkDomainStatusCacheKey]); + } + } + //Domain Cache Clean + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebPopupRepository->findByCriteria($param, $column); + + if (!$data) { + throw new ApiErrorException(lang('Data is not found.')); + } + + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectWebContentCategory($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebPopupRepository->findByCriteria($param, $column); + + if (!$data) { + throw new ApiErrorException(lang('Data is not found.')); + } + + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $chekDataParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $id], + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ], + 'firstRow' => true + ]; + $chekData = $this->propertyWebPopupRepository->findByCriteria($chekDataParam); + if (empty($chekData)) { + throw new Exception('You are not authorized to perform this action.'); + } + + + $updateResult = $this->propertyWebPopupRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + //Domain Cache Clean + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], 'firstRow' => true + ]; + $propertyWebData = $this->propertyWebRepository->findByCriteria($propertyRequest, ['id', 'domain']); + + if ($propertyWebData) { + $checkDomainStatusCacheKey = 'myweb:' . md5('checkDomainStatus-' . $propertyWebData['domain'] . '-' . Carbon::now()->toDateString()); + if (!empty(Redis::executeRaw(['KEYS', $checkDomainStatusCacheKey]))) { + Redis::executeRaw(['DEL', $checkDomainStatusCacheKey]); + } + } + //Domain Cache Clean + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function delete($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $chekDataParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $id], + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ], + 'firstRow' => true + ]; + $chekData = $this->propertyWebPopupRepository->findByCriteria($chekDataParam); + if (empty($chekData)) { + throw new Exception('You are not authorized to perform this action.'); + } + + $deleteResult = $this->propertyWebPopupRepository->destroy([$id]); + if ($deleteResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $urlPath = '/property-web-popup/' . $param['property_id'] . "/"; + $filePath = Config::get('app.fileSystemDriver') . $urlPath; + + $fileExplode = explode('.', $chekData['image'], 2); + $fileName = $fileExplode[0]; + $fileExt = $fileExplode[1]; + + $fileCombinations = []; + $fileCombinations[] = $filePath . $fileName . '.' . $fileExt; + $fileCombinations[] = $filePath . $fileName . '_medium.' . $fileExt; + $fileCombinations[] = $filePath . $fileName . '_thumbnail.' . $fileExt; + + foreach ($fileCombinations as $fileCombination) { + if (file_exists($fileCombination)) { + File::delete($fileCombination); + } + } + + $response = [ + 'status' => true + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function uploadPhoto($params = []) + { + + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + try { + $validateParams = [ + 'photo' => $params['photo'] + ]; + + $uploadedFile = $params['photo']; + $validationResult = $this->propertyPhotoValidator->validate($validateParams); + if ($validationResult->errors()->first()) { + throw new ApiErrorException($validationResult->errors()->all()); + } + + $uploadedOriginalImage = Image::make($uploadedFile); + $imageWidth = $uploadedOriginalImage->width(); + $imageHeight = $uploadedOriginalImage->height(); + + $fileExt = $uploadedFile->getClientOriginalExtension(); + $fileSize = (int)ceil(Image::make($uploadedFile)->filesize() / 1024); + + $imageExtension = 'jpg'; + $fileExt = $fileExt != $imageExtension ? $imageExtension : $fileExt; + $quality = 80; + + $urlPath = '/property-web-popup/' . $params['property_id'] . "/"; + $filePath = Config::get('app.fileSystemDriver') . $urlPath; + $fileName = md5(Carbon::now()->format('dmyHi') . '-' . md5($uploadedFile->getClientOriginalName())); + + if (!File::exists($filePath)) { + File::makeDirectory($filePath, 0777, true); + } + + $originalImageMustResize = ($imageHeight > 2000 || $imageWidth > 2000); + + if ($imageHeight < 768) { + throw new ApiErrorException('image size error: minimum height must be 768px'); + } + + if ($imageHeight > $imageWidth) { + + $ratio = $imageHeight / 768; + //$ratio200 = 768/200; + $resizeWidth = intval($imageWidth / $ratio); + //$resizeThumbWidth = intval($resizeWidth/$ratio200); + + if ($originalImageMustResize) { + $imageHRatio = $imageHeight / 2000; + $imageResizedOriginalWidth = intval($imageWidth / $imageHRatio); + $image = $uploadedOriginalImage->fit($imageResizedOriginalWidth, 2000)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality); + } else { + $image = $uploadedOriginalImage->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality); + } + $image->fit($resizeWidth, 768)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_medium' . '.' . $fileExt); + $image->fit(600, 600)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_thumbnail' . '.' . $fileExt); + } else { + + $ratio = $imageHeight / 768; + $resizeWidth = intval($imageWidth / $ratio); + + $ratioThumbnail = 768 / 600; + $resizeThumbWidth = intval($resizeWidth / $ratioThumbnail); + + if ($originalImageMustResize) { + $imageWRatio = $imageWidth / 2000; + $imageResizedOriginalHeight = intval($imageHeight / $imageWRatio); + $image = $uploadedOriginalImage->fit(2000, $imageResizedOriginalHeight)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality); + } else { + $image = $uploadedOriginalImage->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt, $quality); + } + + if ($resizeWidth > 1599) { + $image->fit(1600, 768)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_medium' . '.' . $fileExt); + $image->fit(600, 600)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_thumbnail' . '.' . $fileExt); + } else { + $image->fit($resizeWidth, 768)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_medium' . '.' . $fileExt); + $image->fit(600)->encode($imageExtension, $quality)->orientate()->save($filePath . $fileName . '_thumbnail' . '.' . $fileExt); + } + + } + + $response = [ + 'status' => true, + 'data' => [ + //'photo_path' => $urlPath, + //'photo_name' => $fileName, + 'image' => $fileName . '.' . $fileExt, + //'file_size' => $fileSize, + //'file_ext' => $fileExt, + 'image_resolution' => $imageWidth . 'x' . $imageHeight, + 'imageUrl' => [ + 'default' => Config::get('app.imageUrl') . '/property-web-popup/' . $params['property_id'] . '/' . $fileName . '.' . $fileExt, + 'thumb' => Config::get('app.imageUrl') . '/property-web-popup/' . $params['property_id'] . '/' . $fileName . '_thumbnail.' . $fileExt, + 'fixed' => Config::get('app.imageUrl') . '/property-web-popup/' . $params['property_id'] . '/' . $fileName . '_medium.' . $fileExt, + ] + + ] + ]; + + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['status'] = false; + } + + return output($response); + } +} diff --git a/app/Core/Service/PropertyWebRoomMappingService.php b/app/Core/Service/PropertyWebRoomMappingService.php new file mode 100644 index 0000000..cc04bb1 --- /dev/null +++ b/app/Core/Service/PropertyWebRoomMappingService.php @@ -0,0 +1,202 @@ +propertyWebRoomMappingRepository = $propertyWebRoomMappingRepository; + $this->propertyRoomRepository = $propertyRoomRepository; + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebRoomMappingRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function createPropertyWebRoomMapping($param = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + // TODO validation yazılmalı! + + $propertyWebMenuMappingResult = $this->propertyWebRoomMappingRepository->insert($param); + + if ($propertyWebMenuMappingResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => NULL + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyWebRoomMapping($params, $select = ['*']){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'property_web_id', 'condition' => '=', 'value' => $params['property_web_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $searchResult = $this->select($searchCriteria, $select); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_room_mapping' => $return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + + } + + public function updatePropertyWebRooomMapping($params = []){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + + $getDeletePropertyWebRoomRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')] + ] + ]; + + $status = null; + if($params['action'] === "publish"){ + $status = 1; + }else if($params['action'] === "preview"){ + + $getDeletePropertyWebRoomRequest['criteria'][] = ['field' => 'status', 'condition' => '=', 'value' => 2]; + $status = 2; + } + + $deleteWebRoomId = $this->propertyWebRoomMappingRepository->findByCriteria($getDeletePropertyWebRoomRequest, ['id']) ; + $deleteWebRoomId = array_column($deleteWebRoomId, 'id') ; + + if (!empty($deleteWebRoomId)) { + $destroyStatus = $this->propertyWebRoomMappingRepository->destroy($deleteWebRoomId); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $myWebRooms = !empty($params['rooms']) ? $params['rooms'] : []; + + + $propertyRoomCheckCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ], + 'whereIn' => [ + ['field' => 'id', 'value' => $myWebRooms] + ] + ]; + + $propertyRoomCheck = $this->propertyRoomRepository->findByCriteria($propertyRoomCheckCriteria, ['id']) ; + $myWebRooms = $params['rooms']; + + if(count($propertyRoomCheck) != count($params['rooms'])) { + throw new Exception('Different room type count.'); + } + + $createPropertyWebRoomMappingData = []; + foreach ($myWebRooms as $key => $myWebRoom){ + + $createPropertyWebRoomMappingData[$key]['property_id'] = fillOnUndefined($params,'property_id'); + $createPropertyWebRoomMappingData[$key]['property_web_id'] = fillOnUndefined($params,'property_web_id'); + $createPropertyWebRoomMappingData[$key]['property_room_id'] = $myWebRoom; + $createPropertyWebRoomMappingData[$key]['status'] = $status; + $createPropertyWebRoomMappingData[$key]['created_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebRoomMappingData[$key]['updated_by'] = fillOnUndefined($params, 'user_id'); + $createPropertyWebRoomMappingData[$key]['created_at'] = time(); + $createPropertyWebRoomMappingData[$key]['updated_at'] = time(); + } + + $propertyWebRoomMappingResult = $this->createPropertyWebRoomMapping($createPropertyWebRoomMappingData); + if ($propertyWebRoomMappingResult['status'] != 'success'){ + throw new ApiErrorException($propertyWebRoomMappingResult['message']); + } + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_room_mapping' => $return ] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + +} diff --git a/app/Core/Service/PropertyWebService.php b/app/Core/Service/PropertyWebService.php new file mode 100644 index 0000000..08e635b --- /dev/null +++ b/app/Core/Service/PropertyWebService.php @@ -0,0 +1,1179 @@ +propertyWebRepository = $propertyWebRepository; + $this->propertyWebGroupRepository = $propertyWebGroupRepository; + $this->propertyWebCreateValidator = $propertyWebCreateValidator; + $this->propertyWebUpdateValidator = $propertyWebUpdateValidator; + $this->propertyWebPhotoMappingRepository = $propertyWebPhotoMappingRepository; + $this->propertyPhotoService = $propertyPhotoService; + $this->propertyWebUpdateContentValidator = $propertyWebUpdateContentValidator; + $this->propertyWebPublishValidator = $propertyWebPublishValidator; + $this->propertyWebMenuService = $propertyWebMenuService; + $this->propertyWebMenuMappingService = $propertyWebMenuMappingService; + $this->propertyWebSetupService = $propertyWebSetupService; + $this->propertyWebLanguageMappingService = $propertyWebLanguageMappingService; + $this->propertyWebColorMappingService = $propertyWebColorMappingService; + $this->propertyWebAboutUsService = $propertyWebAboutUsService; + $this->propertyAdditionalInfoService = $propertyAdditionalInfoService; + $this->propertyWebRoomMappingService = $propertyWebRoomMappingService; + $this->propertyWebPlaceMappingService = $propertyWebPlaceMappingService; + $this->propertyWebComponentRepository = $propertyWebComponentRepository; + $this->propertyWebComponentMappingRepository = $propertyWebComponentMappingRepository; + $this->propertyWebReviewRepository = $propertyWebReviewRepository; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $validationResult = $this->propertyWebCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + "property_id" => fillOnUndefined($param, "property_id"), + "domain" => fillOnUndefined($param, "domain"), + "default_language" => fillOnUndefined($param, "default_language"), + "template_id" => fillOnUndefined($param, "template_id"), + "token" => getGuid(), + "status" => 1, + "is_published" => 0, + "is_dns_checked" => 0, + "created_by" => fillOnUndefined($param, "user_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->propertyWebRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function selectWebGroup($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebGroupRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_web_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $propertyWebData = $this->propertyWebRepository->findByCriteria($propertyRequest, ['id', 'domain']); + + if (!$propertyWebData) { + throw new ApiErrorException(lang('Property - Web mapping not found.')); + } + + $updateData = + [ + "domain" => fillOnUndefined($param, "domain"), + "default_language" => fillOnUndefined($param, "default_language"), + "template_id" => fillOnUndefined($param, "template_id"), + "updated_by" => fillOnUndefined($param, "user_id"), + "updated_at" => time(), + ]; + + $validationResult = $this->propertyWebUpdateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $updateResult = $this->propertyWebRepository->update($id, $updateData); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateLanguages($param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_web_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + $propertyWebData = $this->propertyWebRepository->findByCriteria($propertyRequest, ['id', 'domain']); + + if (!$propertyWebData) { + throw new ApiErrorException(lang('Property - Web updateLanguage data not found.')); + } + + $updateData = + [ + "languages" => fillOnUndefined($param, "languages"), + "updated_by" => fillOnUndefined($param, "user_id"), + "updated_at" => time(), + ]; + + $updateResult = $this->propertyWebRepository->update($propertyWebData['id'], $updateData); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + + } + + public function getPropertyWeb($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($params, 'id')], + ], + "with" => ['propertyWebTemplate', 'propertyWebLanguage'], + "firstRow" => 1 + ]; + $propertyWebs = $this->select($propertyRequest, ['id', 'domain', 'default_language', 'template_id', 'token', 'status', 'is_published', 'is_dns_checked', 'weather_active', 'cover_video_id']); + if ($propertyWebs['status'] != 'success' || !$propertyWebs['data']) { + throw new ApiErrorException('Property Web Data not found'); + } + + $response = [ + 'status' => true, + 'data' => $propertyWebs['data'], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function insertAllPropertyWebPhotosToMapping($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyPhotoRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + ]; + + $propertyPhotos = $this->propertyPhotoService->select($propertyPhotoRequest, ['*']); + if ($propertyPhotos['status'] != 'success') { + throw new ApiErrorException('Property Photo Data not found'); + } + + $propertyPhotos = $propertyPhotos['data'] ? $propertyPhotos['data'] : []; + $insertPhotos = []; + + foreach ($propertyPhotos as $propertyPhoto) { + $insertPhotos[] = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'id'), + 'property_photo_id' => $propertyPhoto['id'], + 'is_cover' => 0, + 'status' => 1, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $propertyWebPhotos = $this->propertyWebPhotoMappingRepository->insert($insertPhotos); + if ($propertyWebPhotos['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getPropertyWebPhotos($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $propertyPhotoRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + + $propertyPhotos = $this->propertyPhotoService->getPropertyPhotos($propertyPhotoRequest); + if ($propertyPhotos['status'] != 'success') { + throw new ApiErrorException('Property Photo Data not found'); + } + $propertyPhotos = $propertyPhotos['data']; + $propertyWebPhotoMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + ]; + $propertyWebMappingPhotos = $this->propertyWebPhotoMappingRepository->findByCriteria($propertyWebPhotoMappingRequest); + + $propertyWebMappingPhotos = $propertyWebMappingPhotos ? $propertyWebMappingPhotos : []; + $propertyWebPhotos = collect($propertyWebMappingPhotos)->keyBy('property_photo_id')->toArray(); + $propertyWebCover = collect($propertyWebMappingPhotos)->where('is_cover', '=', 1)->keyBy('property_photo_id')->toArray(); + + $photoArray = []; + foreach ($propertyPhotos as $propertyPhoto) { + $propertyWebCoverPhoto = isset($propertyWebCover[$propertyPhoto['id']]) ? $propertyWebCover[$propertyPhoto['id']] : null; + $photoArray[] = [ + 'photo_id' => $propertyPhoto['id'], + 'photo_url_thump' => $propertyPhoto['photo_url_thump'], + 'is_cover' => isset($propertyWebCover[$propertyPhoto['id']]), + 'cover_order' => fillOnUndefined($propertyWebCoverPhoto,'cover_order'), + 'is_selected' => isset($propertyWebPhotos[$propertyPhoto['id']]), + 'is_compatible_with_myweb_slider' => $propertyPhoto['is_compatible_with_myweb_slider'] + + ]; + } + + $response = [ + 'status' => true, + 'data' => $photoArray, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function selectPropertyWebPhotos($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebPhotoMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + + public function updatePropertyWebContent($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + DB::beginTransaction(); + $validationResult = $this->propertyWebUpdateContentValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $updateData = [ + "weather_active" => fillOnUndefined($params, "weather_active", null), + "cover_video_id" => fillOnUndefined($params, "cover_video_id", null), + "updated_at" => time(), + ]; + + $this->propertyWebRepository->update($params['property_web_id'], $updateData); + + + $languagesUpdateData = [ + "property_id" => fillOnUndefined($params, "property_id"), + "property_web_id" => fillOnUndefined($params, "property_web_id"), + "user_id" => fillOnUndefined($params, "user_id") + ]; + $languagesUpdateData['languages'] = NULL; + + if (!empty($params['languages'])) { + $languagesUpdateData['languages'] = json_encode($params['languages']); + } + + + $updatePropertyWebLanguageMappingResult = $this->propertyWebLanguageMappingService->updatePropertyWebLanguageMapping($params); + if ($updatePropertyWebLanguageMappingResult['status'] != 'success') { + throw new ApiErrorException($updatePropertyWebLanguageMappingResult['message']); + } + + $updatePropertyWebColorMappingResult = $this->propertyWebColorMappingService->updatePropertyWebColorMapping($params); + if ($updatePropertyWebColorMappingResult['status'] != 'success') { + throw new ApiErrorException($updatePropertyWebColorMappingResult['message']); + } + + $updatePropertyWebMenuMappingResult = $this->propertyWebMenuMappingService->updatePropertyWebMenuMapping($params); + if ($updatePropertyWebMenuMappingResult['status'] != 'success') { + throw new ApiErrorException($updatePropertyWebMenuMappingResult['message']); + } + + $updatePropertyWeAboutUsResult = $this->propertyWebAboutUsService->updatePropertyWebAboutUs($params); + if ($updatePropertyWeAboutUsResult['status'] != 'success') { + throw new ApiErrorException($updatePropertyWeAboutUsResult['message']); + } + + $updatePropertyWebRoomMappingResult = $this->propertyWebRoomMappingService->updatePropertyWebRooomMapping($params); + if ($updatePropertyWebRoomMappingResult['status'] != 'success') { + throw new ApiErrorException($updatePropertyWebRoomMappingResult['message']); + } + + + $updatePropertyWebPlaceMappingResult = $this->propertyWebPlaceMappingService->updatePropertyWebPlaceMapping($params); + if ($updatePropertyWebPlaceMappingResult['status'] != 'success') { + throw new ApiErrorException($updatePropertyWebPlaceMappingResult['message']); + } + + $updateAdditionalInfoData = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'additional_info' => fillOnUndefined($params, 'additional_info'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $params['user_id'], + 'country_code' => fillOnUndefined($params, 'property_country_code'), + ]; + + if ($params['action'] == "publish") { + $updateAdditionalInfo = $this->propertyAdditionalInfoService->updateOrCreatePropertyAdditionalInfo($updateAdditionalInfoData); + + if ($updateAdditionalInfo['status'] != 'success') { + throw new ApiErrorException($updateAdditionalInfo['message']); + } + } + + if ($params['action'] == "preview") { + $updatePhotoPreview = $this->updatePhotoPreview($params); + if ($updatePhotoPreview['status'] != 'success') { + throw new ApiErrorException($updatePhotoPreview['message']); + } + } elseif ($params['action'] == "publish") { + $updatePhotoPublish = $this->updatePhotoPublish($params); + if ($updatePhotoPublish['status'] != 'success') { + throw new ApiErrorException($updatePhotoPublish['message']); + } + } + + + //Domain Cache Clean + //if ($params['action'] == "publish") { + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], 'firstRow' => true + ]; + $propertyWebData = $this->propertyWebRepository->findByCriteria($propertyRequest, ['id', 'domain']); + + if ($propertyWebData) { + $checkDomainStatusCacheKey = 'myweb:' . 'checkDomainStatus'.md5('checkDomainStatus-' . $propertyWebData['domain']); + if (!empty(Redis::executeRaw(['KEYS', $checkDomainStatusCacheKey]))) { + Redis::executeRaw(['DEL', $checkDomainStatusCacheKey]); + } + } + //} + //Domain Cache Clean + + $response = [ + 'status' => true, + 'data' => null, + ]; + + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + + } + + return output($response); + } + + public function updatePhotoPreview($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + // delete preview photos ; + $getDeletePhotosRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')], + ['field' => 'status', 'condition' => '=', 'value' => 2], + ], + ]; + + $deleteWebMappingPhotos = $this->propertyWebPhotoMappingRepository->findByCriteria($getDeletePhotosRequest, ['id']); + $deleteWebMappingPhotos = $deleteWebMappingPhotos ? $deleteWebMappingPhotos : []; + $deleteMappingIds = array_column($deleteWebMappingPhotos, 'id'); + if ($deleteMappingIds) { + $destroyStatus = $this->propertyWebPhotoMappingRepository->destroy($deleteMappingIds); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + + $coverPhotos = $params['cover_photos']; + $photoMapping = $params['photo_mapping']; + $insertPhotos = []; + + $coverOrder = 1; + foreach ($coverPhotos as $photoId) { + $insertPhotos[] = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'property_photo_id' => $photoId, + 'is_cover' => 1, + 'cover_order' => $coverOrder, + 'status' => 2, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + $coverOrder++; + } + + foreach ($photoMapping as $photoId) { + $insertPhotos[] = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'property_photo_id' => $photoId, + 'is_cover' => 0, + 'cover_order' => null, + 'status' => 2, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $propertyWebPhotos = $this->propertyWebPhotoMappingRepository->insert($insertPhotos); + if ($propertyWebPhotos['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + + } + + return output($response); + } + + public function updatePhotoPublish($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + // delete preview photos ; + $getDeletePhotosRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')], + ], + ]; + + $deleteWebMappingPhotos = $this->propertyWebPhotoMappingRepository->findByCriteria($getDeletePhotosRequest, ['id']); + $deleteWebMappingPhotos = $deleteWebMappingPhotos ? $deleteWebMappingPhotos : []; + $deleteMappingIds = array_column($deleteWebMappingPhotos, 'id'); + if ($deleteMappingIds) { + $destroyStatus = $this->propertyWebPhotoMappingRepository->destroy($deleteMappingIds); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $coverPhotos = $params['cover_photos']; + $photoMapping = $params['photo_mapping']; + $insertPhotos = []; + + $coverPhotosCollect = collect($coverPhotos); + $photoMappingCollect = collect($photoMapping); + $photoMapping = $photoMappingCollect->diff($coverPhotosCollect)->toArray(); + + $coverOrder = 1; + foreach ($coverPhotos as $photoId) { + $insertPhotos[] = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'property_photo_id' => $photoId, + 'is_cover' => 1, + 'cover_order' => $coverOrder, + 'status' => 1, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + $coverOrder++; + } + + foreach ($photoMapping as $photoId) { + $insertPhotos[] = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'property_photo_id' => $photoId, + 'is_cover' => 0, + 'cover_order' => null, + 'status' => 1, + "created_by" => fillOnUndefined($params, "user_id"), + "updated_by" => fillOnUndefined($params, "user_id"), + "created_at" => time(), + "updated_at" => time(), + ]; + } + + $propertyWebPhotos = $this->propertyWebPhotoMappingRepository->insert($insertPhotos); + if ($propertyWebPhotos['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + + } + + return output($response); + } + + public function setPublishWeb($param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $validationResult = $this->propertyWebPublishValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_web_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + $propertyWebData = $this->propertyWebRepository->findByCriteria($propertyRequest, ['id', 'domain']); + + if (!$propertyWebData) { + throw new ApiErrorException(lang('Property - Web mapping not found.')); + } + + $updateData = + [ + "is_published" => fillOnUndefined($param, "is_publish", 0), + "updated_by" => fillOnUndefined($param, "user_id"), + "updated_at" => time(), + ]; + + + $updateResult = $this->propertyWebRepository->update($param['property_web_id'], $updateData); + + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function checkDnsStatus() + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $propertyRequest = [ + 'criteria' => [ + + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_dns_checked', 'condition' => '=', 'value' => 0], + ] + ]; + $propertyWebData = $this->propertyWebRepository->findByCriteria($propertyRequest, ['id', 'domain']); + + if (!$propertyWebData) { + throw new ApiErrorException(lang('Property - Web mapping not found.')); + } + + foreach ($propertyWebData as $web) { + + $checkHostName = $web['domain']; + $domainStatus = 0; + $hostNameArray = parse_url($checkHostName); + + if (isset($hostNameArray['host'])) { + $hostName = $hostNameArray['host']; + } else { + $pathArray = explode('/', $hostNameArray['path']); + $hostName = $pathArray[0]; + } + + $recordsArray = []; + $checkRecords = dns_get_record($hostName, DNS_CNAME, $authns_cname, $addtl_cname); + $recordsArray = array_merge($recordsArray, $checkRecords); + $recordsCollection = collect($recordsArray); + + if ($recordsCollection->where('type', 'CNAME')->where('target', '=', Config::get('app.getMyWebCNAME'))->isNotEmpty() === true) { + $domainStatus = 1; + } + + if ($domainStatus == 1) { + $updateData = + [ + "is_dns_checked" => 1, + "updated_by" => 1, + "updated_at" => time(), + ]; + $updateResult = $this->propertyWebRepository->update($web['id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + } + $response = [ + 'status' => true, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function selectPropertyWebComponent($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebComponentRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function selectPropertyWebComponentMapping($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebComponentMappingRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function createPropertyWebComponentMapping($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = [ + 'property_id' => fillOnUndefined($param, 'property_id'), + 'channel_id' => fillOnUndefined($param, 'channel_id'), + 'component_id' => fillOnUndefined($param, 'component_id'), + 'parameter' => fillOnUndefined($param, 'parameter'), + 'language' => fillOnUndefined($param, 'language'), + 'status' => 1, + 'created_by' => fillOnUndefined($param, 'created_by'), + 'updated_by' => fillOnUndefined($param, 'updated_by'), + 'created_at' => time(), + 'updated_at' => time(), + ]; + + $createResult = $this->propertyWebComponentMappingRepository->create($insertData); + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + //Domain Cache Clean + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], 'firstRow' => true + ]; + $propertyWebData = $this->propertyWebRepository->findByCriteria($propertyRequest, ['id', 'domain']); + + if ($propertyWebData) { + $checkDomainStatusCacheKey = 'myweb:' . 'checkDomainStatus'.md5('checkDomainStatus-' . $propertyWebData['domain']); + if (!empty(Redis::executeRaw(['KEYS', $checkDomainStatusCacheKey]))) { + Redis::executeRaw(['DEL', $checkDomainStatusCacheKey]); + } + } + //Domain Cache Clean + + $response = [ + 'status' => true, + 'data' => $createResult['data'], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updatePropertyWebComponentMapping($id, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateData = [ + 'property_id' => fillOnUndefined($param, 'property_id'), + 'channel_id' => fillOnUndefined($param, 'channel_id'), + 'component_id' => fillOnUndefined($param, 'component_id'), + 'parameter' => fillOnUndefined($param, 'parameter'), + 'language' => fillOnUndefined($param, 'language'), + 'updated_by' => fillOnUndefined($param, 'updated_by'), + 'updated_at' => time(), + ]; + + $updateResult = $this->propertyWebComponentMappingRepository->update($id, $updateData); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + //Domain Cache Clean + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], 'firstRow' => true + ]; + $propertyWebData = $this->propertyWebRepository->findByCriteria($propertyRequest, ['id', 'domain']); + + if ($propertyWebData) { + $checkDomainStatusCacheKey = 'myweb:' . 'checkDomainStatus'.md5('checkDomainStatus-' . $propertyWebData['domain']); + if (!empty(Redis::executeRaw(['KEYS', $checkDomainStatusCacheKey]))) { + Redis::executeRaw(['DEL', $checkDomainStatusCacheKey]); + } + } + //Domain Cache Clean + + $response = [ + 'status' => true, + 'data' => $updateResult['data'], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deletePropertyWebComponentMapping($id) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $deleteCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $id] + ] + ]; + + $deleteResult = $this->propertyWebComponentMappingRepository->delete($deleteCriteria); + if (empty($deleteResult)) { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + /*** Property Web Review ***/ + + public function selectPropertyWebReview($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebReviewRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function createPropertyWebReview($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = [ + 'property_id' => fillOnUndefined($param, 'property_id'), + 'channel_id' => fillOnUndefined($param, 'channel_id'), + 'channel' => fillOnUndefined($param, 'channel'), + 'code' => fillOnUndefined($param, 'code'), + 'language_code' => fillOnUndefined($param, 'language_code'), + 'author' => fillOnUndefined($param, 'author'), + 'profile_photo' => fillOnUndefined($param, 'profile_photo'), + 'rating' => fillOnUndefined($param, 'rating'), + 'review' => fillOnUndefined($param, 'review'), + 'time' => fillOnUndefined($param, 'time'), + 'status' => 1, + 'created_by' => fillOnUndefined($param, 'created_by'), + 'updated_by' => fillOnUndefined($param, 'updated_by'), + 'created_at' => time(), + 'updated_at' => time(), + ]; + + + $createResult = $this->propertyWebReviewRepository->create($insertData); + if ($createResult['status'] != 'success') { + throw new ApiErrorException($createResult['message']); + } + + $response = [ + 'status' => true, + 'data' => $createResult['data'], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + /*** Property Web Review ***/ + + +} diff --git a/app/Core/Service/PropertyWebSetupService.php b/app/Core/Service/PropertyWebSetupService.php new file mode 100644 index 0000000..cf3985b --- /dev/null +++ b/app/Core/Service/PropertyWebSetupService.php @@ -0,0 +1,159 @@ +propertyWebSetupRepository = $propertyWebSetupRepository; + + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + $data = $this->propertyWebSetupRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + + public function getPropertyWebSetup($params = []){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + $searchCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')], + ], + 'firstRow' => true + ]; + + $searchResult = $this->select($searchCriteria, ['*']); + + if($searchResult['status'] != 'success'){ + throw new ApiErrorException($searchResult['message']); + } + + $return = $searchResult['data']; + + $response = ['statusCode' => 200, 'status' => true, 'message' => '', 'data' => ['property_web_setup' => $return ] ]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function updatePropertyWebSetup($params = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + DB::beginTransaction(); + + $getDeletePropertyWebSetupRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')] + ] + ]; + + $status = null; + if($params['action'] === "publish"){ + $status = 1; + }else if($params['action'] === "preview"){ + $getDeletePropertyWebSetupRequest['criteria'][] = ['field' => 'status', 'condition' => '=', 'value' => 2]; + $status = 2; + } + + $deleteWebSetupId = $this->propertyWebSetupRepository->findByCriteria($getDeletePropertyWebSetupRequest, ['id']) ; + $deleteWebSetupId = array_column($deleteWebSetupId, 'id') ; + + if (!empty($deleteWebSetupId)) { + $destroyStatus = $this->propertyWebSetupRepository->destroy($deleteWebSetupId); + if ($destroyStatus['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + } + + $webSetupInsertData = [ + "property_id" => fillOnUndefined($params, 'property_id'), + "property_web_id" => fillOnUndefined($params, 'property_web_id'), + "languages" => empty($params['languages']) ? NULL : json_encode($params['languages']), + "color" => empty($params['colors']) ? NULL : json_encode($params['colors']), + "status" => $status, + "created_by" => fillOnUndefined($params, 'user_id'), + "updated_by" => fillOnUndefined($params, 'user_id'), + "created_at" => time(), + "updated_at" => time() + ]; + + $propertyWebSetup = $this->propertyWebSetupRepository->insert($webSetupInsertData) ; + if ($propertyWebSetup['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => null + ]; + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + DB::rollBack(); + } + + return output($response); + + } + + + + +} diff --git a/app/Core/Service/PropertyWebTemplateService.php b/app/Core/Service/PropertyWebTemplateService.php new file mode 100644 index 0000000..76767c2 --- /dev/null +++ b/app/Core/Service/PropertyWebTemplateService.php @@ -0,0 +1,125 @@ +propertyWebTemplateRepository = $propertyWebTemplateRepository; + $this->propertyWebTemplateCreateValidator = $propertyWebTemplateCreateValidator; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->propertyWebTemplateCreateValidator->validate($param); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $insertData = + [ + "name" => fillOnUndefined($param, "name"), + "folder_name" => fillOnUndefined($param, "folder_name"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + $userCreateResult = $this->propertyWebTemplateRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->propertyWebTemplateRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyWebTemplateRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Service/QRCodeService.php b/app/Core/Service/QRCodeService.php new file mode 100644 index 0000000..029f25a --- /dev/null +++ b/app/Core/Service/QRCodeService.php @@ -0,0 +1,33 @@ +generator = $generator; + } + + public function generate($url, $size){ + + $message = "Error occurred while generating qr code"; + $response = ['status' => false, 'message' => $message, 'data' => NULL ]; + + try { + $qrCode = $this->generator->size($size)->format('png')->generate(url($url)); + $response = ['status' => true, 'message' => '', 'data' => $qrCode ]; + }catch (Exception $e){ + Log::error($message); + } + + return $response; + + } + +}*/ diff --git a/app/Core/Service/ReputationManagementService.php b/app/Core/Service/ReputationManagementService.php new file mode 100644 index 0000000..7646cbc --- /dev/null +++ b/app/Core/Service/ReputationManagementService.php @@ -0,0 +1,227 @@ +propertyReviewRepository = $propertyReviewRepository; + $this->propertyReviewChannelRepository = $propertyReviewChannelRepository; + $this->propertyReviewChannelMappingRepository = $propertyReviewChannelMappingRepository; + $this->propertyReviewCategoryRepository = $propertyReviewCategoryRepository; + + } + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $data = $this->propertyReviewRepository->findByCriteria($param, $column); + if (!$data) { + throw new ApiErrorException(lang('An unknown error occurred')); + } + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectChannel($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $data = $this->propertyReviewChannelRepository->findByCriteria($param, $column); + if (!$data) { + throw new ApiErrorException(lang('An unknown error occurred')); + } + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectCategory($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $data = $this->propertyReviewCategoryRepository->findByCriteria($param, $column); + if (!$data) { + throw new ApiErrorException(lang('An unknown error occurred')); + } + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function selectChannelMapping($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $data = $this->propertyReviewChannelMappingRepository->findByCriteria($param, $column); + if (!$data) { + throw new ApiErrorException(lang('An unknown error occurred')); + } + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function createChannelMapping($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $createParam = [ + 'property_id' => fillOnUndefined($param, 'property_id'), + 'channel_id' => fillOnUndefined($param, 'channel_id'), + 'parameter' => fillOnUndefined($param, 'parameter'), + 'status' => fillOnUndefined($param, 'status'), + 'created_by' => fillOnUndefined($param, 'created_by'), + 'created_at' => time() + + ]; + $createResult = $this->propertyReviewChannelMappingRepository->create($createParam); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $createResult["data"]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateChannelMapping($id, $param = []) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $updateResult = $this->propertyReviewChannelMappingRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $updateResult["data"]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function deleteChannelMapping($id) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $deleteCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $id] + ] + ]; + + $deleteResult = $this->propertyReviewChannelMappingRepository->delete($deleteCriteria); + if (empty($deleteResult)) { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $deleteResult, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } +} diff --git a/app/Core/Service/ServiceLogService.php b/app/Core/Service/ServiceLogService.php new file mode 100644 index 0000000..caa26a0 --- /dev/null +++ b/app/Core/Service/ServiceLogService.php @@ -0,0 +1,154 @@ +serviceLogRepository = $serviceLogRepository; + + } + + public function create($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $insertData = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => fillOnUndefined($params, 'user_id', 1), + 'type' => fillOnUndefined($params, 'type', null), + 'service' => fillOnUndefined($params, 'service'), + 'request' => fillOnUndefined($params, 'request'), + 'response' => fillOnUndefined($params, 'response'), + 'response_time' => fillOnUndefined($params, 'response_time'), + 'ip_address' => fillOnUndefined($params, 'ip_address'), + 'status' => fillOnUndefined($params, 'status'), + 'created_by' => fillOnUndefined($params, 'user_id', 1), + 'updated_by' => fillOnUndefined($params, 'user_id', 1), + ]; + + $createResult = $this->serviceLogRepository->create($insertData); + + if ($createResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response = [ + 'status' => true, + 'data' => $createResult["data"], + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->serviceLogRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + + $updateResult = $this->serviceLogRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function updateOrCreate($criteria = [], $saveData = []) + { + + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->serviceLogRepository->updateOrCreate($criteria, $saveData); + if ($data['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response = [ + 'status' => true, + 'data' => $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); + + + } + + +} diff --git a/app/Core/Service/SiteConfigService.php b/app/Core/Service/SiteConfigService.php new file mode 100644 index 0000000..427c5ca --- /dev/null +++ b/app/Core/Service/SiteConfigService.php @@ -0,0 +1,171 @@ +siteConfigRepository = $siteConfigRepository; + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $insertData = + [ + "config_key" => fillOnUndefined($param, "config_key"), + "config_value_json" => fillOnUndefined($param, "config_value_json"), + "key_comment" => fillOnUndefined($param, "key_comment"), + "status" => fillOnUndefined($param, "status", 1), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "updated_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + + + $validationResult = $this->siteConfigCreateValidator->validate($insertData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $userCreateResult = $this->siteConfigRepository->create($insertData); + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userCreateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->siteConfigRepository->findByCriteria($param, $column); + + $response = [ + 'status' => true, + 'data' => $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); + } + + public function update($id, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $updateResult = $this->siteConfigRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + + public function siteHints($params){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + + $siteConfigRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'config_key', 'condition' => '=', 'value' => 'hint_list'], + ], + 'firstRow' => 1 + ]; + + $siteConfig = $this->siteConfigRepository->findByCriteria($siteConfigRequest); + $siteConfig = $siteConfig ? $siteConfig : [] ; + $hintData = fillOnUndefined($siteConfig, 'config_value_json', null); + $hintData = json_decode($hintData,1); + + + foreach ($hintData as $value){ + + $return[] = [ + 'title' => $value['title'], + 'content' => $value['content'], + 'icon' => $value['icon'], + 'url' => $value['url'], + ]; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch(ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + +} diff --git a/app/Core/Service/TempPropertyService.php b/app/Core/Service/TempPropertyService.php new file mode 100644 index 0000000..9b675b7 --- /dev/null +++ b/app/Core/Service/TempPropertyService.php @@ -0,0 +1,89 @@ +request = $request; + $this->tempPropertyRepository = $tempPropertyRepository; + $this->tempPropertySearchValidator = $tempPropertySearchValidator; + } + + public function search($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $criteria = fillOnUndefined($param , 'criteria'); + $searchData = [] ; + foreach ($criteria as $item){ + $searchData[$item['field']] = $item['value']; + } + $validationResult = $this->tempPropertySearchValidator->validate($searchData); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $orWhere[] = ['field' => 'name', 'condition' => 'LIKE', 'value' => $searchData['name']]; + $requestData['orWhere'] = $orWhere; + $data = $this->tempPropertyRepository->searchTempProperty($requestData, $column); + $response = [ + 'status' => true, + 'data' => $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); + + } + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->tempPropertyRepository->findByCriteria($param, $column); + $response = [ + 'status' => true, + 'data' => $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); + } + +} diff --git a/app/Core/Service/ThirdPartyServices/MondayService.php b/app/Core/Service/ThirdPartyServices/MondayService.php new file mode 100644 index 0000000..1a9bb47 --- /dev/null +++ b/app/Core/Service/ThirdPartyServices/MondayService.php @@ -0,0 +1,122 @@ +restClient = $restClient; + $this->url = 'https://api.monday.com/v2'; + + $this->authorization = 'eyJhbGciOiJIUzI1NiJ9.eyJ0aWQiOjEzMTcwODMyNiwidWlkIjoyMjE2NDU2MiwiaWFkIjoiMjAyMS0xMS0wNFQxNzo1OTo0Ni4wMDBaIiwicGVyIjoibWU6d3JpdGUiLCJhY3RpZCI6OTAxNjcyOSwicmduIjoidXNlMSJ9.VT5EjPvtx2CzoF-ZktcrqLSyxp3A6gmgnNdfDwdIExE'; + $this->contactBoardId = 1306161833; + } + + public function requestPost($query) + { + $response = ['status' => false, 'message' => '']; + + try { + $this->restClient = new Client(['http_errors' => false]); + $res = $this->restClient->post($this->url, + [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => $this->authorization + ], + 'body' => json_encode(['query' => $query]), + ] + ); + + $getResponseBody = $res->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponse = json_decode($getResponse, 1); + + if(isset($getResponse['errors'])) { + throw new ApiErrorException(pickItemFromArray('message',$getResponse['errors'])); + } + + $response = ["status" => true, 'message' => '', "data" => $getResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function createBoardItems($params = []) + { + $response = ['status' => false, 'message' => '']; + + try { + + $columnValues = [ + 'text' => $params['property_name'], + 'email' => [ + 'email' => $params['email'], + 'text' => $params['email'], + ], + 'phone' => [ + 'phone' => str_replace(' ', '', $params['phone']), + ], + 'long_text4' => 'SignupForm', + 'metin' => mb_strtoupper($params['language']), + 'tarih' => [ + 'date' => Carbon::now()->toDateString(), + 'time' => Carbon::now()->format('H:i:s') + ], + ]; + + $columnValues = json_encode(json_encode($columnValues)); + + $nameSurname = $params['name'] . ' ' . $params['surname']; + + $query = <<contactBoardId}, item_name: "{$nameSurname}", column_values : {$columnValues} ) { id } } +BUR; + + $responseData = $this->requestPost($query); + + if(!$responseData['status']) { + throw new ApiErrorException($responseData['message']); + } + + $response = [ + 'status' => true, + 'data' => $responseData['data'] + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + } + +} diff --git a/app/Core/Service/UserPropertyMappingService.php b/app/Core/Service/UserPropertyMappingService.php new file mode 100644 index 0000000..e7b8a14 --- /dev/null +++ b/app/Core/Service/UserPropertyMappingService.php @@ -0,0 +1,254 @@ +request = $request; + $this->userPropertyMappingRepository = $userPropertyMappingRepository; + $this->userPropertyMappingAddValidator = $userPropertyMappingAddValidator; + $this->userPropertyMappingRemoveValidator = $userPropertyMappingRemoveValidator; + } + + + public function select($param = [], $column = ['*']) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $data = $this->userPropertyMappingRepository->findByCriteria($param, $column); + if(!$data){ + throw new ApiErrorException(lang('An unknown error occurred')); + } + + $response['status'] = 1; + $response['data'] = $data; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $userPropertyMappingData = + [ + "user_id" => fillOnUndefined($param, "user_id"), + "property_id" => fillOnUndefined($param, "property_id"), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "created_by"), + "updated_by" => fillOnUndefined($param, "created_by"), + "created_at" => time(), + "updated_at" => time(), + ]; + $propertyCreateResult = $this->userPropertyMappingRepository->create($userPropertyMappingData); + + if ($propertyCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $propertyCreateResult["data"]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function addUserProperty($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $validationResult = $this->userPropertyMappingAddValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $checkPropertyList = $this->isAllowedProperty(['user_id' => $params['user_id'], 'add_property_id' => $params['add_property_id'],]); + + if ($checkPropertyList['status'] != 'success') { + throw new ApiErrorException($checkPropertyList['message']); + } + + $criteria = + [ + "criteria" => + [ + ["field" => "user_id", "condition" => "=", "value" => $params['add_user_id']] + ] + ]; + $oldMappingList = $this->userPropertyMappingRepository->findByCriteria($criteria, ['id', 'user_id', 'property_id', 'status']); + foreach ($params['add_property_id'] as $addProperty) { + + $checkData = collect($oldMappingList) + ->where('user_id' , '=', $params['add_user_id']) + ->where('property_id', '=', $addProperty) + ->first(); + + if(!$checkData){ + $addUserPropertyMappingData = [ + "user_id" => $params['add_user_id'], + "property_id" => $addProperty, + "status" => 1, + "created_by" => $params['user_id'], + ]; + $createStatus = $this->create($addUserPropertyMappingData); + if($createStatus['status'] != 'success'){ + throw new ApiErrorException($createStatus['message']); + } + }else{ + + + $updateUserPropertyMappingData = [ + "status" => 1, + "updated_by" => $params['user_id'], + 'updated_at' => time() + ]; + $updateResult = $this->userPropertyMappingRepository->update($checkData['id'], $updateUserPropertyMappingData) ; + if($updateResult['status'] != 'success'){ + throw new Exception('api-unknown_error'); + } + + } + } + + $response = ['status' => 1 , 'message' => '', 'data' => []]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function removeUserProperty($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $validationResult = $this->userPropertyMappingRemoveValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + foreach ($params['remove_property_id'] as $addProperty) { + + + $updatePropertyCriteria = [ + 'criteria' => [ + ['field' => 'user_id', 'condition' => '=', 'value' => $params['remove_user_id'] ], + ['field' => 'property_id', 'condition' => '=', 'value' => $addProperty], + ] + + ]; + + $updateUserPropertyMappingData = [ + "status" => 0, + "updated_by" => $params['user_id'], + 'updated_at' => time() + ]; + + $updateResult = $this->userPropertyMappingRepository->updateWhere($updatePropertyCriteria, $updateUserPropertyMappingData) ; + if($updateResult['status'] != 'success'){ + throw new Exception('api-unknown_error'); + } + } + + $response = ['status' => 1 , 'message' => '', 'data' => []]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function isAllowedProperty($params) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $criteria = + [ + "criteria" => + [ + ["field" => "user_id", "condition" => "=", "value" => $params['user_id']] + ] + ]; + $mappingList = $this->userPropertyMappingRepository->findByCriteria($criteria, ['id', 'user_id', 'property_id']); + + $allowedProperties = collect($mappingList); + foreach ($params['add_property_id'] as $requestProperty) { + $checkProperty = $allowedProperties->where('property_id', '=', $requestProperty)->count(); + if (!$checkProperty) { + throw new ApiErrorException(lang('property {#1} not allowed', [$requestProperty])); + } + } + $response = ['status' => 1, 'message' => '', 'data' => []]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + return output($response); + + } +} diff --git a/app/Core/Service/UserService.php b/app/Core/Service/UserService.php new file mode 100644 index 0000000..3069aff --- /dev/null +++ b/app/Core/Service/UserService.php @@ -0,0 +1,518 @@ +request = $request; + $this->userRepository = $userRepository; + $this->userCreateValidator = $userCreateValidator; + $this->userNewPasswordValidator = $userNewPasswordValidator; + $this->changePasswordValidator = $changePasswordValidator; + $this->resetPasswordValidator = $resetPasswordValidator; + $this->profileUpdateValidator = $profileUpdateValidator; + } + + /* + * + select + create + update + delete + + * */ + + public function select($param = [], $column = ['*']) + { + + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $data = $this->userRepository->findByCriteria($param, $column); + if(!$data){ + throw new ApiErrorException(lang('An unknown error occurred')); + } + + $response['status'] = 1; + $response['data'] = $data; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function create($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + // Todo add permission + $userData = + [ + "gender" => fillOnUndefined($param, "gender"), + "name" => fillOnUndefined($param, "name"), + "surname" => fillOnUndefined($param, "surname"), + "phone" => fillOnUndefined($param, "phone"), + "language" => fillOnUndefined($param, "language"), + "email" => fillOnUndefined($param, "email"), + "password" => Str::random(6), + "hash_key" => hash('sha512', Str::random(32) ), + "status" => fillOnUndefined($param, "status", 0), + "created_by" => fillOnUndefined($param, "user_id", 1), + "updated_by" => fillOnUndefined($param, "user_id", 1), + "created_at" => time(), + "updated_at" => time(), + ]; + + $validationResult = $this->userCreateValidator->validate($userData); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $userPassword = $userData['password'] ; + $userData['password'] = Hash::make($userData['password']) ; + $userCreateResult = $this->userRepository->create($userData); + + if ($userCreateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response['status'] = 1; + $userCreateResult["data"]["userPassword"] = $userPassword; + $response['data'] = $userCreateResult["data"]; + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function update($param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null ]; + + try { + + // Todo add permission + $userUpdateData = fillOnUndefined($param, 'user_update_data', []) ; + $validateKeys = ['name', 'surname', 'gender', 'phone', 'password', 'status']; + $updateData = [] ; + foreach ($userUpdateData as $key => $value){ + if(!in_array($key,$validateKeys)){ + throw new ApiErrorException(lang('Disallowed field')); + } + $updateData[$key] = $value ; + if($key == 'password'){ + $updateData['password'] = Hash::make($value) ; + } + } + if($updateData){ + $updateData['updated_by'] = $param['user_id'] ; + } + + $userUpdateResult = $this->userRepository->update($param['update_user_id'], $updateData); + if ($userUpdateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $userUpdateResult["data"]; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + private function _updateUserProfile($id, $param = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + $updateResult = $this->userRepository->update($id, $param); + if ($updateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $updateData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $updateData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function forgotPassword($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null ]; + + try { + + $userCriteria = [ + 'criteria' => [ + ['field' => 'email', 'condition' => '=', 'value' => $params['email']], + ], + 'firstRow' => 1 + ]; + $findUser = $this->select($userCriteria); + if (!$findUser['status'] || !$findUser['data']) { + throw new ApiErrorException(lang('User not found')); + } + $findUser = $findUser['data'] ; + $hashKey = hash('sha512', Str::random(32) ); + $updateParams = [ + 'hash_key' => $hashKey, + 'updated_by' => $findUser['id'], + 'updated_at' => time() + ]; + $userUpdateResult = $this->userRepository->update($findUser['id'], $updateParams); + if ($userUpdateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $userData = $userUpdateResult["data"]; + $response = [ + 'status' => 1, + 'data' => $userData, + ]; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function resetPassword($params = []) + { + $response = ['status' => -1, 'message' => '', 'data' => null]; + + try { + + $validationResult = $this->resetPasswordValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $userCriteria = [ + 'criteria' => [ + ['field' => 'email', 'condition' => '=', 'value' => $params['email']], + ['field' => 'hash_key', 'condition' => '=', 'value' => $params['hash_key']], + ], + 'firstRow' => 1 + ]; + $findUser = $this->select($userCriteria); + if (!$findUser['status'] || !$findUser['data']) { + throw new ApiErrorException(lang('User not found')); + } + + $findUser = $findUser['data'] ; + $hashKey = hash('sha512', Str::random(32) ); + $updateParams = [ + 'hash_key' => $hashKey, + 'password' => Hash::make($params['password']), + 'updated_by' => $findUser['id'], + 'updated_at' => time() + ]; + $userUpdateResult = $this->userRepository->update($findUser['id'], $updateParams); + if ($userUpdateResult['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $response['status'] = 1; + $response['data'] = $userUpdateResult["data"]; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function findUser($id) + { + return $this->userRepository->find($id); + } + + public function checkUserKey($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try{ + $userCriteria = [ + 'criteria' => [ + ['field' => 'email' , 'condition' => '=' , 'value' => $params['email'] ], + ['field' => 'hash_key' , 'condition' => '=' , 'value' => $params['key'] ], + ], + 'firstRow' => true, + ]; + + $userData = $this->userRepository->findByCriteria($userCriteria, ['id', 'name', 'surname', 'email', 'hash_key', 'status']) ; + if(!$userData){ + throw new ApiErrorException(lang('User not found')) ; + } + if($userData['status'] == 1){ + throw new ApiErrorException(lang('This user already activated')); + } + + $response['status'] = 1; + $response['data'] = $userData; + + }catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + }catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function newPassword($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null ]; + try{ + + $userData = [ + 'email' => fillOnUndefined($params, 'email'), + 'hash_key' => fillOnUndefined($params, 'hash_key'), + 'password' => fillOnUndefined($params, 'password'), + 'password_confirmation' => fillOnUndefined($params, 'password_confirmation'), + ]; + + $validationResult = $this->userNewPasswordValidator->validate($userData); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + + $userUpdateData = [ + 'password' => Hash::make($userData['password']) , + 'status' => 1 , + 'updated_by' => $params['user_id'], + 'updated_at' => time() + ]; + + $userData = $this->userRepository->update($params['user_id'], $userUpdateData) ; + + if(!$userData){ + throw new Exception('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $userData; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + }catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function changePassword($params){ + + $response = ['status' => -1, 'message' => '', 'data' => null ]; + try{ + + $userData = [ + 'user_id' => fillOnUndefined($params, 'user_id'), + 'old_password' => fillOnUndefined($params, 'old_password'), + 'password' => fillOnUndefined($params, 'password'), + 'password_confirmation' => fillOnUndefined($params, 'password_confirmation'), + ]; + + $validationResult = $this->changePasswordValidator->validate($userData); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $userUpdateData = [ + 'password' => Hash::make($userData['password']) , + 'updated_by' => $params['user_id'], + 'updated_at' => time() + ]; + + $userData = $this->userRepository->update($userData['user_id'], $userUpdateData) ; + + if(!$userData){ + throw new Exception('api-unknown_error'); + } + + $response['status'] = 1; + $response['data'] = $userData; + + + } catch (ApiErrorException $e) { + $response['status'] = 0; + $response['message'] = implode(', ', $e->getMessageArr()); + + }catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + + public function getProfile($params, $fields = ['*']){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $profileRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['user_id']], + ['field' => 'status', 'condition' => '=', 'value' => $params['status']], + ], + 'firstRow' => true + ]; + + $profileData = $this->select($profileRequest, $fields); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $profileData['data'] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return output($response); + } + + public function profileUpdate($params = []){ + + $response = ['status' => -1, 'message' => '', 'data' => null]; + try { + + $validationResult = $this->profileUpdateValidator->validate($params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $updateData = + [ + 'name' => fillOnUndefined($params, 'name'), + 'surname' => fillOnUndefined($params, 'surname'), + 'gender' => fillOnUndefined($params, 'gender'), + 'language' => fillOnUndefined($params, 'language'), + 'phone' => fillOnUndefined($params, 'phone'), + "updated_by" => fillOnUndefined($params, "user_id"), + "updated_at" => time() + ]; + + $updateResult = $this->_updateUserProfile($params['user_id'], $updateData); + if ($updateResult['status'] != 'success') { + throw new ApiErrorException($updateResult['message']); + } + $userData = $updateResult["data"]; + $response = [ + 'status' => true, + 'data' => $userData, + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return output($response); + } + +} diff --git a/app/Core/Validator/BaseValidator.php b/app/Core/Validator/BaseValidator.php new file mode 100644 index 0000000..0f87113 --- /dev/null +++ b/app/Core/Validator/BaseValidator.php @@ -0,0 +1,109 @@ +keyMapping = [ + 'phone' => "enw-input-mobile_phone", + 'photo' => "enw-input-photo", + 'room_rate_id' => "enw-input-rate_select", + 'room_id' => "enw-input-room", + 'description' => "enw-input-description", + 'included_occupancy' => "enw-input-included_occupancy", + 'max_room_occupancy' => "enw-input-max_room_occupancy", + ]; + + } + + public function messages() + { + $messages = []; + $rules = $this->rules(); + foreach ($rules as $key => $rule) { + $ruleArray = explode('|', $rule); + $langKey = isset($this->keyMapping[$key]) ? $this->keyMapping[$key] : $key; + foreach ($ruleArray as $ruleItem) { + $ruleArray = explode(':', $ruleItem) ; + $rule = $ruleArray[0] ; + $value = isset($ruleArray[1]) ? $ruleArray[1] : null ; + switch ($rule) { + case 'required': + $messages[$key . '.' . $rule] = __('error-input-required', ['input' => __($langKey)]); + break; + case 'required_with': + $messages[$key . '.' . $rule] = __('error-input-required', ['input' => __($langKey)]); + break; + case 'required_without': + $messages[$key . '.' . $rule] = __('error-input-required', ['input' => __($langKey)]); + break; + case 'numeric': + $messages[$key . '.' . $rule] = __('error-input-numeric', ['input' => __($langKey)]); + break; + case 'array': + $messages[$key . '.' . $rule] = __('error-input-array', ['input' => __($langKey)]); + break; + case 'date': + $messages[$key . '.' . $rule] = __('error-input-date', ['input' => __($langKey)]); + break; + case 'email': + $messages[$key . '.' . $rule] = __('error-input-email', ['input' => __($langKey)]); + break; + case 'integer': + $messages[$key . '.' . $rule] = __('error-input-integer', ['input' => __($langKey)]); + break; + case 'max': + $messages[$key . '.' . $rule] = __('error-input-max', ['input' => __($langKey), 'max' => $value]); + break; + case 'min': + $messages[$key . '.' . $rule] = __('error-input-min', ['input' => __($langKey), 'min' => $value]); + break; + case 'string': + $messages[$key . '.' . $rule] = __('error-input-string', ['input' => __($langKey)]); + break; + case 'boolean': + $messages[$key . '.' . $rule] = __('error-input-boolean', ['input' => __($langKey)]); + break; + case 'date_format': + $messages[$key . '.' . $rule] = __('error-input-date-format', ['input' => __($langKey), 'format' => $value] ); + break; + case 'in': + $messages[$key . '.' . $rule] = __('error-input-in-array', ['input' => __($langKey), 'value' => $value]); + break; + case 'image': + $messages[$key . '.' . $rule] = __('error-input-image', ['input' => __($langKey)]); + break; + case 'mimes': + $messages[$key . '.' . $rule] = __('error-input-mimes', ['input' => __($langKey)]); + break; + case 'confirmed': + $messages[$key . '.' . $rule] = __('error-input-confirmed', ['input' => __($langKey)]); + break; + case 'accepted': + $messages[$key . '.' . $rule] = __('error-input-accepted', ['input' => __($langKey)]); + break; + case 'present': + $messages[$key . '.' . $rule] = __('error-input-present', ['input' => __($langKey)]); + break; + default : + $messages[$key . '.' . $rule] = __('error-input-unknown-error', ['input' => __($langKey), 'rule' => $ruleItem]); + break; + } + } + } + return $messages; + } +} diff --git a/app/Core/Validator/Booking/BookingUpdateValidator.php b/app/Core/Validator/Booking/BookingUpdateValidator.php new file mode 100644 index 0000000..54161fc --- /dev/null +++ b/app/Core/Validator/Booking/BookingUpdateValidator.php @@ -0,0 +1,47 @@ + 'required|numeric', + 'booking_id' => 'required|numeric', + 'total' => 'required|numeric', + 'currency_code' => 'required|min:3|max:3', + 'status' => 'required|numeric|in:0,1,2,3' + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages(), $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/BookingEngine/ContractUploadValidator.php b/app/Core/Validator/BookingEngine/ContractUploadValidator.php new file mode 100644 index 0000000..892cdca --- /dev/null +++ b/app/Core/Validator/BookingEngine/ContractUploadValidator.php @@ -0,0 +1,49 @@ + 'required|numeric', + 'contract_file' => 'nullable|mimes:pdf|max:10240', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/Destination/DestinationSearchValidator.php b/app/Core/Validator/Destination/DestinationSearchValidator.php new file mode 100644 index 0000000..7a4b06e --- /dev/null +++ b/app/Core/Validator/Destination/DestinationSearchValidator.php @@ -0,0 +1,55 @@ + 'required|min:3', + ]; + + } + + public function messages() + { + $thisMessages = + [ + "search_term.required" => lang("search_term is required"), + "search_term.min" => lang("search_term can be minimum 3 characters"), + ]; + + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/EnwContactForm/EnwContactFormCreateValidator.php b/app/Core/Validator/EnwContactForm/EnwContactFormCreateValidator.php new file mode 100644 index 0000000..a49d449 --- /dev/null +++ b/app/Core/Validator/EnwContactForm/EnwContactFormCreateValidator.php @@ -0,0 +1,46 @@ + 'required|max:100', + "surname" => 'required|max:100', + "email" => 'required|email|max:150', + "subject" => 'required|max:500' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/ExampleValidator.php b/app/Core/Validator/ExampleValidator.php new file mode 100644 index 0000000..1ca981e --- /dev/null +++ b/app/Core/Validator/ExampleValidator.php @@ -0,0 +1,66 @@ + 'required|min:32', + 'array1' => 'required|array|in:d,e,f', + 'phone' => 'required', + 'contact.address' => 'required', + 'contact.email' => 'required|email', + 'id' => 'required|integer' + ]; + } + + + public function messages() + { + + // use example 'validation.input' => __('validation_type', ['input' => __('input language_key')]), + // validation types in baseValidator switch-case block + // + + $thisMessages = [ + 'contact.email.required' => __('error-input-required', ['input' => __('myweb-contact-mail_title')]), + 'name.required' => __('error-input-required', ['input' => __('input-property_name')]), + 'name.min' => __('error-input-min', ['input' => __('input-property_name')]), + + ]; + + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/Offer/OfferCreateValidator.php b/app/Core/Validator/Offer/OfferCreateValidator.php new file mode 100644 index 0000000..6d5e3a5 --- /dev/null +++ b/app/Core/Validator/Offer/OfferCreateValidator.php @@ -0,0 +1,47 @@ + 'required|numeric', + "title" => 'required|min:3|max:200', + "expire_date" => 'required|date', + "language" => 'required|min:2|max:10', + "currency" => 'required|min:3|max:3' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/Offer/OfferStatusValidator.php b/app/Core/Validator/Offer/OfferStatusValidator.php new file mode 100644 index 0000000..dbf07b4 --- /dev/null +++ b/app/Core/Validator/Offer/OfferStatusValidator.php @@ -0,0 +1,45 @@ + 'required|numeric', + "offer_id" => 'required|numeric', + "status" => 'required|boolean' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/Offer/OfferUpdateValidator.php b/app/Core/Validator/Offer/OfferUpdateValidator.php new file mode 100644 index 0000000..729741a --- /dev/null +++ b/app/Core/Validator/Offer/OfferUpdateValidator.php @@ -0,0 +1,46 @@ + 'required|min:3|max:200', + "expire_date" => 'required|date', + "language" => 'required|min:2|max:10', + "currency" => 'required|min:3|max:3' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferAccommodationMapping/OfferAccommodationMappingAddValidator.php b/app/Core/Validator/OfferAccommodationMapping/OfferAccommodationMappingAddValidator.php new file mode 100644 index 0000000..4f73c41 --- /dev/null +++ b/app/Core/Validator/OfferAccommodationMapping/OfferAccommodationMappingAddValidator.php @@ -0,0 +1,49 @@ + 'required|numeric', + "accommodation_mapping" => 'required|array', + "accommodation_mapping.*" => 'required|numeric', + ]; + + + } + + public function messages() + { + $thisMessages = [ + 'accommodation_mapping.required' => __('error-input-accommodation_mapping-required') + + ]; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferAccommodationMapping/OfferAccommodationMappingCreateValidator.php b/app/Core/Validator/OfferAccommodationMapping/OfferAccommodationMappingCreateValidator.php new file mode 100644 index 0000000..cd1fa79 --- /dev/null +++ b/app/Core/Validator/OfferAccommodationMapping/OfferAccommodationMappingCreateValidator.php @@ -0,0 +1,44 @@ + 'required|numeric', + "property_accommodation_id" => 'required|numeric', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferCancellationPolicy/OfferCancellationPolicyCreateValidator.php b/app/Core/Validator/OfferCancellationPolicy/OfferCancellationPolicyCreateValidator.php new file mode 100644 index 0000000..f5a89df --- /dev/null +++ b/app/Core/Validator/OfferCancellationPolicy/OfferCancellationPolicyCreateValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + "has_cancellation_policy" => 'required|boolean', + ]; + + if(fillOnUndefined($params, 'has_cancellation_policy', false) == true) + { + $return['cancellation_policy'] = 'required|array|min:1'; + $return['cancellation_policy.*.days_before'] = 'required|numeric'; + $return['cancellation_policy.*.type'] = 'required|in:FIX,PER'; + $return['cancellation_policy.*.value'] = 'required|numeric'; + } + + return $return; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferContactMapping/OfferContactMappingAddValidator.php b/app/Core/Validator/OfferContactMapping/OfferContactMappingAddValidator.php new file mode 100644 index 0000000..ada466f --- /dev/null +++ b/app/Core/Validator/OfferContactMapping/OfferContactMappingAddValidator.php @@ -0,0 +1,47 @@ + 'required|numeric', + "contact_mapping" => 'array', + "contact_mapping.*" => 'numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferContactMapping/OfferContactMappingCreateValidator.php b/app/Core/Validator/OfferContactMapping/OfferContactMappingCreateValidator.php new file mode 100644 index 0000000..982024e --- /dev/null +++ b/app/Core/Validator/OfferContactMapping/OfferContactMappingCreateValidator.php @@ -0,0 +1,45 @@ + 'required|numeric', + "property_executive_id" => 'required|numeric', + + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferFactMapping/OfferFactMappingAddValidator.php b/app/Core/Validator/OfferFactMapping/OfferFactMappingAddValidator.php new file mode 100644 index 0000000..f09a04a --- /dev/null +++ b/app/Core/Validator/OfferFactMapping/OfferFactMappingAddValidator.php @@ -0,0 +1,45 @@ + 'required|numeric', + "fact_mapping" => 'array', + "fact_mapping.*" => 'numeric', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferFactMapping/OfferFactMappingCreateValidator.php b/app/Core/Validator/OfferFactMapping/OfferFactMappingCreateValidator.php new file mode 100644 index 0000000..d07620c --- /dev/null +++ b/app/Core/Validator/OfferFactMapping/OfferFactMappingCreateValidator.php @@ -0,0 +1,47 @@ + 'required|numeric', + "category_id" => 'required|numeric', + "property_fact_parent_id" => 'required|numeric', + "property_fact_id" => 'required|numeric', + + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferImportantNotes/OfferImportantNotesCreateValidator.php b/app/Core/Validator/OfferImportantNotes/OfferImportantNotesCreateValidator.php new file mode 100644 index 0000000..536a046 --- /dev/null +++ b/app/Core/Validator/OfferImportantNotes/OfferImportantNotesCreateValidator.php @@ -0,0 +1,45 @@ + 'required|numeric', + "important_notes" => 'nullable|array', + "important_notes.*" => 'nullable|string' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferPaymentType/OfferPaymentTypeCreateValidator.php b/app/Core/Validator/OfferPaymentType/OfferPaymentTypeCreateValidator.php new file mode 100644 index 0000000..92a39a9 --- /dev/null +++ b/app/Core/Validator/OfferPaymentType/OfferPaymentTypeCreateValidator.php @@ -0,0 +1,42 @@ + 'required|min:3', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } +} diff --git a/app/Core/Validator/OfferPhotoMapping/OfferCoverPhotoAddValidator.php b/app/Core/Validator/OfferPhotoMapping/OfferCoverPhotoAddValidator.php new file mode 100644 index 0000000..8a9a6ea --- /dev/null +++ b/app/Core/Validator/OfferPhotoMapping/OfferCoverPhotoAddValidator.php @@ -0,0 +1,46 @@ + 'required|numeric', + "cover_photos" => 'array', + "cover_photos.*" => 'numeric', + + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferPhotoMapping/OfferPhotoMappingAddValidator.php b/app/Core/Validator/OfferPhotoMapping/OfferPhotoMappingAddValidator.php new file mode 100644 index 0000000..56becfd --- /dev/null +++ b/app/Core/Validator/OfferPhotoMapping/OfferPhotoMappingAddValidator.php @@ -0,0 +1,46 @@ + 'required|numeric', + "photo_mapping" => 'array', + "photo_mapping.*" => 'numeric', + + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferPhotoMapping/OfferPhotoMappingCreateValidator.php b/app/Core/Validator/OfferPhotoMapping/OfferPhotoMappingCreateValidator.php new file mode 100644 index 0000000..271f73d --- /dev/null +++ b/app/Core/Validator/OfferPhotoMapping/OfferPhotoMappingCreateValidator.php @@ -0,0 +1,45 @@ + 'required|numeric', + "property_photo_id" => 'required|numeric', + + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferPrice/OfferPriceCreateValidator.php b/app/Core/Validator/OfferPrice/OfferPriceCreateValidator.php new file mode 100644 index 0000000..2091e24 --- /dev/null +++ b/app/Core/Validator/OfferPrice/OfferPriceCreateValidator.php @@ -0,0 +1,51 @@ + 'required|numeric', + "date" => 'required|date', + "property_room_id" => 'required|numeric', + "property_accommodation_id" => 'required|numeric', + "number_of_rooms" => 'required|numeric', + "currency" => 'required', + "amount" => 'required|numeric', + "total_amount" => 'required|numeric', + + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferRoomMapping/OfferRoomMappingAddValidator.php b/app/Core/Validator/OfferRoomMapping/OfferRoomMappingAddValidator.php new file mode 100644 index 0000000..0ec5de5 --- /dev/null +++ b/app/Core/Validator/OfferRoomMapping/OfferRoomMappingAddValidator.php @@ -0,0 +1,49 @@ + 'required|numeric', + "room_mapping" => 'required|array', + "room_mapping.*" => 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = [ + 'room_mapping.required' => __('error-input-room_mapping-required') + ]; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/OfferRoomMapping/OfferRoomMappingCreateValidator.php b/app/Core/Validator/OfferRoomMapping/OfferRoomMappingCreateValidator.php new file mode 100644 index 0000000..0c2d8bc --- /dev/null +++ b/app/Core/Validator/OfferRoomMapping/OfferRoomMappingCreateValidator.php @@ -0,0 +1,46 @@ + 'required|numeric', + "property_room_id" => 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/Property/PropertyBrandAddValidator.php b/app/Core/Validator/Property/PropertyBrandAddValidator.php new file mode 100644 index 0000000..15a0b65 --- /dev/null +++ b/app/Core/Validator/Property/PropertyBrandAddValidator.php @@ -0,0 +1,51 @@ + 'required|numeric', + 'title' => 'nullable|json', + 'color_codes' => 'nullable|json', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/Property/PropertyBrandPhotoValidator.php b/app/Core/Validator/Property/PropertyBrandPhotoValidator.php new file mode 100644 index 0000000..fde4d74 --- /dev/null +++ b/app/Core/Validator/Property/PropertyBrandPhotoValidator.php @@ -0,0 +1,48 @@ + 'nullable|mimes:jpg,jpeg,png,webp,gif|max:10240', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } +} diff --git a/app/Core/Validator/Property/PropertyCreateValidator.php b/app/Core/Validator/Property/PropertyCreateValidator.php new file mode 100644 index 0000000..8bf38ed --- /dev/null +++ b/app/Core/Validator/Property/PropertyCreateValidator.php @@ -0,0 +1,50 @@ +propertyRepository = $propertyRepository; + } + + public function rules($params = null) + { + return + [ + 'name' => 'required|min:3' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/Property/PropertyLocationUpdateValidator.php b/app/Core/Validator/Property/PropertyLocationUpdateValidator.php new file mode 100644 index 0000000..15f2b56 --- /dev/null +++ b/app/Core/Validator/Property/PropertyLocationUpdateValidator.php @@ -0,0 +1,51 @@ + 'nullable|string|max:200', + 'latitude' => 'nullable', + 'longitude' => 'nullable', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/Property/PropertyPhotoValidator.php b/app/Core/Validator/Property/PropertyPhotoValidator.php new file mode 100644 index 0000000..c6d179e --- /dev/null +++ b/app/Core/Validator/Property/PropertyPhotoValidator.php @@ -0,0 +1,48 @@ + 'nullable|mimes:jpg,jpeg,png,webp|max:10240', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } +} diff --git a/app/Core/Validator/Property/PropertySearchValidator.php b/app/Core/Validator/Property/PropertySearchValidator.php new file mode 100644 index 0000000..f2ef484 --- /dev/null +++ b/app/Core/Validator/Property/PropertySearchValidator.php @@ -0,0 +1,50 @@ +PropertyRepository = $PropertyRepository; + } + + public function rules($params = null) + { + return + [ + 'name' => 'required|min:3' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/Property/PropertyUpdateValidator.php b/app/Core/Validator/Property/PropertyUpdateValidator.php new file mode 100644 index 0000000..f1dc7e1 --- /dev/null +++ b/app/Core/Validator/Property/PropertyUpdateValidator.php @@ -0,0 +1,73 @@ + 'required|min:3|max:255', + 'property_info.property_type_id' => 'required|numeric', + 'property_info.chain_id' => 'required|numeric', + 'property_info.official_name' => 'nullable|min:3|max:255', + 'property_info.country' => 'required|max:5', + 'property_info.tax_office' => 'nullable|min:3|max:255', + 'property_info.tax_number' => 'nullable|min:2|max:255', + 'property_info.checkin_time' => 'nullable|min:3|max:100', + 'property_info.checkout_time' => 'nullable|min:2|max:100', + 'additional_info' => 'array', + 'additional_info.*.year_construction' => 'nullable|string|max:100', + 'additional_info.*.year_renovation' => 'nullable|string|max:100', + + ]; + if (isset($params['additional_info']['locale_name_language']) || isset($params['additional_info']['locale_name'])) { + if ( !empty($params['additional_info']['locale_name_language']) || !empty($params['additional_info']['locale_name'])) { + $return['additional_info.locale_name_language'] = 'required|min:2|max:100'; + $return['additional_info.locale_name'] = 'required'; + } + } + + return $return; + } + + public function messages() + { + + // use example 'validation.input' => __('validation_type', ['input' => __('input form language key')]), + // validation types in baseValidator switch-case block + + $thisMessages = [ + 'property_info.name.required' => __('error-input-required', ['input' => __('input-property_name')]), + 'property_info.name.min' => __('error-input-min', ['input' => __('input-property_name')]), + + ]; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyAdditionalInfo/PropertyAdditionalInfoAddValidator.php b/app/Core/Validator/PropertyAdditionalInfo/PropertyAdditionalInfoAddValidator.php new file mode 100644 index 0000000..479ba53 --- /dev/null +++ b/app/Core/Validator/PropertyAdditionalInfo/PropertyAdditionalInfoAddValidator.php @@ -0,0 +1,50 @@ + 'required|array', + 'data.*.additional_info_key_id' => 'required|numeric', + 'data.*.value' => 'required', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyAddon/PropertyAddonValidator.php b/app/Core/Validator/PropertyAddon/PropertyAddonValidator.php new file mode 100644 index 0000000..0945bda --- /dev/null +++ b/app/Core/Validator/PropertyAddon/PropertyAddonValidator.php @@ -0,0 +1,47 @@ + 'required|numeric', + "channel_id" => 'required|numeric', + "property_addon_id" => 'required|numeric', + 'type' => 'required_without:status|in:ONT,PRP', + 'amount' => 'required_without:status|numeric', + 'status' => 'nullable|numeric' + ]; + } + + public function messages() + { + return parent::messages(); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyAwardsCertificate/PropertyAwardCertificateUploadValidator.php b/app/Core/Validator/PropertyAwardsCertificate/PropertyAwardCertificateUploadValidator.php new file mode 100644 index 0000000..38756de --- /dev/null +++ b/app/Core/Validator/PropertyAwardsCertificate/PropertyAwardCertificateUploadValidator.php @@ -0,0 +1,48 @@ + 'nullable|mimes:jpg,jpeg,png,webp,gif,pdf|max:10240', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } +} diff --git a/app/Core/Validator/PropertyAwardsCertificate/PropertyAwardsCertificateCreateValidator.php b/app/Core/Validator/PropertyAwardsCertificate/PropertyAwardsCertificateCreateValidator.php new file mode 100644 index 0000000..4ac7c63 --- /dev/null +++ b/app/Core/Validator/PropertyAwardsCertificate/PropertyAwardsCertificateCreateValidator.php @@ -0,0 +1,45 @@ + 'required|numeric', + "category_id" => 'required|numeric', + "name" => 'max:255', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyAwardsCertificate/PropertyAwardsCertificateUpdateValidator.php b/app/Core/Validator/PropertyAwardsCertificate/PropertyAwardsCertificateUpdateValidator.php new file mode 100644 index 0000000..779d6d4 --- /dev/null +++ b/app/Core/Validator/PropertyAwardsCertificate/PropertyAwardsCertificateUpdateValidator.php @@ -0,0 +1,46 @@ + 'required|numeric', + "property_id" => 'required|numeric', + "category_id" => 'required|numeric', + "name" => 'max:255', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyBookingTicket/PropertyTicketCreateValidator.php b/app/Core/Validator/PropertyBookingTicket/PropertyTicketCreateValidator.php new file mode 100644 index 0000000..d9e8152 --- /dev/null +++ b/app/Core/Validator/PropertyBookingTicket/PropertyTicketCreateValidator.php @@ -0,0 +1,47 @@ + 'nullable|numeric', + 'booking_code' => 'required|string|min:18|max:18', + 'message' => 'required|string|max:2000', + 'is_note' => 'nullable|boolean', + 'ip_address' => 'required|ip', + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages(), $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyBookingTicket/PropertyTicketGetValidator.php b/app/Core/Validator/PropertyBookingTicket/PropertyTicketGetValidator.php new file mode 100644 index 0000000..1d417eb --- /dev/null +++ b/app/Core/Validator/PropertyBookingTicket/PropertyTicketGetValidator.php @@ -0,0 +1,45 @@ + 'nullable|numeric', + 'booking_code' => 'required|string|min:18|max:18', + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages(), $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyCancellationPolicy/PropertyCancellationPolicyAddValidator.php b/app/Core/Validator/PropertyCancellationPolicy/PropertyCancellationPolicyAddValidator.php new file mode 100644 index 0000000..1b5c9a5 --- /dev/null +++ b/app/Core/Validator/PropertyCancellationPolicy/PropertyCancellationPolicyAddValidator.php @@ -0,0 +1,67 @@ + 'required|numeric', + 'name' => 'nullable', + 'before_arrival' => 'required|numeric', + 'is_nonrefundable' => 'required|boolean', + 'is_free_cancellation' => 'required|boolean', + 'type' => 'nullable|in:PER,FIX,NGT', + 'value' => 'nullable|numeric', + 'affects_price' => 'required|boolean', + 'affects_price_action_type' => 'nullable|in:INC,DEC', + 'affects_price_type' => 'nullable|in:PER,FIX', + 'affects_price_value' => 'nullable|numeric', + 'is_date_range' => 'required|boolean', + 'start_date' => 'required_without:is_date_range|date', + 'finish_date' => 'required_without:is_date_range|date|after:start_date', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyCancellationPolicy/PropertyCancellationPolicyUpdateValidator.php b/app/Core/Validator/PropertyCancellationPolicy/PropertyCancellationPolicyUpdateValidator.php new file mode 100644 index 0000000..c6a62d8 --- /dev/null +++ b/app/Core/Validator/PropertyCancellationPolicy/PropertyCancellationPolicyUpdateValidator.php @@ -0,0 +1,78 @@ + 'required|numeric', + 'cancellation_policy_id' => 'required|numeric', + 'name' => 'nullable', + 'before_arrival' => 'required|numeric', + 'is_nonrefundable' => 'required|boolean', + 'is_free_cancellation' => 'required|boolean', + 'type' => 'nullable|in:PER,FIX,NGT', + 'value' => 'nullable|numeric', + 'affects_price' => 'required|boolean', + 'affects_price_action_type' => 'nullable|in:INC,DEC', + 'affects_price_type' => 'nullable|in:PER,FIX', + 'affects_price_value' => 'nullable|numeric', + 'checkData' => 'accepted', + 'is_date_range' => 'required|boolean', + 'start_date' => 'required_without:is_date_range|date', + 'finish_date' => 'required_without:is_date_range|date|after:start_date', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkData($params){ + + $data = PropertyCancellationPolicy::where('id' , '=', $params['cancellation_policy_id']) + ->where('property_id' , '=', $params['property_id']) + ->first(); + return $data ? true : false ; + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkData"] = $this->checkData($params); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyCancellationPolicy/UpdateRoomRateChannelCancellationPolicyValidator.php b/app/Core/Validator/PropertyCancellationPolicy/UpdateRoomRateChannelCancellationPolicyValidator.php new file mode 100644 index 0000000..a79d37a --- /dev/null +++ b/app/Core/Validator/PropertyCancellationPolicy/UpdateRoomRateChannelCancellationPolicyValidator.php @@ -0,0 +1,57 @@ + 'required|numeric', + 'channel_id' => 'required|numeric', + 'room_rate_channel_mapping_id' => 'required|numeric', + 'cancellation_policy_id' => 'required|numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannel/PropertyChannelAddValidator.php b/app/Core/Validator/PropertyChannel/PropertyChannelAddValidator.php new file mode 100644 index 0000000..6e61b90 --- /dev/null +++ b/app/Core/Validator/PropertyChannel/PropertyChannelAddValidator.php @@ -0,0 +1,51 @@ + 'required', + 'channel_category_id' => 'required|numeric', + 'currency_code' => 'required', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannel/PropertyChannelDeleteValidator.php b/app/Core/Validator/PropertyChannel/PropertyChannelDeleteValidator.php new file mode 100644 index 0000000..2de819e --- /dev/null +++ b/app/Core/Validator/PropertyChannel/PropertyChannelDeleteValidator.php @@ -0,0 +1,52 @@ + 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } +} diff --git a/app/Core/Validator/PropertyChannel/PropertyChannelUpdateValidator.php b/app/Core/Validator/PropertyChannel/PropertyChannelUpdateValidator.php new file mode 100644 index 0000000..c20b2d2 --- /dev/null +++ b/app/Core/Validator/PropertyChannel/PropertyChannelUpdateValidator.php @@ -0,0 +1,52 @@ + 'required|numeric', + 'name' => 'required', + 'channel_category_id' => 'required|numeric', + 'currency_code' => 'required', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannelBookingPaymentSetup/PropertyChannelBookingPaymentSetupAddValidator.php b/app/Core/Validator/PropertyChannelBookingPaymentSetup/PropertyChannelBookingPaymentSetupAddValidator.php new file mode 100644 index 0000000..a54744c --- /dev/null +++ b/app/Core/Validator/PropertyChannelBookingPaymentSetup/PropertyChannelBookingPaymentSetupAddValidator.php @@ -0,0 +1,57 @@ + 'required|numeric', + "channel_id" => 'required|numeric', + "payments" => 'nullable|array', + "payments.*.payment_type_id" => 'required|numeric', + "payments.*.is_affected_price" => 'required|boolean', + "payments.*.action_type" => 'nullable|in:INC,DEC', + "payments.*.value_type" => 'nullable|in:FIX,PER', + "payments.*.value" => 'nullable|numeric', + "payments.*.is_selected" => 'required|boolean', + "payments.*.cancellation_policy" => 'present|array|nullable', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannelCoupon/PropertyChannelCouponValidator.php b/app/Core/Validator/PropertyChannelCoupon/PropertyChannelCouponValidator.php new file mode 100644 index 0000000..bbe0307 --- /dev/null +++ b/app/Core/Validator/PropertyChannelCoupon/PropertyChannelCouponValidator.php @@ -0,0 +1,49 @@ + 'required|numeric', + "channel_id" => 'required|numeric', + "code" => 'required', + "start_date" => 'required|date', + "end_date" => 'required|date', + 'type' => 'required|in:PER,FIX', + 'value' => 'required|numeric', + 'status' => 'nullable|boolean' + ]; + } + + public function messages() + { + return parent::messages(); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingAddValidator.php b/app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingAddValidator.php new file mode 100644 index 0000000..78f33ea --- /dev/null +++ b/app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingAddValidator.php @@ -0,0 +1,53 @@ + 'required|numeric', + 'channels' => 'required|array', + 'channels.*' => 'required|numeric' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingRemoveValidator.php b/app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingRemoveValidator.php new file mode 100644 index 0000000..702e352 --- /dev/null +++ b/app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingRemoveValidator.php @@ -0,0 +1,51 @@ + 'required|numeric', + 'channels' => 'required|array', + 'channels.*' => 'numeric' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingUpdateValidator.php b/app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingUpdateValidator.php new file mode 100644 index 0000000..c6f3c5f --- /dev/null +++ b/app/Core/Validator/PropertyChannelMapping/PropertyChannelMappingUpdateValidator.php @@ -0,0 +1,53 @@ + 'required|numeric', + 'property_channel' => 'required|array', + 'property_channel.*.id' => 'required|numeric', + 'property_channel.*.is_selected' => 'required|boolean', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannelMapping/PropertyChannelSetupValidator.php b/app/Core/Validator/PropertyChannelMapping/PropertyChannelSetupValidator.php new file mode 100644 index 0000000..282489f --- /dev/null +++ b/app/Core/Validator/PropertyChannelMapping/PropertyChannelSetupValidator.php @@ -0,0 +1,96 @@ + 'required|numeric', + 'channel_id' => 'required|numeric', + 'checkPayment' => 'accepted', + 'booking' => 'array', + 'payment' => 'array', + 'availability' => 'array', + 'availability.availability_type_id' => 'numeric', + + ] ; + + + if(isset($params['booking']['value'])){ + $optionalRules = [ + 'booking.booking_type_id' => 'required|numeric', + 'booking.action_type' => 'required|in:INC,DEC', + 'booking.value_type' => 'required|in:FIX,PER', + 'booking.value' => 'numeric', + ] ; + $rules = array_merge($rules, $optionalRules) ; + } + + return $rules ; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function paymentValueControl($params){ + try { + $errorStatus = false ; + + foreach ($params['payment'] as $payment) { + if( + ($payment['value']) && + ( + $payment['action_type'] == null || $payment['value_type'] == null || + !in_array($payment['action_type'], ['INC', 'DEC']) || !in_array($payment['value_type'], ['FIX', 'PER']) + + ) + ){ + $errorStatus = true ; + break; + } + } + return $errorStatus ? false : true; + } catch (Exception $e) { + return false; + } + + + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkPayment"] = $this->paymentValueControl($params); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupAddValidator.php b/app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupAddValidator.php new file mode 100644 index 0000000..e88c7c2 --- /dev/null +++ b/app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupAddValidator.php @@ -0,0 +1,56 @@ + 'required', + 'channel_id' => 'required', + 'currency_code' => 'nullable|string|required_without:connected_channel_id', + 'booking_type_id' => 'nullable|numeric|required_without:connected_channel_id', + 'availability_type_id' => 'nullable|numeric|required_without:connected_channel_id', + 'room_pricing_type_id' => 'nullable|numeric|required_without:connected_channel_id', + 'connected_channel_id' => 'nullable|numeric|required_without:currency_code', + 'connected_channel_action' => 'nullable|string|required_with:connected_channel_id' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupUpdateValidator.php b/app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupUpdateValidator.php new file mode 100644 index 0000000..fe4d068 --- /dev/null +++ b/app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupUpdateValidator.php @@ -0,0 +1,56 @@ + 'required', + 'channel_id' => 'required', + 'currency_code' => 'nullable|string|required_without:connected_channel_id', + 'booking_type_id' => 'nullable|numeric|required_without:connected_channel_id', + 'availability_type_id' => 'nullable|numeric|required_without:connected_channel_id', + 'room_pricing_type_id' => 'nullable|numeric|required_without:connected_channel_id', + 'connected_channel_id' => 'nullable|numeric|required_without:currency_code', + 'connected_channel_action' => 'nullable|string|required_with:connected_channel_id' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupWithMissingParametersAddValidator.php b/app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupWithMissingParametersAddValidator.php new file mode 100644 index 0000000..ff1f30f --- /dev/null +++ b/app/Core/Validator/PropertyChannelSetup/PropertyChannelSetupWithMissingParametersAddValidator.php @@ -0,0 +1,50 @@ + 'required', + 'channel_id' => 'required' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyConfig/PropertyConfigCreateValidator.php b/app/Core/Validator/PropertyConfig/PropertyConfigCreateValidator.php new file mode 100644 index 0000000..2ab3ce3 --- /dev/null +++ b/app/Core/Validator/PropertyConfig/PropertyConfigCreateValidator.php @@ -0,0 +1,49 @@ + 'required|integer', + 'config_key' => 'required', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyContact/PropertyContactCreateValidator.php b/app/Core/Validator/PropertyContact/PropertyContactCreateValidator.php new file mode 100644 index 0000000..5e2dc4b --- /dev/null +++ b/app/Core/Validator/PropertyContact/PropertyContactCreateValidator.php @@ -0,0 +1,79 @@ +propertyContactRepository = $propertyContactRepository; + } + + public function rules($params = null) + { + return + [ + // 'property_id' => 'required|integer|unique:property_contact,property_id', // todo: It should be added uniqueness + "checkMainPhoneAndMobilePhone" => "accepted", + 'phone' => 'nullable|max:50', + 'mobile' => 'nullable|max:50', + 'mobile2' => 'nullable|max:50', + 'fax' => 'nullable|max:50', + 'email' => 'required|email|max:100', + 'web' => 'nullable|max:100', + 'zip_code' => 'nullable|max:20', + 'address' => 'nullable|max:200', + 'latitude' => 'nullable', + 'longitude' => 'nullable', + 'status' => 'required|integer|max:4', + 'created_by' => 'required|integer', + 'updated_by' => 'required|integer', + 'social_media_addresses' => 'nullable|json|max:512', + ]; + + + } + + public function messages() + { + $thisMessages = [ + 'checkMainPhoneAndMobilePhone.accepted' => 'phone or mobile phone required' // TODO : language key eklenicek + ]; + return array_merge(parent::messages() , $thisMessages); + } + + + public function checkMainPhoneAndMobilePhone($params) + { + if (empty($params['phone']) && empty($params['mobile'])) { + + return false; + } + return true; + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkMainPhoneAndMobilePhone"] = $this->checkMainPhoneAndMobilePhone( $params); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyContact/PropertyContactUpdateValidator.php b/app/Core/Validator/PropertyContact/PropertyContactUpdateValidator.php new file mode 100644 index 0000000..6120543 --- /dev/null +++ b/app/Core/Validator/PropertyContact/PropertyContactUpdateValidator.php @@ -0,0 +1,73 @@ +propertyContactRepository = $propertyContactRepository; + } + + public function rules($params = null) + { + return + [ + "checkMainPhoneAndMobilePhone" => "accepted", + 'email' => 'required|email|max:100', + 'phone' => 'nullable|max:50', + 'mobile' => 'nullable|max:50', + 'mobile2' => 'nullable|max:50', + 'fax' => 'nullable|max:50', + 'web' => 'nullable|max:100', + 'zip_code' => 'nullable|max:20', + 'address' => 'nullable|max:200', + 'latitude' => 'nullable', + 'longitude' => 'nullable', + 'social_media_addresses' => 'nullable|json|max:512', + ]; + + } + + public function messages() + { + $thisMessages = [ + 'checkMainPhoneAndMobilePhone.accepted' => 'phone or mobile phone required' // TODO : language key eklenicek + ]; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkMainPhoneAndMobilePhone($params) + { + if (empty($params['phone']) && empty($params['mobile'])) { + + return false; + } + return true; + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkMainPhoneAndMobilePhone"] = $this->checkMainPhoneAndMobilePhone($params); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyContent/PropertyContentCreateValidator.php b/app/Core/Validator/PropertyContent/PropertyContentCreateValidator.php new file mode 100644 index 0000000..34816a3 --- /dev/null +++ b/app/Core/Validator/PropertyContent/PropertyContentCreateValidator.php @@ -0,0 +1,54 @@ +propertyContentRepository = $propertyContentRepository; + } + + public function rules($params = null) + { + return + [ + 'property_id' => 'required', + 'content_category_id' => 'required', + 'locale' => 'required|string|max:2', + 'status' => 'required|integer|max:4', + 'created_by' => 'required|integer', + 'updated_by' => 'required|integer', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyExecutive/PropertyExecutiveCreateValidator.php b/app/Core/Validator/PropertyExecutive/PropertyExecutiveCreateValidator.php new file mode 100644 index 0000000..9fa0e31 --- /dev/null +++ b/app/Core/Validator/PropertyExecutive/PropertyExecutiveCreateValidator.php @@ -0,0 +1,57 @@ +propertyExecutiveRepository = $propertyExecutiveRepository; + } + + public function rules($params = null) + { + return + [ + 'property_id' => 'required|numeric', + 'executive_type_id' => 'required|numeric', + 'name_surname' => 'required|max:50', + 'email' => 'nullable|email|max:100', + 'extension' => 'nullable|max:10', + 'phone' => 'nullable|max:50', + 'mobile' => 'nullable|max:50', + 'fax' => 'nullable|max:50', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyExecutive/PropertyExecutiveUpdateValidator.php b/app/Core/Validator/PropertyExecutive/PropertyExecutiveUpdateValidator.php new file mode 100644 index 0000000..ac053f6 --- /dev/null +++ b/app/Core/Validator/PropertyExecutive/PropertyExecutiveUpdateValidator.php @@ -0,0 +1,55 @@ +propertyExecutiveRepository = $propertyExecutiveRepository; + } + + public function rules($params = null) + { + return + [ + 'executive_type_id' => 'required|numeric', + 'name_surname' => 'required|max:50', + 'email' => 'nullable|email|max:100', + 'extension' => 'nullable|max:10', + 'phone' => 'nullable|max:50', + 'mobile' => 'nullable|max:50', + 'fax' => 'nullable|max:50', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyFact/PropertyFactCheckboxValidator.php b/app/Core/Validator/PropertyFact/PropertyFactCheckboxValidator.php new file mode 100644 index 0000000..86fc15d --- /dev/null +++ b/app/Core/Validator/PropertyFact/PropertyFactCheckboxValidator.php @@ -0,0 +1,51 @@ + 'required|numeric', + 'property_fact' => 'required|array', + 'property_fact.*.id' => 'required|numeric', + 'property_fact.*.is_selected' => 'required|boolean' + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyFact/PropertyFactSearchValidator.php b/app/Core/Validator/PropertyFact/PropertyFactSearchValidator.php new file mode 100644 index 0000000..551a374 --- /dev/null +++ b/app/Core/Validator/PropertyFact/PropertyFactSearchValidator.php @@ -0,0 +1,49 @@ + 'nullable|min:3', + + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyNonrefundable/PropertyNonrefundableAddValidator.php b/app/Core/Validator/PropertyNonrefundable/PropertyNonrefundableAddValidator.php new file mode 100644 index 0000000..c25e82b --- /dev/null +++ b/app/Core/Validator/PropertyNonrefundable/PropertyNonrefundableAddValidator.php @@ -0,0 +1,56 @@ + 'required|numeric', + "channels" => 'required|array', + "channels.*" => 'required|integer', + "room_rate_mapping" => 'required|array', + "room_rate_mapping.*.room_rate_mapping_id" => 'required|numeric', + "room_rate_mapping.*.action_type" => 'required|in:DEC,INC', + "room_rate_mapping.*.value_type" => 'required|in:PER,FIX', + "room_rate_mapping.*.value" => 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPayment/PropertyPaymentTransactionCreateValidator.php b/app/Core/Validator/PropertyPayment/PropertyPaymentTransactionCreateValidator.php new file mode 100644 index 0000000..fcc9044 --- /dev/null +++ b/app/Core/Validator/PropertyPayment/PropertyPaymentTransactionCreateValidator.php @@ -0,0 +1,57 @@ + 'required|numeric', + "title" => 'required', + "description" => 'required', + "email" => 'nullable|email', + "currency" => "required", + "base_amount" => "required|numeric", + "commission" => "required|numeric", + "payment_method" => "required|in:online,offline", + "payment_type_mapping_id" => "nullable|numeric", + + ]; + } + + + + + public function messages() + { + $thisMessages = + [ + ]; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPayment/PropertyPaymentTransactionUpdateValidator.php b/app/Core/Validator/PropertyPayment/PropertyPaymentTransactionUpdateValidator.php new file mode 100644 index 0000000..573c111 --- /dev/null +++ b/app/Core/Validator/PropertyPayment/PropertyPaymentTransactionUpdateValidator.php @@ -0,0 +1,58 @@ + 'required|numeric', + "order_id" => 'required', + "title" => 'required', + "description" => 'required', + "email" => 'nullable|email', + "currency" => "required", + "base_amount" => "required|numeric", + "commission" => "required|numeric", + "payment_method" => "required|in:online,offline", + "payment_type_mapping_id" => "nullable|numeric", + + ]; + } + + + + + public function messages() + { + $thisMessages = + [ + ]; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentInstallmentsUpdateValidator.php b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentInstallmentsUpdateValidator.php new file mode 100644 index 0000000..ab15a80 --- /dev/null +++ b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentInstallmentsUpdateValidator.php @@ -0,0 +1,56 @@ + 'required|numeric', + 'property_payment_mapping_id' => 'required|numeric', + 'has_installments' => 'required|boolean', + 'installments' => 'array', + 'installments.*.installment' => 'required|numeric', + 'installments.*.commission' => 'required|numeric', + ]; + + } + + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingCreateValidator.php b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingCreateValidator.php new file mode 100644 index 0000000..31d9c6a --- /dev/null +++ b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingCreateValidator.php @@ -0,0 +1,102 @@ + 'required|numeric', + 'payment_type_id' => 'required|numeric', + 'currency' => 'required', + 'is_default' => 'required|boolean', + 'fields' => 'required|array', + 'check_fields' => 'accepted', + 'check_currencies' => 'accepted', + ]; + + } + + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkFields($params) + { + try { + $fields = PaymentType::where('status', 1) + ->where('id', $params['payment_type_id']) + ->first(); + if(!$fields){ + throw new Exception ('fields error'); + } + $fields = json_decode($fields['params']); + $apiKeys = $params['fields']; + $requiredKeys = collect($fields)->keys()->all() ; + foreach ($requiredKeys as $key) { + if(!isset($apiKeys[$key])){ + throw new Exception('field error') ; + } + } + return true; + } catch (Exception $e) { + return false; + } + } + + public function checkCurrencies($params) + { + try { + $currencies = Currency::get('code')->toArray() ; + if(!$currencies){ + throw new Exception ('currency error'); + } + + $apiCurrency = $params['currency']; + $requiredCurrencies = collect($currencies)->keyBy('code')->keys()->all() ; + + $arraySearch = in_array($apiCurrency, $requiredCurrencies) ; + if(!$arraySearch){ + throw new Exception ('valid currency error'); + } + return true; + } catch (Exception $e) { + return false; + } + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["check_fields"] = $this->checkFields($params); + $params["check_currencies"] = $this->checkCurrencies($params); + + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingSetDefaultValidator.php b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingSetDefaultValidator.php new file mode 100644 index 0000000..e068e97 --- /dev/null +++ b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingSetDefaultValidator.php @@ -0,0 +1,53 @@ + 'required|numeric', + 'property_payment_mapping_id' => 'required|numeric', + + ]; + + } + + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingStatusUpdateValidator.php b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingStatusUpdateValidator.php new file mode 100644 index 0000000..00d839e --- /dev/null +++ b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingStatusUpdateValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + 'property_payment_mapping_id' => 'required|numeric', + 'set_status' => 'required|numeric', + + ]; + + } + + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingUpdateValidator.php b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingUpdateValidator.php new file mode 100644 index 0000000..e8f685f --- /dev/null +++ b/app/Core/Validator/PropertyPaymentMapping/PropertyPaymentMappingUpdateValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + 'property_payment_mapping_id' => 'required|numeric', + 'is_default' => 'required|boolean', + 'fields' => 'required|array', + ]; + + } + + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyAdultAddValidator.php b/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyAdultAddValidator.php new file mode 100644 index 0000000..8a5cfe1 --- /dev/null +++ b/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyAdultAddValidator.php @@ -0,0 +1,76 @@ + 'nullable', + 'adult_action_type' => 'required|in:DEC,INC', + 'adult' => 'required|numeric', + 'action_type' => 'required|in:DEC,INC', + 'type' => 'required|in:PER,FIX', + 'value' => 'required|numeric', + // "checkPricingName" => "accepted" + ]; + + + } + + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkPricingName($params) + { + try { + $isExistPricingName = PropertyPricingPolicyAdult::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->first(); + + return $isExistPricingName ? false : true; + } catch (Exception $e) { + return false; + } + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + // $params["checkPricingName"] = $this->checkPricingName(['property_id' => $params["property_id"], 'name' => $params["name"]]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyAdultUpdateValidator.php b/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyAdultUpdateValidator.php new file mode 100644 index 0000000..8d663c3 --- /dev/null +++ b/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyAdultUpdateValidator.php @@ -0,0 +1,86 @@ + 'required|numeric', + 'name' => 'nullable', + 'adult_action_type' => 'required|in:DEC,INC', + 'adult' => 'required|numeric', + 'action_type' => 'required|in:DEC,INC', + 'type' => 'required|in:PER,FIX', + 'value' => 'required|numeric', + 'checkData' => 'accepted', + // "checkPricingName" => "accepted" + ]; + + + } + + + public function checkData($params){ + + $data = PropertyPricingPolicyAdult::where('id' , '=', $params['pricing_policy_id']) + ->where('property_id' , '=', $params['property_id']) + ->first(); + return $data ? true : false ; + } + + public function checkPricingName($params) + { + try { + $isExistPricingName = PropertyPricingPolicyAdult::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->first(); + + return $isExistPricingName && $isExistPricingName['id'] !== $params['pricing_policy_id'] ? false : true; + + } catch (Exception $e) { + return false; + } + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkData"] = $this->checkData($params); + /* $params["checkPricingName"] = $this->checkPricingName([ + 'property_id' => $params["property_id"], + 'name' => $params["name"], + 'pricing_policy_id' => $params['pricing_policy_id'] + ]);*/ + + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyChildAddValidator.php b/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyChildAddValidator.php new file mode 100644 index 0000000..65fecb4 --- /dev/null +++ b/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyChildAddValidator.php @@ -0,0 +1,73 @@ + 'nullable', + 'adult' => 'required|numeric', + 'child_order' => 'required|numeric', + 'child_age_start' => 'required|numeric', + 'child_age_end' => 'required|numeric', + 'type' => 'required|in:PER,FIX', + 'value' => 'required|numeric', + //"checkPricingName" => "accepted" + ]; + + + } + + public function checkPricingName($params) + { + try { + $isExistPricingName = PropertyPricingPolicyChild::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->first(); + + return $isExistPricingName ? false : true; + } catch (Exception $e) { + return false; + } + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + // $params["checkPricingName"] = $this->checkPricingName(['property_id' => $params["property_id"], 'name' => $params["name"]]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyChildUpdateValidator.php b/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyChildUpdateValidator.php new file mode 100644 index 0000000..633a39f --- /dev/null +++ b/app/Core/Validator/PropertyPersonPricingPolicy/PropertyPricingPolicyChildUpdateValidator.php @@ -0,0 +1,88 @@ + 'nullable', + 'adult' => 'required|numeric', + 'child_order' => 'required|numeric', + 'child_age_start' => 'required|numeric', + 'child_age_end' => 'required|numeric', + 'type' => 'required|in:PER,FIX', + 'value' => 'required|numeric', + 'checkData' => 'accepted', + // "checkPricingName" => "accepted" + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkData($params){ + + $data = PropertyPricingPolicyChild::where('id' , '=', $params['pricing_policy_id']) + ->where('property_id' , '=', $params['property_id']) + ->first(); + return $data ? true : false ; + } + + public function checkPricingName($params) + { + try { + $isExistPricingName = PropertyPricingPolicyChild::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->first(); + + return $isExistPricingName && $isExistPricingName['id'] !== $params['pricing_policy_id'] ? false : true; + + } catch (Exception $e) { + return false; + } + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkData"] = $this->checkData($params); + /* $params["checkPricingName"] = $this->checkPricingName([ + 'property_id' => $params["property_id"], + 'name' => $params["name"], + 'pricing_policy_id' => $params['pricing_policy_id'] + ]);*/ + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPersonPricingPolicy/UpdateRoomRateChannelPersonPricingAdultPolicyValidator.php b/app/Core/Validator/PropertyPersonPricingPolicy/UpdateRoomRateChannelPersonPricingAdultPolicyValidator.php new file mode 100644 index 0000000..bdd5268 --- /dev/null +++ b/app/Core/Validator/PropertyPersonPricingPolicy/UpdateRoomRateChannelPersonPricingAdultPolicyValidator.php @@ -0,0 +1,58 @@ + 'required|numeric', + 'channel_id' => 'required|numeric', + 'room_rate_channel_mapping_id' => 'required|numeric', + 'pricing_policy_adult_id' => 'required|numeric', + 'is_selected' => 'required|boolean', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPersonPricingPolicy/UpdateRoomRateChannelPersonPricingChildPolicyValidator.php b/app/Core/Validator/PropertyPersonPricingPolicy/UpdateRoomRateChannelPersonPricingChildPolicyValidator.php new file mode 100644 index 0000000..e564dfa --- /dev/null +++ b/app/Core/Validator/PropertyPersonPricingPolicy/UpdateRoomRateChannelPersonPricingChildPolicyValidator.php @@ -0,0 +1,58 @@ + 'required|numeric', + 'channel_id' => 'required|numeric', + 'room_rate_channel_mapping_id' => 'required|numeric', + 'pricing_policy_child_id' => 'required|numeric', + 'is_selected' => 'required|boolean', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPlace/PropertyPlaceCreateValidator.php b/app/Core/Validator/PropertyPlace/PropertyPlaceCreateValidator.php new file mode 100644 index 0000000..c886f94 --- /dev/null +++ b/app/Core/Validator/PropertyPlace/PropertyPlaceCreateValidator.php @@ -0,0 +1,69 @@ + 'required|numeric', + "place_category_id" => 'required|numeric', + "place_working_hour_id" => 'required|numeric', + "name" => 'required|min:1|max:50', + "reservation_requirement" => "required|boolean", + "include" => "nullable|numeric", + "hasName" => "accepted", + + ]; + } + + + + + public function messages() + { + $thisMessages = + [ + "hasName.accepted" => "This name added before", + ]; + return array_merge(parent::messages() , $thisMessages); + } + + public function hasName($params) + { + try { + $domain = PropertyPlace::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->first(); + return $domain ? false : true; + } catch (Exception $e) { + return false; + } + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["hasName"] = $this->hasName($params); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPlace/PropertyPlaceFactMappingUpdateValidator.php b/app/Core/Validator/PropertyPlace/PropertyPlaceFactMappingUpdateValidator.php new file mode 100644 index 0000000..4da4e07 --- /dev/null +++ b/app/Core/Validator/PropertyPlace/PropertyPlaceFactMappingUpdateValidator.php @@ -0,0 +1,47 @@ + 'required|numeric', + "place_id" => 'required|numeric', + "place_fact_title_fact_mapping_id" => 'required|numeric', + "is_selected" => "required|boolean", + + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPlace/PropertyPlacePhotoMappingAddValidator.php b/app/Core/Validator/PropertyPlace/PropertyPlacePhotoMappingAddValidator.php new file mode 100644 index 0000000..0fdad20 --- /dev/null +++ b/app/Core/Validator/PropertyPlace/PropertyPlacePhotoMappingAddValidator.php @@ -0,0 +1,88 @@ + 'required|numeric', + 'place_id' => 'required|numeric', + "checkPropertyPlace" => "accepted", + "checkPropertyPhoto" => "accepted", + 'property_place_photo' => 'required|array', + 'property_place_photo.*.id' => 'required|numeric', + 'property_place_photo.*.is_selected' => 'required|boolean', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkPropertyPlace($params) + { + try { + $placeData = PropertyPlace::where('property_id', $params['property_id']) + ->where('id', $params['place_id']) + ->first(); + + return $placeData ? true : false; + } catch (Exception $e) { + return false; + } + } + + public function checkPropertyPhoto($params) + { + try { + + $photos = collect($params['property_place_photo'])->keyBy('id')->keys()->toArray(); + $arrayCount = collect($params['property_place_photo'])->count(); + $photoData = PropertyPhoto::where('property_id', $params['property_id']) + ->whereIn('id', $photos) + ->count(); + return $arrayCount == $photoData ? true : false; + } catch (Exception $e) { + return false; + } + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkPropertyPlace"] = $this->checkPropertyPlace(['property_id' => $params["property_id"], 'place_id' => $params["place_id"] ]); + $params["checkPropertyPhoto"] = $this->checkPropertyPhoto(['property_id' => $params["property_id"], 'property_place_photo' => $params["property_place_photo"] ]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPlace/PropertyPlaceUpdateValidator.php b/app/Core/Validator/PropertyPlace/PropertyPlaceUpdateValidator.php new file mode 100644 index 0000000..64ccbe4 --- /dev/null +++ b/app/Core/Validator/PropertyPlace/PropertyPlaceUpdateValidator.php @@ -0,0 +1,68 @@ + 'required|numeric', + "place_working_hour_id" => 'required|numeric', + "name" => 'required|min:1|max:50', + "reservation_requirement" => "required|boolean", + "include" => "nullable|numeric", + "hasName" => "accepted", + + ]; + } + + public function messages() + { + $thisMessages = + [ + "hasName.accepted" => "This name added before", + ]; + return array_merge(parent::messages() , $thisMessages); + } + + + public function hasName($params) + { + try { + $place = PropertyPlace::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->where('id', "!=", $params['property_place_id']) + ->first(); + return $place ? false : true; + } catch (Exception $e) { + return false; + } + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["hasName"] = $this->hasName($params); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPromotion/PropertyPromotionAddValidator.php b/app/Core/Validator/PropertyPromotion/PropertyPromotionAddValidator.php new file mode 100644 index 0000000..80f18c2 --- /dev/null +++ b/app/Core/Validator/PropertyPromotion/PropertyPromotionAddValidator.php @@ -0,0 +1,84 @@ + 'required|numeric', + 'promotion_type_id' => 'required|numeric', + 'amount' => 'required|numeric|max:100', + 'days' => 'required|array', + + ]; + + $promotionType = PromotionType::where('id' , '=', $params['promotion_type_id']) + ->first(); + + $returnAdd = []; + if($promotionType['type_code'] == 'PRD'){ + $returnAdd = [ + 'start_date' => 'required|date_format:Y-m-d', + 'end_date' => 'required|date_format:Y-m-d', + 'reservation_start_date' => 'required|date_format:Y-m-d', + 'reservation_end_date' => 'required|date_format:Y-m-d', + ]; + }elseif($promotionType['type_code'] == 'BFD'){ + + $returnAdd = [ + 'day_before' => 'required|numeric', + ]; + + } + if($params['min_stay'] != null){ + $returnAdd['min_stay'] = 'numeric'; + } + + + return array_merge($return, $returnAdd) ; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages(), $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPromotion/PropertyPromotionUpdateValidator.php b/app/Core/Validator/PropertyPromotion/PropertyPromotionUpdateValidator.php new file mode 100644 index 0000000..55b9b2d --- /dev/null +++ b/app/Core/Validator/PropertyPromotion/PropertyPromotionUpdateValidator.php @@ -0,0 +1,83 @@ + 'required|numeric', + 'promotion_type_id' => 'required|numeric', + 'amount' => 'required|numeric|max:100', + 'days' => 'required|array', + + ]; + + $promotionType = PromotionType::where('id' , '=', $params['promotion_type_id']) + ->first(); + + $returnAdd = []; + if($promotionType['type_code'] == 'PRD'){ + $returnAdd = [ + 'start_date' => 'required|date_format:Y-m-d', + 'end_date' => 'required|date_format:Y-m-d', + 'reservation_start_date' => 'required|date_format:Y-m-d', + 'reservation_end_date' => 'required|date_format:Y-m-d', + ]; + }elseif($promotionType['type_code'] == 'BFD'){ + $returnAdd = [ + 'day_before' => 'required|numeric', + ]; + + } + if($params['min_stay'] != null){ + $returnAdd['min_stay'] = 'numeric'; + } + + + return array_merge($return, $returnAdd) ; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages(), $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyPromotion/UpdateRoomRateChannelPromotionValidator.php b/app/Core/Validator/PropertyPromotion/UpdateRoomRateChannelPromotionValidator.php new file mode 100644 index 0000000..3c38c47 --- /dev/null +++ b/app/Core/Validator/PropertyPromotion/UpdateRoomRateChannelPromotionValidator.php @@ -0,0 +1,56 @@ + 'required|numeric', + 'room_rate_channel_mapping_id' => 'required|numeric', + 'property_promotion_id' => 'required|numeric', + 'is_selected' => 'required|boolean', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyQuickPricing/PropertyQuickPricingCreateValidator.php b/app/Core/Validator/PropertyQuickPricing/PropertyQuickPricingCreateValidator.php new file mode 100644 index 0000000..aed63e0 --- /dev/null +++ b/app/Core/Validator/PropertyQuickPricing/PropertyQuickPricingCreateValidator.php @@ -0,0 +1,48 @@ + 'required|numeric', + "channel_id" => 'required|numeric', + "room_rate_channel_mapping_id" => 'required|numeric', + 'action_type' => 'required|in:INC,DEC', + 'price_type' => 'required|in:PER,FIX', + 'price_value' => 'required|numeric', + + ]; + } + + public function messages() + { + return parent::messages(); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoom/PropertyRoomAddValidator.php b/app/Core/Validator/PropertyRoom/PropertyRoomAddValidator.php new file mode 100644 index 0000000..1e9831d --- /dev/null +++ b/app/Core/Validator/PropertyRoom/PropertyRoomAddValidator.php @@ -0,0 +1,69 @@ + 'required|numeric', + 'name' => 'required', + "checkName" => "accepted", + 'room_type_id' => 'required|numeric', + 'room_type_count' => 'required|numeric', + 'max_occupancy' => 'required|numeric', + 'max_adult' => 'required|numeric', + 'max_child' => 'required|numeric', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkName($params) + { + try { + $roomRate = PropertyRoom::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->first(); + return $roomRate ? false : true; + } catch (Exception $e) { + return false; + } + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkName"] = $this->checkName(['property_id' => $params["property_id"], 'name' => $params["name"] ]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoom/PropertyRoomAndBedAddValidator.php b/app/Core/Validator/PropertyRoom/PropertyRoomAndBedAddValidator.php new file mode 100644 index 0000000..8e0c353 --- /dev/null +++ b/app/Core/Validator/PropertyRoom/PropertyRoomAndBedAddValidator.php @@ -0,0 +1,81 @@ + 'required|numeric', + 'name' => 'required|max:200', + "checkName" => "accepted", + 'room_type_id' => 'required|numeric', + 'room_type_count' => 'required|numeric', + 'max_occupancy' => 'required|numeric|min:1', + 'max_adult' => 'required|numeric', + 'max_child' => 'required|numeric', + 'room_bed_group' => 'nullable|array', + 'room_bed_group.*' => 'nullable|array', + 'room_bed_group.*.*.bed_type_id' => 'nullable|numeric', + 'exclude_occupancy' => 'nullable|max:500', + 'room_size_type' => 'required|max:30', + 'room_size' => 'required|max:11', + 'is_connected_room' => 'nullable|boolean', + 'is_connected_room_price' => 'nullable|boolean', + 'is_connected_room_availability' => 'nullable|boolean', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function checkName($params) + { + try { + $roomRate = PropertyRoom::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->where('room_type_id', $params['room_type_id']) + ->where('status', 1) + ->first(); + return $roomRate ? false : true; + } catch (Exception $e) { + return false; + } + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkName"] = $this->checkName(['property_id' => $params["property_id"], 'name' => $params["name"], 'room_type_id' => $params['room_type_id'] ]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoom/PropertyRoomAndBedUpdateValidator.php b/app/Core/Validator/PropertyRoom/PropertyRoomAndBedUpdateValidator.php new file mode 100644 index 0000000..5936bc3 --- /dev/null +++ b/app/Core/Validator/PropertyRoom/PropertyRoomAndBedUpdateValidator.php @@ -0,0 +1,91 @@ + 'required|numeric', + 'room_id' => 'required|numeric', + 'name' => 'required|max:200', + "checkName" => "accepted", + 'room_type_id' => 'required|numeric', + 'room_type_count' => 'required|numeric', + 'max_occupancy' => 'required|numeric|min:1', + 'max_adult' => 'required|numeric', + 'max_child' => 'required|numeric', + 'room_bed_group' => 'nullable|array', + 'room_bed_group.*' => 'nullable|array', + 'room_bed_group.*.*.bed_type_id' => 'nullable|numeric', + 'exclude_occupancy' => 'nullable|max:500', + 'room_size_type' => 'required|max:30', + 'room_size' => 'required|max:11', + 'is_connected_room' => 'nullable|boolean', + 'is_connected_room_price' => 'nullable|boolean', + 'is_connected_room_availability' => 'nullable|boolean', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function checkName($params) + { + try { + $roomRate = PropertyRoom::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->where('room_type_id', $params['room_type_id']) + ->where('id', '!=', $params['room_id']) + ->where('status', 1) + ->first(); + + return $roomRate ? false : true; + } catch (Exception $e) { + return false; + } + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkName"] = $this->checkName( + [ + 'property_id' => $params["property_id"], + 'name' => $params["name"], + 'room_id' => $params['room_id'] , + 'room_type_id' => $params['room_type_id'] + ]); + + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoom/PropertyRoomDeleteValidator.php b/app/Core/Validator/PropertyRoom/PropertyRoomDeleteValidator.php new file mode 100644 index 0000000..ddcd146 --- /dev/null +++ b/app/Core/Validator/PropertyRoom/PropertyRoomDeleteValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoom/PropertyRoomFactMappingAddValidator.php b/app/Core/Validator/PropertyRoom/PropertyRoomFactMappingAddValidator.php new file mode 100644 index 0000000..12478dd --- /dev/null +++ b/app/Core/Validator/PropertyRoom/PropertyRoomFactMappingAddValidator.php @@ -0,0 +1,69 @@ + 'required|numeric', + 'room_id' => 'required|numeric', + "checkPropertyRoom" => "accepted", + 'property_room_fact' => 'required|array', + 'property_room_fact.*.id' => 'required|numeric', + 'property_room_fact.*.is_selected' => 'required|boolean', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkPropertyRoom($params) + { + try { + $roomData = PropertyRoom::where('property_id', $params['property_id']) + ->where('id', $params['room_id']) + ->first(); + + return $roomData ? true : false; + } catch (Exception $e) { + return false; + } + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkPropertyRoom"] = $this->checkPropertyRoom(['property_id' => $params["property_id"], 'room_id' => $params["room_id"] ]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoom/PropertyRoomInventoryValidator.php b/app/Core/Validator/PropertyRoom/PropertyRoomInventoryValidator.php new file mode 100644 index 0000000..47a1d21 --- /dev/null +++ b/app/Core/Validator/PropertyRoom/PropertyRoomInventoryValidator.php @@ -0,0 +1,51 @@ + 'required|numeric', + 'channel_id' => 'required|numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoom/PropertyRoomPhotoMappingAddValidator.php b/app/Core/Validator/PropertyRoom/PropertyRoomPhotoMappingAddValidator.php new file mode 100644 index 0000000..2c9e560 --- /dev/null +++ b/app/Core/Validator/PropertyRoom/PropertyRoomPhotoMappingAddValidator.php @@ -0,0 +1,88 @@ + 'required|numeric', + 'room_id' => 'required|numeric', + "checkPropertyRoom" => "accepted", + "checkPropertyPhoto" => "accepted", + 'property_room_photo' => 'required|array', + 'property_room_photo.*.id' => 'required|numeric', + 'property_room_photo.*.is_selected' => 'required|boolean', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkPropertyRoom($params) + { + try { + $roomData = PropertyRoom::where('property_id', $params['property_id']) + ->where('id', $params['room_id']) + ->first(); + + return $roomData ? true : false; + } catch (Exception $e) { + return false; + } + } + + public function checkPropertyPhoto($params) + { + try { + + $photos = collect($params['property_room_photo'])->keyBy('id')->keys()->toArray(); + $arrayCount = collect($params['property_room_photo'])->count(); + $photoData = PropertyPhoto::where('property_id', $params['property_id']) + ->whereIn('id', $photos) + ->count(); + return $arrayCount == $photoData ? true : false; + } catch (Exception $e) { + return false; + } + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkPropertyRoom"] = $this->checkPropertyRoom(['property_id' => $params["property_id"], 'room_id' => $params["room_id"] ]); + $params["checkPropertyPhoto"] = $this->checkPropertyPhoto(['property_id' => $params["property_id"], 'property_room_photo' => $params["property_room_photo"] ]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoom/PropertyRoomUpdateValidator.php b/app/Core/Validator/PropertyRoom/PropertyRoomUpdateValidator.php new file mode 100644 index 0000000..0596aa3 --- /dev/null +++ b/app/Core/Validator/PropertyRoom/PropertyRoomUpdateValidator.php @@ -0,0 +1,72 @@ + 'required|numeric', + 'name' => 'required', + "checkName" => "accepted", + 'room_type_id' => 'required|numeric', + 'room_type_count' => 'required|numeric', + 'max_occupancy' => 'required|numeric', + 'max_adult' => 'required|numeric', + 'max_child' => 'required|numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function checkName($params) + { + try { + $roomRate = PropertyRoom::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->where('id', '!=' ,$params['id']) + ->first(); + return $roomRate ? false : true; + } catch (Exception $e) { + return false; + } + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + + $params["checkName"] = $this->checkName(['property_id' => $params["property_id"], 'name' => $params["name"], 'id' => $params["id"] ]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityAddValidator.php b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityAddValidator.php new file mode 100644 index 0000000..899e413 --- /dev/null +++ b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityAddValidator.php @@ -0,0 +1,62 @@ + 'required|numeric', + 'property_room_id' => 'required|numeric', + 'room_rate_mapping_id' => 'required|numeric', + 'channel_id' => 'required|numeric', + 'min_stay' => 'required|numeric', + 'max_stay' => 'required|numeric', + 'stop_sell' => 'required|numeric', + 'booking_on_request' => 'required|numeric', + 'start_date' => 'required|date_format:Y-m-d', + 'end_date' => 'required|date_format:Y-m-d', + 'amount' => 'required|numeric', + 'currency' => 'required', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityBulkInsertValidator.php b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityBulkInsertValidator.php new file mode 100644 index 0000000..9ace505 --- /dev/null +++ b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityBulkInsertValidator.php @@ -0,0 +1,63 @@ +updateTypes = ["availability","room_stop_sell"]; + $this->includeDays = array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); + } + + public function rules($params = null) + { + return + [ + 'property_id' => 'required|numeric', + 'update_type' => 'required|in:'.implode(',', $this->updateTypes), + 'value' => 'required|numeric', + 'start_date' => 'required|date_format:Y-m-d', + 'end_date' => 'required|date_format:Y-m-d', + 'availability_type_id' => 'required|in:1,2,3', + 'channel_id' => 'required|integer', + + 'room_rates' => 'required|array', + 'room_rates.*.room_id' => 'required|numeric', + 'room_rates.*.room_rate_mapping_id' => 'array', + 'include_days.*' => 'required|in:'.implode(',', $this->includeDays), + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityBulkUpdateValidator.php b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityBulkUpdateValidator.php new file mode 100644 index 0000000..a66bce3 --- /dev/null +++ b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityBulkUpdateValidator.php @@ -0,0 +1,56 @@ + 'array', + 'availability.*.setup_type_id' => 'required|numeric', + 'availability.*.room_id' => 'required|numeric', + 'availability.*.date' => 'required|date_format:Y-m-d', + 'availability.*.availability' => 'numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityDeleteValidator.php b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityDeleteValidator.php new file mode 100644 index 0000000..310b3a9 --- /dev/null +++ b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityDeleteValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityUpdateValidator.php b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityUpdateValidator.php new file mode 100644 index 0000000..23e961d --- /dev/null +++ b/app/Core/Validator/PropertyRoomAvailability/PropertyRoomAvailabilityUpdateValidator.php @@ -0,0 +1,53 @@ + 'required|numeric', + 'property_room_id' => 'required|numeric', + 'room_rate_mapping_id' => 'required|numeric', + 'channel_id' => 'required|numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomBed/PropertyRoomBedAddValidator.php b/app/Core/Validator/PropertyRoomBed/PropertyRoomBedAddValidator.php new file mode 100644 index 0000000..f14f659 --- /dev/null +++ b/app/Core/Validator/PropertyRoomBed/PropertyRoomBedAddValidator.php @@ -0,0 +1,53 @@ + 'required|numeric', + 'bed_group' => 'required|numeric', + 'count' => 'required|numeric', + 'bed_type_id' => 'required|numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRate/PropertyRoomRateAddValidator.php b/app/Core/Validator/PropertyRoomRate/PropertyRoomRateAddValidator.php new file mode 100644 index 0000000..f7497f3 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRate/PropertyRoomRateAddValidator.php @@ -0,0 +1,53 @@ + 'required|numeric', + 'name' => 'required', + 'accommodation_type' => 'required|numeric', + 'min_stay' => 'required|numeric', + 'max_stay' => 'required|numeric', + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRate/PropertyRoomRateAddWithInclusionValidator.php b/app/Core/Validator/PropertyRoomRate/PropertyRoomRateAddWithInclusionValidator.php new file mode 100644 index 0000000..0720479 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRate/PropertyRoomRateAddWithInclusionValidator.php @@ -0,0 +1,70 @@ + 'required|numeric', + 'name' => 'required', + "checkName" => "accepted", + 'accommodation_type' => 'required|numeric', + /*'min_stay' => 'required|numeric', + 'max_stay' => 'required|numeric',*/ + 'facts' => 'nullable|array', + 'facts.*' => 'nullable|numeric', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function checkName($params) + { + try { + $roomRate = PropertyRoomRate::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->first(); + return $roomRate ? false : true; + } catch (Exception $e) { + return false; + } + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + + $params["checkName"] = $this->checkName(['property_id' => $params["property_id"], 'name' => $params["name"] ]); + return $this->make($params, $this->rules($params), $this->messages()); + + } + +} diff --git a/app/Core/Validator/PropertyRoomRate/PropertyRoomRateDeleteValidator.php b/app/Core/Validator/PropertyRoomRate/PropertyRoomRateDeleteValidator.php new file mode 100644 index 0000000..4c9f057 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRate/PropertyRoomRateDeleteValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRate/PropertyRoomRateUpdateValidator.php b/app/Core/Validator/PropertyRoomRate/PropertyRoomRateUpdateValidator.php new file mode 100644 index 0000000..04352b6 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRate/PropertyRoomRateUpdateValidator.php @@ -0,0 +1,71 @@ + 'required|numeric', + 'property_id' => 'required|numeric', + 'name' => 'required', + "checkName" => "accepted", + 'accommodation_type' => 'required|numeric', + 'status' => 'numeric|min:0|max:1' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkName($params) + { + try { + $roomRate = PropertyRoomRate::where('property_id', $params['property_id']) + ->where('name', $params['name']) + ->where('id', '!=' ,$params['id']) + ->first(); + return $roomRate ? false : true; + } catch (Exception $e) { + return false; + } + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + + $params["checkName"] = $this->checkName(['property_id' => $params["property_id"], 'name' => $params["name"], 'id' => $params["id"] ]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingAddValidator.php b/app/Core/Validator/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingAddValidator.php new file mode 100644 index 0000000..1fe3b0c --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingAddValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + 'channels' => 'required|array', + 'channels.*' => 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingUpdateValidator.php b/app/Core/Validator/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingUpdateValidator.php new file mode 100644 index 0000000..408fc1e --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateChannelMapping/PropertyRoomRateChannelMappingUpdateValidator.php @@ -0,0 +1,54 @@ + 'required|array', + 'room_rate_channel_mapping.*.room_rate_mapping_id' => 'required|numeric', + 'room_rate_channel_mapping.*.channel_id' => 'required|numeric', + 'room_rate_channel_mapping.*.is_selected' => 'required|boolean' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateInclusionMapping/PropertyRoomRateInclusionMappingAddValidator.php b/app/Core/Validator/PropertyRoomRateInclusionMapping/PropertyRoomRateInclusionMappingAddValidator.php new file mode 100644 index 0000000..5e90747 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateInclusionMapping/PropertyRoomRateInclusionMappingAddValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + 'facts' => 'required|array', + 'facts.*' => 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingAddValidator.php b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingAddValidator.php new file mode 100644 index 0000000..e5daec0 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingAddValidator.php @@ -0,0 +1,55 @@ + 'required|numeric', + 'room_rate_id' => 'required|numeric', + 'description' => 'required', + 'rack_rate' => 'required|numeric', + 'included_occupancy' => 'required|numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingAddWithSetupValidator.php b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingAddWithSetupValidator.php new file mode 100644 index 0000000..214b995 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingAddWithSetupValidator.php @@ -0,0 +1,59 @@ + 'required|array', + 'room_id.*' => 'required|numeric', + 'room_rate_id' => 'required|numeric', + // 'rack_rate' => 'required|numeric', + 'included_occupancy' => 'required|numeric', + /*'room_rate_mapping_setup' => 'required|array', + 'room_rate_mapping_setup.*.setup_type' => 'required|in:EXT_ADULT,EXT_CHILD,DIS_GUEST', + 'room_rate_mapping_setup.*.value_type' => 'required|in:FIX,PER', + 'room_rate_mapping_setup.*.value' => 'required|numeric',*/ + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingConnectedValidator.php b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingConnectedValidator.php new file mode 100644 index 0000000..51a1feb --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingConnectedValidator.php @@ -0,0 +1,55 @@ + 'required|numeric', + 'room_rate_id' => 'required|numeric', + 'connected_room_rate.*.room_id' => 'required|numeric', + 'connected_room_rate.*.room_rate_id' => 'required|numeric', + 'connected_room_rate.*.is_affected_price' => 'required|boolean', + 'connected_room_rate.*.affect_price_action_type' => 'nullable|in:INC,DEC', + 'connected_room_rate.*.affect_price_type' => 'nullable|in:PER,FIX', + 'connected_room_rate.*.affect_price_value' => 'nullable|numeric', + ]; + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages(), $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingDeleteValidator.php b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingDeleteValidator.php new file mode 100644 index 0000000..7207dc3 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingDeleteValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingSetStatusValidator.php b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingSetStatusValidator.php new file mode 100644 index 0000000..14cbeca --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingSetStatusValidator.php @@ -0,0 +1,48 @@ + 'required|numeric', + 'is_selected' => 'required|boolean', + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingUpdateValidator.php b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingUpdateValidator.php new file mode 100644 index 0000000..8a2f468 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateMapping/PropertyRoomRateMappingUpdateValidator.php @@ -0,0 +1,55 @@ + 'required|numeric', + 'room_id' => 'required|numeric', + 'room_rate_id' => 'required|numeric', + 'description' => 'required', + 'rack_rate' => 'required|numeric', + 'included_occupancy' => 'required|numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRateMappingSetup/PropertyRoomRateMappingSetupAddValidator.php b/app/Core/Validator/PropertyRoomRateMappingSetup/PropertyRoomRateMappingSetupAddValidator.php new file mode 100644 index 0000000..be0a74c --- /dev/null +++ b/app/Core/Validator/PropertyRoomRateMappingSetup/PropertyRoomRateMappingSetupAddValidator.php @@ -0,0 +1,61 @@ +setupType = array('EXT_ADULT', 'EXT_CHILD', 'DIS_GUEST'); + $this->valueType = array('PER', 'FIX'); + + } + + public function rules($params = null) + { + return + [ + 'room_rate_mapping_id' => 'required|numeric', + 'setup' => 'required|array', + 'setup.*.setup_type' => 'required|in:'.implode(',', $this->setupType), + 'setup.*.value_type' => 'required|in:'.implode(',', $this->valueType), + 'setup.*.value' => 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceAddValidator.php b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceAddValidator.php new file mode 100644 index 0000000..d352f3b --- /dev/null +++ b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceAddValidator.php @@ -0,0 +1,62 @@ + 'required|numeric', + 'property_room_id' => 'required|numeric', + 'room_rate_mapping_id' => 'required|numeric', + 'channel_id' => 'required|numeric', + 'min_stay' => 'required|numeric', + 'max_stay' => 'required|numeric', + 'stop_sell' => 'required|numeric', + 'booking_on_request' => 'required|numeric', + 'start_date' => 'required|date_format:Y-m-d', + 'end_date' => 'required|date_format:Y-m-d', + 'amount' => 'required|numeric', + 'currency' => 'required', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceBulkInsertValidator.php b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceBulkInsertValidator.php new file mode 100644 index 0000000..1029989 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceBulkInsertValidator.php @@ -0,0 +1,65 @@ +updateTypes = ["rate", "minstay", "maxstay", "stopsell", "bookingonrequest","rate_stop_sell","min_stay","quick_pricing"]; + $this->includeDays = array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); + } + + public function rules($params = null) + { + return + [ + 'property_id' => 'required|numeric', + 'update_type' => 'required|in:'.implode(',', $this->updateTypes), + 'value' => 'required|numeric', + 'start_date' => 'required|date_format:Y-m-d', + 'end_date' => 'required|date_format:Y-m-d', + 'availability_type_id' => 'required|in:1,2,3', + 'channel_id' => 'required|integer', + + 'room_rates' => 'required|array', + 'room_rates.*.room_id' => 'required|numeric', + 'room_rates.*.room_rate_mapping_id' => 'required|array', + 'include_days.*' => 'required|in:'.implode(',', $this->includeDays), + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceBulkUpdateValidator.php b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceBulkUpdateValidator.php new file mode 100644 index 0000000..46d7390 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceBulkUpdateValidator.php @@ -0,0 +1,59 @@ +updateTypes = ["rate", "min_stay", "max_stay", "stop_sell"]; + $this->includeDays = array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); + } + + public function rules($params = null) + { + return + [ + 'rates' => 'array', + 'rates.*.setup_type_id' => 'required|numeric', + 'rates.*.room_id' => 'required|numeric', + 'rates.*.room_rate_mapping_id' => 'required|numeric', + 'rates.*.date' => 'required|date_format:Y-m-d', + 'rates.*.amount' => 'numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceDeleteValidator.php b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceDeleteValidator.php new file mode 100644 index 0000000..d55783d --- /dev/null +++ b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceDeleteValidator.php @@ -0,0 +1,54 @@ + 'required|numeric', + + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceUpdateValidator.php b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceUpdateValidator.php new file mode 100644 index 0000000..f4c32e5 --- /dev/null +++ b/app/Core/Validator/PropertyRoomRatePrice/PropertyRoomRatePriceUpdateValidator.php @@ -0,0 +1,53 @@ + 'required|numeric', + 'property_room_id' => 'required|numeric', + 'room_rate_mapping_id' => 'required|numeric', + 'channel_id' => 'required|numeric', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyWeb/PropertyWebContactFormValidator.php b/app/Core/Validator/PropertyWeb/PropertyWebContactFormValidator.php new file mode 100644 index 0000000..7983116 --- /dev/null +++ b/app/Core/Validator/PropertyWeb/PropertyWebContactFormValidator.php @@ -0,0 +1,52 @@ + 'required', + "surname" => 'required', + "email" => "required|email", + "phone" => "required", + "message" => 'required|min:10', + ]; + } + + public function messages() + { + $thisMessages = [ + 'message.min' => __('error-input-min', ['input' => __('myweb-input-message')]), + 'message.required' => __('error-input-required', ['input' => __('myweb-input-message')]), + 'phone.required' => __('error-input-required', ['input' => __('enw-input-phone')]), + ]; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyWeb/PropertyWebCreateValidator.php b/app/Core/Validator/PropertyWeb/PropertyWebCreateValidator.php new file mode 100644 index 0000000..c4d445d --- /dev/null +++ b/app/Core/Validator/PropertyWeb/PropertyWebCreateValidator.php @@ -0,0 +1,88 @@ + 'required|numeric', + "domain" => 'required|min:5|max:50', + "hasDomain" => "accepted", + "checkDomain" => "accepted", + "isValidDomain" => "accepted", + "default_language" => 'required|min:2|max:10', + "template_id" => 'required|numeric|min:1', + ]; + } + + public function messages() + { + $thisMessages = + [ + "checkDomain.accepted" => "This domain added before", + "hasDomain.accepted" => "This property have added other domain before", + "isValidDomain.accepted" => "Not validate domain name.", + ]; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkDomain($params) + { + try { + $domain = PropertyWeb::where('domain', $params['domain']) + ->first(); + return $domain ? false : true; + } catch (Exception $e) { + return false; + } + } + + public function hasDomain($params) + { + try { + $domain = PropertyWeb::where('property_id', $params['property_id']) + ->first(); + return $domain ? false : true; + } catch (Exception $e) { + return false; + } + } + + public function validDomain($domainName) + { + try { + return preg_match("/^(?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63}$/", $domainName) ? true : false; + } catch (Exception $e) { + return false; + } + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["isValidDomain"] = $this->validDomain($params['domain']); + $params["hasDomain"] = $this->hasDomain(['property_id' => $params["property_id"] ]); + $params["checkDomain"] = $this->checkDomain(['domain' => $params["domain"] ]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyWeb/PropertyWebPublishValidator.php b/app/Core/Validator/PropertyWeb/PropertyWebPublishValidator.php new file mode 100644 index 0000000..d96afb2 --- /dev/null +++ b/app/Core/Validator/PropertyWeb/PropertyWebPublishValidator.php @@ -0,0 +1,44 @@ + 'required|numeric', + "property_web_id" => 'required|numeric', + "is_publish" => 'required' + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyWeb/PropertyWebUpdateContentValidator.php b/app/Core/Validator/PropertyWeb/PropertyWebUpdateContentValidator.php new file mode 100644 index 0000000..de4599e --- /dev/null +++ b/app/Core/Validator/PropertyWeb/PropertyWebUpdateContentValidator.php @@ -0,0 +1,50 @@ + 'required|numeric', + "property_id" => 'required|numeric', + "action" => 'required|in:preview,publish', + "photo_mapping" => "array", + "photo_mapping.*" => "integer", + "cover_photos" => 'required|array', + "cover_photos.*" => 'integer' + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyWeb/PropertyWebUpdateValidator.php b/app/Core/Validator/PropertyWeb/PropertyWebUpdateValidator.php new file mode 100644 index 0000000..af43081 --- /dev/null +++ b/app/Core/Validator/PropertyWeb/PropertyWebUpdateValidator.php @@ -0,0 +1,76 @@ + 'required|numeric', + "property_id" => 'required|numeric', + "domain" => 'required|min:5|max:50', + "checkDomain" => "accepted", + "isValidDomain" => "accepted", + "default_language" => 'required|min:2|max:10', + "template_id" => 'required|numeric|min:1', + ]; + } + + public function messages() + { + $thisMessages = + [ + "checkDomain.accepted" => "This domain used other property." + ]; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkDomain($params) + { + try { + $domain = PropertyWeb::where('domain', $params['domain']) + ->whereNotIn( 'property_id', [ $params['property_id'] ]) + ->first(); + return $domain ? false : true; + } catch (Exception $e) { + return false; + } + } + + public function validDomain($domainName) + { + try { + return preg_match("/^(?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63}$/", $domainName) ? true : false; + } catch (Exception $e) { + return false; + } + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkDomain"] = $this->checkDomain(['domain' => $params["domain"], 'property_id' => $params["property_id"] ]); + $params["isValidDomain"] = $this->validDomain($params['domain']); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyWebContent/PropertyWebContentCreateValidator.php b/app/Core/Validator/PropertyWebContent/PropertyWebContentCreateValidator.php new file mode 100644 index 0000000..f943eaa --- /dev/null +++ b/app/Core/Validator/PropertyWebContent/PropertyWebContentCreateValidator.php @@ -0,0 +1,52 @@ + 'required|numeric', + "content_category_id" => 'required|numeric', + "title" => 'required|min:2|max:250', + "language_code" => 'required|min:2|max:10', + "image" => 'nullable', + "content" => 'nullable', + ]; + } + + public function messages() + { + $thisMessages = + [ + "checkDomain.accepted" => "This domain added before", + "hasDomain.accepted" => "This property have added other domain before", + "isValidDomain.accepted" => "Not validate domain name.", + ]; + return array_merge(parent::messages(), $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/PropertyWebTemplate/PropertyWebTemplateCreateValidator.php b/app/Core/Validator/PropertyWebTemplate/PropertyWebTemplateCreateValidator.php new file mode 100644 index 0000000..5415843 --- /dev/null +++ b/app/Core/Validator/PropertyWebTemplate/PropertyWebTemplateCreateValidator.php @@ -0,0 +1,43 @@ + 'required|min:3', + "folder_name" => 'required|min:5', + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/TempProperty/TempPropertySearchValidator.php b/app/Core/Validator/TempProperty/TempPropertySearchValidator.php new file mode 100644 index 0000000..90c9f7c --- /dev/null +++ b/app/Core/Validator/TempProperty/TempPropertySearchValidator.php @@ -0,0 +1,50 @@ +tempPropertyRepository = $tempPropertyRepository; + } + + public function rules($params = null) + { + return + [ + 'name' => 'required|min:3' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/User/ChangePasswordValidator.php b/app/Core/Validator/User/ChangePasswordValidator.php new file mode 100644 index 0000000..6c9f40d --- /dev/null +++ b/app/Core/Validator/User/ChangePasswordValidator.php @@ -0,0 +1,69 @@ +userRepository = $userRepository; + } + + public function rules($params = null) + { + return + [ + "user_id" => "required", + "checkOldPassword" => "accepted", + 'password' => 'required|max:20|min:6|confirmed', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function checkOldPassword($params) + { + try { + + + $user = User::where('id', $params['user_id'])->first(); + $result = Hash::check($params['old_password'], $user->password) ; + return $result ? true : false; + + } catch (Exception $e) { + return false; + } + + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["checkOldPassword"] = $this->checkOldPassword(['user_id' => $params["user_id"], 'old_password' => $params["old_password"] ]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/User/ResetPasswordValidator.php b/app/Core/Validator/User/ResetPasswordValidator.php new file mode 100644 index 0000000..64b51f1 --- /dev/null +++ b/app/Core/Validator/User/ResetPasswordValidator.php @@ -0,0 +1,55 @@ +userRepository = $userRepository; + } + + public function rules($params = null) + { + return + [ + 'email' => 'required|email', + 'hash_key' => 'required|min:128', + 'password' => 'required|max:20|min:6|confirmed', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/User/UserCreateValidator.php b/app/Core/Validator/User/UserCreateValidator.php new file mode 100644 index 0000000..83aac49 --- /dev/null +++ b/app/Core/Validator/User/UserCreateValidator.php @@ -0,0 +1,73 @@ +userRepository = $userRepository; + } + + public function rules($params = null) + { + return + [ + 'name' => 'required|min:3|max:150', + 'email' => 'required|email', + 'isUniqueEmail' => 'accepted', + 'password' => 'required|max:20' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function isUniqueEmail($email) + { + try { + $criteria = + [ + "criteria" => + [ + ["field" => "email", "condition" => "=", "value" => $email] + ] + ]; + $result = $this->userRepository->findByCriteria($criteria); + + return $result ? false : true; + + } catch (Exception $e) { + return false; + } + + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + $params["isUniqueEmail"] = $this->isUniqueEmail($params["email"]); + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/User/UserLoginValidator.php b/app/Core/Validator/User/UserLoginValidator.php new file mode 100644 index 0000000..f4a2413 --- /dev/null +++ b/app/Core/Validator/User/UserLoginValidator.php @@ -0,0 +1,50 @@ +userRepository = $userRepository; + } + + public function rules($params = null) + { + return + [ + 'email' => 'required|email', + 'password' => 'required' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/User/UserNewPasswordValidator.php b/app/Core/Validator/User/UserNewPasswordValidator.php new file mode 100644 index 0000000..12a2474 --- /dev/null +++ b/app/Core/Validator/User/UserNewPasswordValidator.php @@ -0,0 +1,52 @@ +userRepository = $userRepository; + } + + public function rules($params = null) + { + return + [ + 'email' => 'required|email', + 'hash_key' => 'required|min:128', + 'password' => 'required|max:20|min:6|confirmed', + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/User/UserProfileUpdateValidator.php b/app/Core/Validator/User/UserProfileUpdateValidator.php new file mode 100644 index 0000000..9594969 --- /dev/null +++ b/app/Core/Validator/User/UserProfileUpdateValidator.php @@ -0,0 +1,44 @@ + 'required', + "surname" => 'required', + "gender" => 'nullable', + "language" => 'required', + "phone" => 'nullable' + ]; + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/UserPropertyMapping/UserPropertyMappingAddValidator.php b/app/Core/Validator/UserPropertyMapping/UserPropertyMappingAddValidator.php new file mode 100644 index 0000000..024ba3f --- /dev/null +++ b/app/Core/Validator/UserPropertyMapping/UserPropertyMappingAddValidator.php @@ -0,0 +1,53 @@ + 'required|numeric', + 'add_property_id' => 'array', + 'add_property_id.*' => 'numeric' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Core/Validator/UserPropertyMapping/UserPropertyMappingRemoveValidator.php b/app/Core/Validator/UserPropertyMapping/UserPropertyMappingRemoveValidator.php new file mode 100644 index 0000000..5d90bf0 --- /dev/null +++ b/app/Core/Validator/UserPropertyMapping/UserPropertyMappingRemoveValidator.php @@ -0,0 +1,52 @@ + 'required|numeric', + 'remove_property_id' => 'array', + 'remove_property_id.*' => 'numeric' + ]; + + + } + + public function messages() + { + $thisMessages = []; + return array_merge(parent::messages() , $thisMessages); + } + + + + public function validate(array $params, array $rules = [], array $messages = [], array $customAttributes = []) + { + return $this->make($params, $this->rules($params), $this->messages()); + } + +} diff --git a/app/Events/Event.php b/app/Events/Event.php new file mode 100644 index 0000000..4c2962b --- /dev/null +++ b/app/Events/Event.php @@ -0,0 +1,10 @@ +message = $message; + $this->messageArr = $message; + $message = is_array($message) ? implode(" -- ",$message):$message; + parent::__construct($message,$code,$previous); + } + + public function getMessageArr() + { + return is_array( $this->messageArr ) ? $this->messageArr : [$this->messageArr]; + } + + +} diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php new file mode 100644 index 0000000..1a5cd78 --- /dev/null +++ b/app/Exceptions/Handler.php @@ -0,0 +1,96 @@ + "#ea4335", + "title" => ":warning: Exception ", + "text" => "<" . config("app.url") . "|" . config("app.name") . ">, " . + date("d-m-Y h:i:s") . ' ```' . + $exception->getFile() . ', ' . + $exception->getLine() . ', ' . + $exception->getMessage() . '```' + ] + ]; + + $jobCount = Jobs::count(); + if ($jobCount < 100) { + //dispatch(new SlackLogJob($param))->onQueue('slackLog'); + } else { + Log::error($param); + } + } + } + + parent::report($exception); + } + + /** + * Render an exception into an HTTP response. + * + * @param \Illuminate\Http\Request $request + * @param \Exception $exception + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function render($request, Exception $exception) + { + return parent::render($request, $exception); + } +} diff --git a/app/Export/PropertyBookingListExport.php b/app/Export/PropertyBookingListExport.php new file mode 100644 index 0000000..630a003 --- /dev/null +++ b/app/Export/PropertyBookingListExport.php @@ -0,0 +1,78 @@ +dataTableData = $dataTableData; + } + + public function registerEvents(): array + { + return [ + AfterSheet::class => function (AfterSheet $event) { + $cellRange = 'A1:J1'; // All headers + $event->sheet->getDelegate()->getStyle($cellRange)->getFont()->setBold(true); + //$event->sheet->setAutoFilter($cellRange); + $event->sheet->setTitle('Property Booking List'); + }, + ]; + } + + public function columnFormats(): array + { + return [ + 'D' => NumberFormat::FORMAT_DATE_DDMMYYYY, + 'E' => NumberFormat::FORMAT_DATE_DDMMYYYY, + 'J' => NumberFormat::FORMAT_DATE_DATETIME, + ]; + } + + public function headings(): array + { + $resetData = reset($this->dataTableData); + $header = array_keys($resetData); + + return $header; + } + + public function array(): array + { + + $dataTableData = []; + + foreach ($this->dataTableData as $dataOrderKey => $dataBooking) { + + foreach ($dataBooking as $dataKey => $value) { + + if (in_array($dataKey, ['checkin_date', 'checkout_date', 'reservation_time'])) { + $value = Date::dateTimeToExcel(Carbon::parse($value)); + } + + $dataTableData[$dataOrderKey][$dataKey] = $value; + + } + + } + + + return [$dataTableData]; + } + +} diff --git a/app/Export/PropertyCompetitorExport.php b/app/Export/PropertyCompetitorExport.php new file mode 100644 index 0000000..82411ce --- /dev/null +++ b/app/Export/PropertyCompetitorExport.php @@ -0,0 +1,75 @@ +dataTableData = $dataTableData; + } + + public function registerEvents(): array + { + return [ + AfterSheet::class => function(AfterSheet $event) { + $cellRange = 'A1:Q1'; // All headers + $event->sheet->getDelegate()->getStyle($cellRange)->getFont()->setBold(true); + //$event->sheet->setAutoFilter($cellRange); + $event->sheet->setTitle('Property Competitor Export'); + }, + ]; + } + + public function headings(): array + { + + $header = [ + 'Property' + ]; + + foreach ($this->dataTableData['date'] as $date) { + $header[] = $date; + } + + $header[] = 'Currency'; + + return $header; + } + + public function array(): array + { + + $dataTableData = []; + + foreach ($this->dataTableData['value'] as $propertyKey => $propertyValue) { + + $dataTableData[$propertyKey] = [ + 'name' => $this->dataTableData['property'][$propertyKey] + ]; + + foreach ($propertyValue as $dateKey => $value) { + $dataTableData[$propertyKey][$dateKey] = $value['amount']; + } + + $dataTableData[$propertyKey]['currency'] = $value['currency']; + + } + + + return [$dataTableData]; + } + +} diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php new file mode 100644 index 0000000..9277f8a --- /dev/null +++ b/app/Http/Controllers/AuthController.php @@ -0,0 +1,328 @@ +request = $request; + $this->userLoginValidator = $userLoginValidator; + $this->jwtService = $jwtService; + $this->userPropertyMappingService = $userPropertyMappingService; + $this->permissionService = $permissionService; + $this->apiAccessTokenService = $apiAccessTokenService; + } + + public function authenticate(User $user) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + $return = []; + + $validationData = [ + 'email' => $this->request->input('email'), + 'password' => $this->request->input('password') + ]; + + $locale = $this->request->input('locale'); + $rememberMe = $this->request->input('remember_me') ; + + $validationResult = $this->userLoginValidator->validate($validationData); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + $user = User::where('email', $this->request->input('email'))->where('status', 1)->first(); + if (!$user) { + throw new ApiErrorException(lang('Email or password is wrong.')); + } + + if (Hash::check($this->request->input('password'), $user->password)) { + $jwtToken = $this->jwtService->jwtCreate(['user_id' => $user['id'], 'remember_me' => $rememberMe, 'day_counter' => 5]); + if ($jwtToken['status'] != 'success') { + throw new ApiErrorException(lang('An unknown error occurred.')); + } + + $jwtToken = $jwtToken['data']; + + $return = [ + 'token' => $jwtToken['token'] + ]; + } else { + throw new ApiErrorException(lang('Email or password is wrong.')); + } + + $saveToken = [ + "token" => md5(fillOnUndefined($jwtToken, "token")), + "expire_date" => fillOnUndefined($jwtToken, "exp"), + "user_id" => fillOnUndefined($user, "id"), + "invalidate" => fillOnUndefined($jwtToken, "invalidate", 0), + ]; + + $saveTokenTo = $this->apiAccessTokenService->create($saveToken); + if ($saveTokenTo['status'] != 'success') { + + throw new ApiErrorException(lang('General error')); + + } + + $return = [ + 'token' => $jwtToken['token'], + 'expire_time' => $saveTokenTo['data']['expire_time'], + 'locale' => $user['locale'] + ]; + + $onesignalKey = $this->request->input('onesignal_key'); + + if(isset($onesignalKey) && $onesignalKey){ + if(strlen($onesignalKey) > 36){ + throw new ApiErrorException(lang('Onesignal Key Size error')); + } + $userUpdateStatus = User::where('id', $user['id'])->where('status', 1) + ->update(['onesignal_key' => $onesignalKey]); + + if ($userUpdateStatus !== 1) { + throw new ApiErrorException(lang('Onesignal Key Update Error')); + } + } + + $mappingPropertiesCriteria = [ + 'criteria' => [ + ['field' => 'user_id', 'condition' => '=', 'value' => $user['id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + + ], + 'with' => ['property.defaultPropertyPhoto'], + ]; + + $mappingProperties = $this->userPropertyMappingService->select($mappingPropertiesCriteria); + if (!$mappingProperties['data']) { + throw new ApiErrorException(lang('User Property mapping not found')); + } + $propertyList = collect($mappingProperties['data'])->map(function ($value) use ($user, $locale) { + $menuParams = [ + 'user_id' => $user['id'], + 'property_id' => $value['property']['id'], + 'status' => $value['property']['status'], + 'locale' => $locale + ] ; + if (is_array($value['property'])) { + $defaultPhoto = isset($value['property']['default_property_photo']) ? $value['property']['default_property_photo'] : null ; + + $photoUrlThumbFilePath = '/assets/img/placeholder.png'; + if(isset($defaultPhoto['photo_name'])){ + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$value['property']['id']}" . "/{$defaultPhoto['photo_name']}_200x200.{$defaultPhoto['file_ext']}"; + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$value['property']['id']}" . "/{$defaultPhoto['photo_name']}_200x200.{$defaultPhoto['file_ext']}"; + }else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . "/property-photos/{$value['property']['id']}" . "/{$defaultPhoto['photo_name']}_thumbnail.{$defaultPhoto['file_ext']}"; + } + } + return $value['property'] = [ + 'id' => $value['property']['id'], + 'name' => $value['property']['name'], + 'status' => $value['property']['status'], + 'default_photo' => $photoUrlThumbFilePath , + // 'property_menu' => $this->permissionService->getMenuTreeForUser($menuParams) + ]; + } + })->where('status' , '=', 1); + + $propertyList = $propertyList->map(function ($value) { + return [ + + 'id' => $value['id'], + 'name' => $value['name'], + 'default_photo' => $value['default_photo'], + ]; + })->toArray(); + $return['property_list'] = $propertyList; + + $return['user'] = [ + 'name' => $user['name'], + 'surname' => $user['surname'], + 'language' => $user['language'] + ]; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function refreshToken(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + $token = $request->header('authToken'); + $credentials = JWT::decode($token, Config::get('app.jwt.secret'), ['HS256']); + $rememberMe = $credentials->remember_me ; + $userId = $request->credentials->user_id; + + $findTokenCriteria = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => md5($token) ], + ['field' => 'expire_date', 'condition' => '>', 'value' => time() ], + ['field' => 'user_id', 'condition' => '=', 'value' => $userId ], + ['field' => 'invalidate', 'condition' => '=', 'value' => 0 ], + ], + 'firstRow' => 1 + ]; + $getTokenData = $this->apiAccessTokenService->select($findTokenCriteria); + + if(!$getTokenData['data']){ + throw new ApiErrorException(lang('Token data not found')); + } + $getTokenData = $getTokenData['data']; + $jwtToken = $this->jwtService->jwtCreate(['user_id' => $userId, 'remember_me' => $rememberMe, 'day_counter' => 0.5]); + if ($jwtToken['status'] != 'success') { + throw new ApiErrorException(lang('An unknown error occurred.')); + } + $jwtToken = $jwtToken['data']; + + $updateToken = [ + "token" => md5(fillOnUndefined($jwtToken, "token")), + "expire_date" => fillOnUndefined($jwtToken, "exp"), + "updated_at" => time(), + ]; + + + $updateTokenTo = $this->apiAccessTokenService->update($getTokenData['id'], $updateToken); + if ($updateTokenTo['status'] != 'success') { + throw new ApiErrorException(lang('General error')); + } + + $return = [ + 'token' => $jwtToken['token'], + 'expire_time' => $updateTokenTo['data']['expire_time'] + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function logOut(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + $token = $request->header('authToken'); + $userId = $request->credentials->user_id; + + $findTokenCriteria = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => md5($token)], + ['field' => 'expire_date', 'condition' => '>', 'value' => time()], + ['field' => 'user_id', 'condition' => '=', 'value' => $userId], + ['field' => 'invalidate', 'condition' => '=', 'value' => 0 ], + ], + 'firstRow' => 1 + ]; + $getTokenData = $this->apiAccessTokenService->select($findTokenCriteria); + + if(!$getTokenData['data']){ + throw new ApiErrorException(lang('Token data not found.')); + } + + $getTokenData = $getTokenData['data']; + $updateToken = [ + "updated_at" => time(), + "invalidate" => 1 , + ]; + + + $updateTokenTo = $this->apiAccessTokenService->update($getTokenData['id'], $updateToken); + if ($updateTokenTo['status'] != 'success') { + throw new ApiErrorException(lang('An unknown error occurred.')); + } + + /*$userUpdateStatus = User::where('id', $userId)->where('status', 1) + ->update(['onesignal_key' => null]); + + if ($userUpdateStatus !== 1) { + throw new ApiErrorException(lang('Onesignal Key Update Error')); + }*/ + + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => 'Logged out.', 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} diff --git a/app/Http/Controllers/BookingEngine/V1/BookingController.php b/app/Http/Controllers/BookingEngine/V1/BookingController.php new file mode 100644 index 0000000..a8caa60 --- /dev/null +++ b/app/Http/Controllers/BookingEngine/V1/BookingController.php @@ -0,0 +1,2661 @@ +request = $request; + $this->bookingService = $bookingService; + $this->bookingRoomService = $bookingRoomService; + $this->bookingRoomPaxService = $bookingRoomPaxService; + $this->bookingContactService = $bookingContactService; + $this->bookingPaymentService = $bookingPaymentService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyPaymentService = $propertyPaymentService; + $this->channelManagerBookingService = $channelManagerBookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + + $this->channelId = $this->request->channelId; + $this->channelToken = $this->request->channelToken; + $this->bookingEngineToken = $this->request->bookingEngineToken; + $this->bookingEnginePropertyId = $this->request->bookingEnginePropertyId; + $this->newBookingMailService = $newBookingMailService; + $this->notificationService = $notificationService; + $this->propertyAddonService = $propertyAddonService; + $this->bookingAddonService = $bookingAddonService; + $this->propertyChannelCouponService = $propertyChannelCouponService; + $this->propertyBookingEngineSearchService = $propertyBookingEngineSearchService; + $this->channelService = $channelService; + $this->propertyBookingEngineService = $propertyBookingEngineService; + $this->currencyService = $currencyService; + $this->mailer = $mailer; + } + + public function checkRoomAndCacheData($roomInfo, $searchCacheData) + { + + $checkRoomAndCache = true; + + $roomPricesCollect = collect($searchCacheData['roomPrices']); + + foreach ($roomInfo as $roomKey => $room) { + + $cachedSearchPriceKeys = $roomPricesCollect->where('occupancyCode', $room['occupancyCode'])->keys()->toArray(); + + if (!in_array($room['rateKey'], $cachedSearchPriceKeys)) { + $checkRoomAndCache = false; + continue; + } + } + + return $checkRoomAndCache; + } + + public function getPropertyRoomAndRoomRateAvailability($param = []) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ['field' => 'availability', 'condition' => '>', 'value' => 0], + ['field' => 'date', 'condition' => '>=', 'value' => $param['checkIn']], + ['field' => 'date', 'condition' => '<', 'value' => $param['checkOut']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['roomDetail.propertyRoomConnected'], + 'orderBy' => [ + ['field' => 'availability_type_id', 'value' => 'ASC'], + ['field' => 'property_room_id', 'value' => 'ASC'], + ['field' => 'room_rate_mapping_id', 'value' => 'ASC'], + ['field' => 'date', 'value' => 'ASC'] + ], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->propertyRoomAvailabilityService->select($requestParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success' || empty($getPropertyRoomAndRoomRateAvailability['data'])) { + throw new ApiErrorException(lang('PropertyRoomAndRoomRateAvailability not found')); + } + + $response = [ + 'status' => true, + 'data' => $getPropertyRoomAndRoomRateAvailability['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + public function getChannelPropertyDetail($channelId, $propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'property', 'channelAvailabilityType', 'channelBookingType', 'channelRoomPricingType', + 'channelBookingPaymentType.paymentType', 'channel' + ], + 'firstRow' => true, + ]; + + $getChannelProperty = $this->propertyChannelMappingService->select($requestParam); + + $response = [ + 'status' => true, + 'data' => $getChannelProperty['data'], + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + public function booking(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $roomInfo = $params['roomInfo']; + $contactInfo = $params['contactInfo']; + $searchKey = $params['searchKey']; + $couponCode = fillOnUndefined($params, 'couponCode'); + $propertyId = $this->bookingEnginePropertyId; + $extraParam = $params['extraParam'] ?? null; + + if (empty($propertyId)) { + $propertyId = $params['propertyId']; + } + + //SearchKey Check Data + $searchCacheData = $this->getCacheDataWithSearchKey($searchKey); + + if (empty($searchCacheData)) { + throw new ApiErrorException(lang('Cache data was expired.')); + } + + $requestParams = $searchCacheData['requestParams']; + + //$checkRoomAndCacheData Check Data + $checkRoomAndCacheData = $this->checkRoomAndCacheData($roomInfo, $searchCacheData); + if (!$checkRoomAndCacheData) { + throw new ApiErrorException(lang('Room rate keys are not matched.')); + } + + + $getChannelPropertyDetail = $this->getChannelPropertyDetail($this->channelId, $propertyId); + if (!$getChannelPropertyDetail['status']) { + throw new ApiErrorException(lang('Property and Channel are not matched.')); + } + + $getChannelPropertyDetail = $getChannelPropertyDetail['data']; + + $currencyCode = null; + $currencyCodes = []; + $paymentCodes = []; + $propertyIds = []; + $totalRoomsPrice = 0; + + $roomAvailabilityKeyGroup = []; + $searchCacheRoomPrices = $searchCacheData['roomPrices']; + foreach ($roomInfo as $roomOccupancy => $room) { + + $rateKeyCodeDecoded = $this->rateKeyCodeDecode($searchCacheRoomPrices[$room['rateKey']]['rateKeyCode']); + + if ($rateKeyCodeDecoded['availabilityTypeId'] == 1) { + + if (!isset($roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']]['count'])) { + $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']]['count'] = 0; + } + + $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']]['count']++; + $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']]['detail'] = $rateKeyCodeDecoded; + + } else { + + + if (!isset($roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']][$rateKeyCodeDecoded['rateMappingId']]['count'])) { + $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']][$rateKeyCodeDecoded['rateMappingId']]['count'] = 0; + } + + $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']][$rateKeyCodeDecoded['rateMappingId']]['count']++; + $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']][$rateKeyCodeDecoded['rateMappingId']]['detail'] = $rateKeyCodeDecoded; + + } + + + $totalRoomsPrice += $searchCacheData['roomPrices'][$room['rateKey']]['total']; + $currencyCodes[] = $searchCacheData['roomPrices'][$room['rateKey']]['currency']; + $paymentCodes[] = $searchCacheData['roomPrices'][$room['rateKey']]['bookingPaymentType']['code']; + $propertyIds[] = $rateKeyCodeDecoded['propertyId']; + + } + + $currencyCodes = array_unique($currencyCodes); + if (count($currencyCodes) != 1) { + throw new ApiErrorException(lang('Currency Code not matched')); + } + + $currencyCode = reset($currencyCodes); + + $paymentCodes = array_unique($paymentCodes); + if (count($paymentCodes) != 1) { + throw new ApiErrorException(lang('Payment Code not matched')); + } + + $paymentCode = reset($paymentCodes); + + if ($paymentCode != $params['paymentInfo']['paymentCode']) { + throw new ApiErrorException(lang('Payment Code and Payment Code Param not matched')); + } + + $propertyIds = array_unique($propertyIds); + if (count($propertyIds) != 1) { + throw new ApiErrorException(lang('Property ID not matched')); + } + + if (reset($propertyIds) != $propertyId) { + throw new ApiErrorException(lang('Property ID not matched')); + } + + //dd($roomInfo); + + $roomInfoCollect = collect($roomInfo)->groupBy('occupancyCode')->toArray(); + $roomsByOccupancies = $this->getRoomsByOccupancies($requestParams['rooms']); + foreach ($roomsByOccupancies as $roomOccupancyKey => $roomsByOccupancy) { + + if (!isset($roomInfoCollect[$roomOccupancyKey])) { + throw new ApiErrorException(lang('Room Occupancy Key not matched')); + } + + if ($roomsByOccupancy['roomCount'] != count($roomInfoCollect[$roomOccupancyKey])) { + throw new ApiErrorException(lang('Room Occupancy Count and Request Room Occupancy Count not matched')); + } + } + + + $propertyRoomAndRoomRateAvailabilityParam = [ + 'property_id' => $propertyId, + 'checkIn' => $searchCacheData['requestParams']['date']['checkIn'], + 'checkOut' => $searchCacheData['requestParams']['date']['checkOut'], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->getPropertyRoomAndRoomRateAvailability($propertyRoomAndRoomRateAvailabilityParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success' || empty($getPropertyRoomAndRoomRateAvailability['data'])) { + throw new ApiErrorException(lang('Room or rooms not available.')); + } + + $propertyRoomAndRoomRateAvailability = collect($getPropertyRoomAndRoomRateAvailability['data']); + $dateByDay = $this->getDateByDay($searchCacheData['requestParams']['date']); + + + $bookingAddon = []; + $totalAddonPrice = 0; + + + if (isset($params['addonInfo'])) { + + $propertyChannelAddonCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyAddon.fact'] + ]; + $columns = ['id', 'property_id', 'channel_id', 'property_addon_id', 'amount', 'type', 'min_stay']; + $selectPropertyChannelAddon = $this->propertyAddonService->selectPropertyChannelAddon($propertyChannelAddonCriteria, $columns); + + $propertyChannelAddon = []; + if ($selectPropertyChannelAddon['status'] == 'success') { + $propertyChannelAddon = $selectPropertyChannelAddon['data']; + } + + if (empty($propertyChannelAddon)) { + throw new ApiErrorException(lang('No additional defined products found.')); + } + + $propertyChannelAddonCollect = collect($propertyChannelAddon); + $propertyChannelAddonIds = $propertyChannelAddonCollect->pluck('id'); + $propertyChannelAddonIds = $propertyChannelAddonIds ? $propertyChannelAddonIds->toArray() : []; + $nightOfStay = Carbon::parse($searchCacheData['requestParams']['date']['checkIn'])->diff(Carbon::parse($searchCacheData['requestParams']['date']['checkOut']))->days; + + + $addonInfo = collect($params['addonInfo']); + $addonInfoIds = $addonInfo->pluck('id'); + $addonInfoIds = $addonInfoIds ? $addonInfoIds->toArray() : []; + + + if (count(array_intersect($addonInfoIds, $propertyChannelAddonIds)) != count($addonInfoIds)) { + throw new ApiErrorException(lang('Additional product values submitted do not match.')); + } + + foreach ($params['addonInfo'] as $addonInfo) { + + $selectedAddon = $propertyChannelAddonCollect->where('id', $addonInfo['id'])->first(); + + $addonInfoAttribute = null; + /*if (isset($addonInfo['attribute'])) { + foreach ($addonInfo['attribute'] as $addonAttribute) { + if (count(array_intersect_key($addonAttribute, $selectedAddon['property_addon']['attributeArray'])) != count($addonAttribute)) { + throw new ApiErrorException(lang('Additional product attribute values submitted do not match.')); + } + } + $addonInfoAttribute = $addonInfo['attribute']; + }*/ + + if (!is_null($selectedAddon['min_stay']) && $selectedAddon['min_stay'] <= $nightOfStay) { + $selectedAddon['amount'] = 0; + } + + $bookingAddon[$selectedAddon['id']] = [ + 'property_addon_id' => $selectedAddon['property_addon_id'], + 'property_channel_addon_id' => $selectedAddon['id'], + 'count' => $addonInfo['count'], + 'amount' => $selectedAddon['amount'], + 'total' => moneyDoubleFormatDecimal($addonInfo['count'] * $selectedAddon['amount']), + 'currency_code' => $currencyCode, + 'attribute' => $addonInfoAttribute + ]; + + $totalAddonPrice += $bookingAddon[$selectedAddon['id']]['total']; + } + + } + + $couponCodeData = []; + if (!is_null($couponCode)) { + + $propertyChannelCouponCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['channel_id']], + ['field' => 'code', 'condition' => '=', 'value' => $couponCode], + ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::now()->toDateString()], + ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $propertyChannelCoupon = $this->propertyChannelCouponService->select($propertyChannelCouponCriteria); + if ($propertyChannelCoupon['status'] == 'success') { + $couponCodeData = $propertyChannelCoupon['data']; + } + + //Reservation Date Check + $reservationDateCheck = true; + if (!is_null($couponCodeData['reservation_start_date']) && !is_null($couponCodeData['reservation_end_date'])) { + if (isset($searchCacheData['requestParams']['date']['checkIn']) && isset($searchCacheData['requestParams']['date']['checkOut'])) { + $reservationDateCheck = Carbon::parse($couponCodeData['reservation_start_date'])->lessThanOrEqualTo(Carbon::parse($searchCacheData['requestParams']['date']['checkIn'])) + && Carbon::parse($couponCodeData['reservation_end_date'])->greaterThanOrEqualTo(Carbon::parse($searchCacheData['requestParams']['date']['checkOut'])); + } + } + + if(!$reservationDateCheck) { + $couponCodeData = []; + } + + } + + if (!isset($params['bookingCode'])) { + + //TODO: VALIDATORs + //1. Roominfo içindeki her bir occupancyCode ile paxes alar validatorden geçecek + //2. paxes için valdiatorden geçecek, eğer çocuk ise dt zorunlu, type, name, surname, citizen zorunlu + //3. contactInfo içi komple zorunlu validatorden geçecek + //4. searchKey, propertyId, contactInfo, roomInfo, paymentInfo zorunlu + //5. gelen ödeme tipi ile rate lerde ödmee tipleri kontrol edilecek + + + ///BOOKING START + DB::beginTransaction(); + + + $bookingCode = getCodeGenerate('BKG'); + $bookingStatus = in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) ? 1 : 2; + //1. Booking + //2. PreBooking + + + $totalRoomsPriceBeforeDiscount = $totalRoomsPrice; + $bookingDiscountAmount = 0; + if (!empty($couponCodeData)) { + if ($couponCodeData['type'] == 'PER') { + $bookingDiscountAmount = ($totalRoomsPriceBeforeDiscount * $couponCodeData['value'] / 100); + } elseif ($couponCodeData['type'] == 'FIX') { + $bookingDiscountAmount = $couponCodeData['value']; + } + $totalRoomsPrice = $totalRoomsPrice - $bookingDiscountAmount; + } + + $bookingTotalAmount = $totalRoomsPrice + $totalAddonPrice; + + + //Wholesaler MARKUP + $channelPropertyMarkup = []; + if (in_array($getChannelPropertyDetail['channel']['channel_category_id'], [7])) { + + if (!is_null($getChannelPropertyDetail['markup']) && !empty($getChannelPropertyDetail['channel']['default_currency'])) { + + $lastExchangeRate = 1; + if ($getChannelPropertyDetail['currency_code'] != $getChannelPropertyDetail['channel']['default_currency']) { + $lastExchangeRate = $this->currencyService->lastExchangeRate($getChannelPropertyDetail['currency_code'], $getChannelPropertyDetail['channel']['default_currency']); + if ($lastExchangeRate['status'] == 'success') { + $lastExchangeRate = $lastExchangeRate['data']; + } + } + + $connectedChannelAction = !is_null($getChannelPropertyDetail['connected_channel_action']) ? json_decode($getChannelPropertyDetail['connected_channel_action'], 1) : null; + + $channelPropertyMarkup = [ + 'channel_discount' => !empty($connectedChannelAction) && isset($connectedChannelAction['value']) ? $connectedChannelAction['value'] : null, + 'channel_markup' => $getChannelPropertyDetail['markup'], + 'channel_currency_code' => $getChannelPropertyDetail['channel']['default_currency'], + 'channel_currency_exchange' => $lastExchangeRate, + ]; + + } + } + //Wholesaler MARKUP + + $bannedCharacters = ['<','>', ';', '(', ')', '=', ':', 'shell_exec']; + $bannedCheckParameter = ['name', 'surname', 'phone_number', 'email', 'note']; + + $bookingCreateParam = [ + 'property_id' => $propertyId, + 'channel_id' => $this->channelId, + 'booking_code' => $bookingCode, + 'search_key' => $params['searchKey'], + 'checkin_date' => $searchCacheData['requestParams']['date']['checkIn'], + 'checkout_date' => $searchCacheData['requestParams']['date']['checkOut'], + 'rooms' => json_encode($searchCacheData['requestParams']['rooms']), + 'payment_type_code' => $params['paymentInfo']['paymentCode'], + 'room_amount' => $totalRoomsPriceBeforeDiscount, + 'addon_amount' => $totalAddonPrice, + 'discount_amount' => $bookingDiscountAmount, + 'total' => $bookingTotalAmount, + 'currency_code' => $currencyCode, + 'channel_token' => $this->channelToken, + 'booking_engine_token' => $this->bookingEngineToken, + 'status' => $bookingStatus, + 'channel_discount' => fillOnUndefined($channelPropertyMarkup, 'channel_discount'), + 'channel_markup' => fillOnUndefined($channelPropertyMarkup, 'channel_markup'), + 'channel_currency_code' => fillOnUndefined($channelPropertyMarkup, 'channel_currency_code'), + 'channel_currency_exchange' => fillOnUndefined($channelPropertyMarkup, 'channel_currency_exchange'), + 'extra_param' => !empty($extraParam) ? json_encode($extraParam) : null, + 'coupon_code' => fillOnUndefined($couponCodeData,'code') + //Burada sor sat için bişey gelebilir + ]; + + $bookingCreate = $this->bookingService->create($bookingCreateParam); + + //$bookingCreate['status'] = 'success'; + //$bookingCreate['data']['id'] = 16; + + if ($bookingCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking could not be made')); + } + + //INSERT CONTACT DATA + $bookingContactCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'name' => $params['contactInfo']['name'], + 'surname' => $params['contactInfo']['surName'], + 'phone_code' => $params['contactInfo']['phoneCode'], + 'phone_number' => $params['contactInfo']['phoneNumber'], + 'email' => $params['contactInfo']['email'], + 'note' => fillOnUndefined($params['contactInfo'], 'note'), + 'language_code' => fillOnUndefined($params['contactInfo'], 'language_code', 'en'), + 'invoice_request' => fillOnUndefined($params['contactInfo'], 'invoice_request') ? 1 : null, + 'status' => 1 + ]; + + foreach ($bannedCheckParameter as $checkParameter) { + if (isset($bookingContactCreateParam[$checkParameter])) { + foreach ($bannedCharacters as $bannedCharacter) { + if (str_contains($bookingContactCreateParam[$checkParameter], $bannedCharacter)) { + throw new ApiErrorException(lang('Booking could not be made! - Contact')); + } + } + } + } + + $bookingContactCreate = $this->bookingContactService->create($bookingContactCreateParam); + //$bookingContactCreate['status'] = 'success'; + + if ($bookingContactCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Contact could not be made')); + } + + //INSERT ADDON DATA + foreach ($bookingAddon as $propertyAddonId => $propertyAddon) { + + $bookingAddonCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'property_channel_addon_id' => $propertyAddon['property_channel_addon_id'], + 'count' => $propertyAddon['count'], + 'amount' => $propertyAddon['amount'], + 'total' => $propertyAddon['total'], + 'currency_code' => $propertyAddon['currency_code'], + 'attribute' => !empty($propertyAddon['attribute']) ? json_encode($propertyAddon['attribute']) : null, + ]; + + $bookingAddonCreate = $this->bookingAddonService->create($bookingAddonCreateParam); + //$bookingRoomCreate['status'] = 'success'; + //$bookingRoomCreate['data']['id'] = 1; + + if ($bookingAddonCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Addon could not be made')); + } + + } + + + //INSERT ROOM DATA + foreach ($roomInfo as $roomOrder => $room) { + + $rateKeyCodeDecoded = $this->rateKeyCodeDecode($searchCacheRoomPrices[$room['rateKey']]['rateKeyCode']); + + $roomPrice = $searchCacheRoomPrices[$room['rateKey']]['total']; + $roomPriceBeforeDiscount = $searchCacheRoomPrices[$room['rateKey']]['total']; + $roomDiscountAmount = 0; + if (!empty($couponCodeData)) { + if ($couponCodeData['type'] == 'PER') { + $roomDiscountAmount = ($roomPriceBeforeDiscount * $couponCodeData['value'] / 100); + } elseif ($couponCodeData['type'] == 'FIX') { + $roomDiscountAmount = $couponCodeData['value']; + } + $roomPrice = $roomPriceBeforeDiscount - $roomDiscountAmount; + } + + $bookingRoomCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'room_order_number' => ($roomOrder + 1), + 'occupancy_code' => $room['occupancyCode'], + 'checkin_date' => $searchCacheData['requestParams']['date']['checkIn'], + 'checkout_date' => $searchCacheData['requestParams']['date']['checkOut'], + 'rate_key' => $room['rateKey'], + 'rate_key_code' => $searchCacheRoomPrices[$room['rateKey']]['rateKeyCode'], + 'availability_id' => $rateKeyCodeDecoded['availabilityTypeId'], + 'availability_code' => $searchCacheRoomPrices[$room['rateKey']]['availability']['code'], + 'room_id' => $rateKeyCodeDecoded['roomId'], + 'room_name' => $searchCacheRoomPrices[$room['rateKey']]['room']['name'], + 'room_rate_mapping_id' => $rateKeyCodeDecoded['rateMappingId'], + 'room_rate_name' => $searchCacheRoomPrices[$room['rateKey']]['rate']['name'], + 'cancellation_policy' => json_encode($searchCacheRoomPrices[$room['rateKey']]['cancellationPolicy']), + 'payment_type_code' => $searchCacheRoomPrices[$room['rateKey']]['bookingPaymentType']['code'], + 'daily_amount' => json_encode($searchCacheRoomPrices[$room['rateKey']]['policyPriceDaily']), + 'rate_detail' => json_encode($searchCacheRoomPrices[$room['rateKey']]), + 'amount' => $roomPriceBeforeDiscount, + 'discount_amount' => $roomDiscountAmount, + 'total' => $roomPrice, + 'currency_code' => $searchCacheRoomPrices[$room['rateKey']]['currency'], + 'status' => 1 + ]; + + $bookingRoomCreate = $this->bookingRoomService->create($bookingRoomCreateParam); + //$bookingRoomCreate['status'] = 'success'; + //$bookingRoomCreate['data']['id'] = 1; + + if ($bookingRoomCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Room could not be made')); + } + + foreach ($room['paxes'] as $roomPax) { + + $bookingRoomPaxParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'booking_room_id' => $bookingRoomCreate['data']['id'], + 'type' => fillOnUndefined($roomPax, 'type'), + 'name' => fillOnUndefined($roomPax, 'name'), + 'surname' => fillOnUndefined($roomPax, 'surName'), + 'gender' => fillOnUndefined($roomPax, 'gender'), + 'citizen' => fillOnUndefined($roomPax, 'citizen'), + 'birth_date' => fillOnUndefined($roomPax, 'birthDate'), + ]; + + foreach ($bannedCheckParameter as $checkParameter) { + if (isset($bookingRoomPaxParam[$checkParameter])) { + foreach ($bannedCharacters as $bannedCharacter) { + if (str_contains($bookingRoomPaxParam[$checkParameter], $bannedCharacter)) { + throw new ApiErrorException(lang('Booking could not be made! - Pax')); + } + } + } + } + + $bookingRoomPaxCreate = $this->bookingRoomPaxService->create($bookingRoomPaxParam); + //$bookingRoomCreate['status'] = 'success'; + //$bookingRoomCreate['data']['id'] = 1; + + if ($bookingRoomPaxCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Room Pax could not be made')); + } + + + } + + } + + + } else { + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'booking_code', 'condition' => '=', 'value' => $params['bookingCode']] + ], + 'with' => ['bookingPayment', 'bookingRoom', 'bookingAddon'], + 'firstRow' => true + ]; + + $bookingCreate = $this->bookingService->select($bookingDetailParam); + + if ($bookingCreate['status'] != 'success' || empty($bookingCreate['data'])) { + throw new ApiErrorException(lang('Booking not found')); + } + + $bookingDetail = $bookingCreate['data']; + + + //DELETE OLD ADDON DATA + if (!empty(pickItemFromArray('id', $bookingDetail['booking_addon']))) { + $this->bookingAddonService->delete(pickItemFromArray('id', $bookingDetail['booking_addon'])); + } + + + //INSERT ADDON DATA + foreach ($bookingAddon as $propertyAddonId => $propertyAddon) { + + $bookingAddonCreateParam = [ + 'booking_id' => $bookingDetail['id'], + 'property_channel_addon_id' => $propertyAddon['property_channel_addon_id'], + 'count' => $propertyAddon['count'], + 'amount' => $propertyAddon['amount'], + 'total' => $propertyAddon['total'], + 'currency_code' => $propertyAddon['currency_code'], + 'attribute' => !empty($propertyAddon['attribute']) ? json_encode($propertyAddon['attribute']) : null, + ]; + + $bookingAddonCreate = $this->bookingAddonService->create($bookingAddonCreateParam); + + if ($bookingAddonCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Addon could not be made')); + } + + } + + $bookingCode = $bookingCreate['data']['booking_code']; + $bookingStatus = in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) ? 1 : 2; + + $totalRoomsPriceBeforeDiscount = $totalRoomsPrice; + $bookingDiscountAmount = 0; + if (!empty($couponCodeData)) { + if ($couponCodeData['type'] == 'PER') { + $bookingDiscountAmount = ($totalRoomsPriceBeforeDiscount * $couponCodeData['value'] / 100); + } elseif ($couponCodeData['type'] == 'FIX') { + $bookingDiscountAmount = $couponCodeData['value']; + } + $totalRoomsPrice = $totalRoomsPrice - $bookingDiscountAmount; + } + + $bookingTotalAmount = $totalRoomsPrice + $totalAddonPrice; + + + $bookingDetailUpdate = [ + 'room_amount' => $totalRoomsPriceBeforeDiscount, + 'addon_amount' => $totalAddonPrice, + 'total' => $bookingTotalAmount, + ]; + + $this->bookingService->update($bookingDetail['id'], $bookingDetailUpdate); + + + } + + //Eğer kredi kartı ise + $initializePayment = []; + if (!in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN'])) { + + $isDirectPayment = true; + + //TODO: validator + $params['paymentInfo']['creditCard']['cardExpireMonth'] = strlen($params['paymentInfo']['creditCard']['cardExpireMonth']) == 2 ? $params['paymentInfo']['creditCard']['cardExpireMonth'] : str_pad($params['paymentInfo']['creditCard']['cardExpireMonth'], 2, 0, STR_PAD_LEFT); + + $initializePaymentParam = [ + 'propertyId' => $propertyId, + 'orderId' => $bookingCode, + 'installment' => isset($params['paymentInfo']['creditCard']['installment']) ? $params['paymentInfo']['creditCard']['installment'] : 1, + 'amount' => (double)$bookingTotalAmount, + 'currency' => $currencyCode, + 'type' => 'BKG', + 'responseUrl' => $params['paymentInfo']['responseUrl'], + 'creditCard' => [ + 'name' => $params['paymentInfo']['creditCard']['cardHolder'], + 'number' => $params['paymentInfo']['creditCard']['cardNumber'], + 'month' => $params['paymentInfo']['creditCard']['cardExpireMonth'], + 'year' => $params['paymentInfo']['creditCard']['cardExpireYear'], + 'cvv' => $params['paymentInfo']['creditCard']['cardCvv'], + ] + ]; + + + $initializePayment = $this->propertyPaymentService->initializePayment($initializePaymentParam); + + if (!$initializePayment['status']) { + Log::error($initializePayment); + throw new ApiErrorException(lang('Initialize Payment could not be made')); + } + + if (isset($initializePayment['data']['redirectUrl'])) { + $isDirectPayment = false; + } + } + + //Eğer direk ödeme ile para çekilip işlem yapılmış ise + if (isset($isDirectPayment) && $isDirectPayment) { + $bookingStatus = 1; + $this->bookingService->update($bookingCreate['data']['id'], ['status' => 1]); + } + + $bookingPaymentCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'payment_code' => isset($initializePayment['data']['paymentCode']) ? $initializePayment['data']['paymentCode'] : null, + 'payment_type_code' => $params['paymentInfo']['paymentCode'], + 'total' => in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || ($bookingStatus == 1 && $isDirectPayment) ? $bookingTotalAmount : null, + 'currency_code' => in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || ($bookingStatus == 1 && $isDirectPayment) ? $currencyCode : null, + 'status' => (isset($isDirectPayment) && $isDirectPayment) ? 1 : 2 + ]; + + $bookingPaymentCreate = $this->bookingPaymentService->create($bookingPaymentCreateParam); + //$bookingContactCreate['status'] = 'success'; + + if ($bookingPaymentCreate['status'] != 'success') { + throw new ApiErrorException(lang('Booking Payment could not be made')); + } + + //Buradan eksiltme transaction ları başlatılacak ki ok ise düşsün + $roomAvailabilityStatus = true; + foreach ($roomAvailabilityKeyGroup as $roomAvailabilityKey => $roomAvailability) { + + if ($roomAvailabilityKey == 1) { + + foreach ($roomAvailability as $roomKey => $room) { + + foreach ($dateByDay as $day) { + + $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability + ->where('date', $day) + ->where('property_room_id', $room['detail']['roomId']) + ->where('availability_type_id', $room['detail']['availabilityTypeId']) + ->where('availability', '>=', $room['count']) + ->where('status', 1) + ->first(); + + if (is_null($roomAndRoomRateAvailability)) { + $roomAvailabilityStatus = false; + break 3; + } + + //Kontenjan Azaltma + //Otelde öde ise düşürülecek kontenjan + if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || $isDirectPayment) { + $roomNewAvailability = $roomAndRoomRateAvailability['availability'] - $room['count']; + $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailability['id'], ['availability' => $roomNewAvailability]); + + //Connected Room Case + if ($roomAndRoomRateAvailability['room_detail']['is_connected_room'] && $roomAndRoomRateAvailability['room_detail']['is_connected_room_availability']) { + + foreach ($roomAndRoomRateAvailability['room_detail']['property_room_connected'] as $connectedRoom) { + + $roomAndRoomRateAvailabilityConnected = $propertyRoomAndRoomRateAvailability + ->where('date', $day) + ->where('property_room_id', $connectedRoom['connected_room_id']) + ->where('availability_type_id', $room['detail']['availabilityTypeId']) + ->where('availability', '>=', $room['count']) + ->where('status', 1) + ->first(); + + if (is_null($roomAndRoomRateAvailabilityConnected)) { + continue; + } + + $roomNewAvailabilityConnected = $roomAndRoomRateAvailabilityConnected['availability'] - $room['count']; + $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailabilityConnected['id'], ['availability' => $roomNewAvailabilityConnected]); + } + } + //Connected Room Case + + } + } + + } + + //Connected Room Case + $roomAvailabilityUpdateForConnectedRoomParams = [ + 'property_id' => $propertyId, + 'channel_id' => $this->channelId, + 'availability_type_id' => [$roomAvailabilityKey], + 'startDate' => reset($dateByDay), + 'endDate' => last($dateByDay), + 'user_id' => fillOnUndefined($params, "user_id", 0) + ]; + + $this->propertyRoomAvailabilityService->roomAvailabilityUpdateForConnectedRooms($roomAvailabilityUpdateForConnectedRoomParams); + //Connected Room Case + + } else { + + foreach ($roomAvailability as $roomKey => $roomRateMapping) { + + foreach ($roomRateMapping as $roomRateMappingKey => $roomRate) { + + foreach ($dateByDay as $day) { + + $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability + ->where('date', $day) + ->where('property_room_id', $roomRate['detail']['roomId']) + ->where('availability_type_id', $roomRate['detail']['availabilityTypeId']) + ->where('room_rate_mapping_id', $roomRate['detail']['rateMappingId']) + ->where('channel_id', $roomRate['detail']['channelId']) + ->where('availability', '>=', $roomRate['count']) + ->where('status', 1) + ->first(); + + if (is_null($roomAndRoomRateAvailability)) { + $roomAvailabilityStatus = false; + break 4; + } + + //Kontenjan Azaltma + //Otelde öde ise düşürülecek kontenjan + if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || $isDirectPayment) { + //TODO: Garantili kontenjan ve limitlide havuzdan da azaltılmalı + $roomNewAvailability = $roomAndRoomRateAvailability['availability'] - $roomRate['count']; + $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailability['id'], ['availability' => $roomNewAvailability]); + } + + } + + } + + } + + } + + } + + if (!$roomAvailabilityStatus) { + throw new ApiErrorException(lang('One or more of the requested rooms are not available')); + } + //BOOKING FINISHED + + + $responseData = [ + 'bookingCode' => $bookingCode, + 'total' => $bookingTotalAmount, + 'currency' => $currencyCode, + 'bookingStatus' => $bookingStatus, + 'bookingStatusText' => $bookingStatus == 1 ? 'Booking' : 'PreBooking', + + ]; + + //TODO: Eğer Booking yapılmış ise + if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || ($bookingStatus == 1 && isset($isDirectPayment) && $isDirectPayment)) { + + $mailParams = ['booking_id' => $bookingCreate['data']['id']]; + $this->newBookingMailService->process($mailParams); + + //PUSH NOTIFICATION + $newBookingNotificationParam = ['booking_id' => $bookingCreate['data']['id']]; + $this->notificationService->sendNewBookingNotification($newBookingNotificationParam); + //PUSH NOTIFICATION + + + //PUSH CHANNEL MANAGER QUEUE + //if ($bookingCreate['data']['channel_id'] == 1) { + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingCreate['data']['property_id']], + //['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $channelPropertyData) { + + //Trivago + if ($channelPropertyData['channel_manager_id'] == 11 && !isset($params['extraParam']['trv_reference'])) { + continue; + } + + //Hyperguest + if ($channelPropertyData['channel_manager_id'] != 10 && !is_null($channelPropertyData['channel_manager_property_id'])) { + continue; + } + + if(in_array($channelPropertyData['channel_manager_id'],[13])) { + continue; + } + + $channelManagerBookingCreateParam = [ + 'property_id' => $bookingCreate['data']['property_id'], + 'booking_id' => $bookingCreate['data']['id'], + 'channel_manager_id' => $channelPropertyData['channel_manager_id'], + 'type' => 'Booking', + ]; + + $channelManagerBookingCreate = $this->channelManagerBookingService->create($channelManagerBookingCreateParam); + + } + + } + + //} + //PUSH CHANNEL MANAGER QUEUE + + + } + + //Eğer kredi kartı ise ve ödeme başlamış ise + if (!empty($initializePayment)) { + $responseData['paymentCode'] = $initializePayment['data']['paymentCode']; + if (isset($initializePayment['data']['redirectUrl'])) { + $responseData['redirectUrl'] = $initializePayment['data']['redirectUrl']; + } + } + + //Pay at Hotel - Info Store + if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) && isset($params['paymentInfo']['creditCard']) && !empty($params['paymentInfo']['creditCard']['cardNumber'])) { + + $creditCard = $params['paymentInfo']['creditCard']; + + $cardNumber = $creditCard['cardNumber']; + $cardLengthSize = ceil(strlen($cardNumber) / 4); + + $cardNumberParse = []; + for ($i = 0; $i < $cardLengthSize; $i++) { + $cardNumberParse['cc'][] = Crypt::encrypt(mb_substr($cardNumber, $i * 4, 4)); + } + + $cardNumberParse['cm'] = Crypt::encrypt($creditCard['cardExpireMonth']); + $cardNumberParse['cy'] = Crypt::encrypt(mb_substr($creditCard['cardExpireYear'], -2, 2)); + $cardNumberParse['cv'] = Crypt::encrypt($creditCard['cardCvv']); + $cardNumberParse['ch'] = Crypt::encrypt($creditCard['cardHolder']); + + + foreach ($cardNumberParse as $type => $value) { + + $bookingPaymentDataCreateParam = []; + + if ($type == 'cc') { + foreach ($value as $cardValue) { + $bookingPaymentDataCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'type' => $type, + 'data' => $cardValue, + ]; + $this->bookingPaymentService->createPaymentData($bookingPaymentDataCreateParam); + } + } else { + $bookingPaymentDataCreateParam = [ + 'booking_id' => $bookingCreate['data']['id'], + 'type' => $type, + 'data' => $value, + ]; + + $this->bookingPaymentService->createPaymentData($bookingPaymentDataCreateParam); + } + + } + + } + //Pay at Hotel - Info Store + + + //Booking Engine Search Update + $bookingEngineSearchParam = [ + 'criteria' => [ + ['field' => 'search_key', 'condition' => '=', 'value' => $params['searchKey']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $bookingEngineSearch = $this->propertyBookingEngineSearchService->select($bookingEngineSearchParam); + + if ($bookingEngineSearch['status'] == 'success' && !empty($bookingEngineSearch['data'])) { + + $bookingEngineSearchUpdateParam = [ + 'booking_code' => $bookingCode, + 'amount' => $bookingTotalAmount, + 'status' => 2, + ]; + + if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || ($bookingStatus == 1 && isset($isDirectPayment) && $isDirectPayment)) { + $bookingEngineSearchUpdateParam['status'] = 3; + } + + $bookingEngineSearchUpdate = $this->propertyBookingEngineSearchService->update($bookingEngineSearch['data']['id'], $bookingEngineSearchUpdateParam); + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function bookingConfirm(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $bookingCode = $params['bookingCode']; + $paymentCode = $params['paymentCode']; + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode] + ], + 'with' => ['property', 'bookingPayment', 'bookingRoom'], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) { + throw new ApiErrorException(lang('Booking not found')); + } + + $bookingDetail = $bookingDetail['data']; + + $paymentDetailParam = [ + 'criteria' => [ + ['field' => 'code', 'condition' => '=', 'value' => $paymentCode] + ], + 'firstRow' => true + ]; + + $paymentDetail = $this->propertyPaymentService->selectPaymentTransaction($paymentDetailParam); + + if ($paymentDetail['status'] != 'success' || empty($paymentDetail['data'])) { + throw new ApiErrorException(lang('Payment not found')); + } + + $paymentDetail = $paymentDetail['data']; + + if ($paymentDetail['status'] != 1) { + throw new ApiErrorException('Payment not confirmed'); + } + + if ($bookingDetail['status'] == 1) { + throw new ApiErrorException('Previously approved transaction'); + } + + + DB::beginTransaction(); + + + //Booking Confirm + $this->bookingService->update($bookingDetail['id'], ['status' => 1]); + + + //Availability Change + if ($bookingDetail['status'] == 2) { + + $propertyRoomAndRoomRateAvailabilityParam = [ + 'property_id' => $bookingDetail['property_id'], + 'checkIn' => $bookingDetail['checkin_date'], + 'checkOut' => $bookingDetail['checkout_date'], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->getPropertyRoomAndRoomRateAvailability($propertyRoomAndRoomRateAvailabilityParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success' || empty($getPropertyRoomAndRoomRateAvailability['data'])) { + throw new ApiErrorException(lang('Room or rooms not available.')); + } + + $propertyRoomAndRoomRateAvailability = collect($getPropertyRoomAndRoomRateAvailability['data']); + + + $roomAvailabilityStatus = true; + foreach ($bookingDetail['booking_room'] as $room) { + + $dateByDay = $this->getDateByDay(['checkIn' => $room['checkin_date'], 'checkOut' => $room['checkout_date']]); + + foreach ($dateByDay as $day) { + + $roomRateDetail = json_decode($room['rate_detail'], 1); + $rateKeyCodeDecoded = $this->rateKeyCodeDecode($roomRateDetail['rateKeyCode']); + + if ($rateKeyCodeDecoded['availabilityTypeId'] == 1) { + + $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability + ->where('date', $day) + ->where('property_room_id', $rateKeyCodeDecoded['roomId']) + ->where('availability_type_id', $rateKeyCodeDecoded['availabilityTypeId']) + ->where('availability', '>=', 1) + ->where('status', 1) + ->first(); + + if (is_null($roomAndRoomRateAvailability)) { + $roomAvailabilityStatus = false; + break 2; + } + + //Kontenjan Azaltma + $roomNewAvailability = $roomAndRoomRateAvailability['availability'] - 1; + $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailability['id'], ['availability' => $roomNewAvailability]); + + //Connected Room Case + if ($roomAndRoomRateAvailability['room_detail']['is_connected_room'] && $roomAndRoomRateAvailability['room_detail']['is_connected_room_availability']) { + + foreach ($roomAndRoomRateAvailability['room_detail']['property_room_connected'] as $connectedRoom) { + + $roomAndRoomRateAvailabilityConnected = $propertyRoomAndRoomRateAvailability + ->where('date', $day) + ->where('property_room_id', $connectedRoom['connected_room_id']) + ->where('availability_type_id', $rateKeyCodeDecoded['availabilityTypeId']) + ->where('availability', '>=', 1) + ->where('status', 1) + ->first(); + + if (is_null($roomAndRoomRateAvailabilityConnected)) { + continue; + } + + $roomNewAvailabilityConnected = $roomAndRoomRateAvailabilityConnected['availability'] - 1; + $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailabilityConnected['id'], ['availability' => $roomNewAvailabilityConnected]); + } + } + //Connected Room Case + + } else { + + $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability + ->where('date', $day) + ->where('property_room_id', $rateKeyCodeDecoded['roomId']) + ->where('availability_type_id', $rateKeyCodeDecoded['availabilityTypeId']) + ->where('room_rate_mapping_id', $rateKeyCodeDecoded['rateMappingId']) + ->where('channel_id', $rateKeyCodeDecoded['channelId']) + ->where('availability', '>=', 1) + ->where('status', 1) + ->first(); + + + if (is_null($roomAndRoomRateAvailability)) { + $roomAvailabilityStatus = false; + break 2; + } + + //TODO: Garantili kontenjan ve limitlide havuzdan da azaltılmalı + //Kontenjan Azaltma + $roomNewAvailability = $roomAndRoomRateAvailability['availability'] - 1; + $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailability['id'], ['availability' => $roomNewAvailability]); + + + } + + } + + } + + //Connected Room Case + $roomAvailabilityUpdateForConnectedRoomParams = [ + 'property_id' => $bookingDetail['property_id'], + 'channel_id' => $bookingDetail['channel_id'], + 'availability_type_id' => [1], + 'startDate' => reset($dateByDay), + 'endDate' => last($dateByDay), + 'user_id' => fillOnUndefined($params, "user_id", 0) + ]; + + $this->propertyRoomAvailabilityService->roomAvailabilityUpdateForConnectedRooms($roomAvailabilityUpdateForConnectedRoomParams); + //Connected Room Case + + if (!$roomAvailabilityStatus) { + throw new ApiErrorException(lang('One or more of the requested rooms are not available')); + } + + } + + //Booking Payment Update + $bookingPaymentUpdateParam = [ + 'status' => 1, + 'total' => $paymentDetail['amount'], + 'currency_code' => $paymentDetail['currency'], + ]; + + $this->bookingPaymentService->update($bookingDetail['booking_payment']['id'], $bookingPaymentUpdateParam); + + + $mailParams = ['booking_id' => $bookingDetail['id']]; + $this->newBookingMailService->process($mailParams); + + //PUSH NOTIFICATION + $newBookingNotificationParam = ['booking_id' => $bookingDetail['id']]; + $this->notificationService->sendNewBookingNotification($newBookingNotificationParam); + //PUSH NOTIFICATION + + //PUSH CHANNEL MANAGER QUEUE + //if ($bookingDetail['channel_id'] == 1) { + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingDetail['property_id']], + //['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $channelPropertyData) { + + //Trivago + if ($channelPropertyData['channel_manager_id'] == 11 && !isset($params['extraParam']['trv_reference'])) { + continue; + } + + //Hyperguest + if ($channelPropertyData['channel_manager_id'] != 10 && !is_null($channelPropertyData['channel_manager_property_id'])) { + continue; + } + + //Yandex + if(in_array($channelPropertyData['channel_manager_id'],[13])) { + continue; + } + + $channelManagerBookingCreateParam = [ + 'property_id' => $bookingDetail['property_id'], + 'booking_id' => $bookingDetail['id'], + 'channel_manager_id' => $channelPropertyData['channel_manager_id'], + 'type' => 'Booking', + ]; + + $channelManagerBookingCreate = $this->channelManagerBookingService->create($channelManagerBookingCreateParam); + + } + + } + + //} + //PUSH CHANNEL MANAGER QUEUE + + + //Booking Engine Search Update + //if ($bookingDetail['channel_id'] == 1) { + + $bookingEngineSearchParam = [ + 'criteria' => [ + ['field' => 'search_key', 'condition' => '=', 'value' => $bookingDetail['search_key']], + ['field' => 'status', 'condition' => '=', 'value' => 2], + ], + 'firstRow' => true + ]; + + $bookingEngineSearch = $this->propertyBookingEngineSearchService->select($bookingEngineSearchParam); + + if ($bookingEngineSearch['status'] == 'success' && !empty($bookingEngineSearch['data'])) { + + $bookingEngineSearchUpdateParam = [ + 'booking_code' => $bookingDetail['booking_code'], + 'amount' => $bookingDetail['total'], + 'currency_code' => $bookingDetail['currency_code'], + 'status' => 3, + ]; + + $bookingEngineSearchUpdate = $this->propertyBookingEngineSearchService->update($bookingEngineSearch['data']['id'], $bookingEngineSearchUpdateParam); + + } + //} + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function bookingDetail(Request $request, $bookingCode) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode], + //['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId],//TODO: Kontrol gerekli! + ], + 'with' => [ + 'bookingPayment.paymentTypeCode', 'bookingRoom.roomPax.paxCountry', 'bookingRoom.roomRateMapping.propertyRoomRate', 'bookingRoom.roomRateMapping.propertyRoom', 'bookingContact', + 'bookingProperty.propertyContact', 'bookingProperty.propertyBrand', 'bookingProperty.propertyAdditionalInfos.propertyAdditionalInfoKey', + 'bookingAddon.propertyChannelAddon.propertyAddon.fact', 'bookingChannel','propertyChannelMapping.channelBookingPaymentType', + 'bookingRoom.roomRateMapping.propertyRoom.propertyRoomBedGroup.propertyRoomBedType', 'bookingRoom.roomRateMapping.propertyRoom.smokingPreference.propertyFact', + 'bookingRoom.propertyRoomBed', 'bookingRoom.smokingFact','propertyBookingEngineSearch' + ], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) { + throw new ApiErrorException(lang('Booking not found')); + } + + $bookingDetail['data']['booking_property']['checkin_time'] = '14:00'; + $bookingDetail['data']['booking_property']['checkout_time'] = '12:00'; + + if (!empty($bookingDetail['data']['booking_property']['property_additional_infos'])) { + $propertyAdditionalInfos = collect($bookingDetail['data']['booking_property']['property_additional_infos']); + + $checkinTimeData = $propertyAdditionalInfos->where('property_additional_info_key.additional_info_key', 'checkin_time')->first(); + $bookingDetail['data']['booking_property']['checkin_time'] = isset($checkinTimeData['value']) ? $checkinTimeData['value'] : '14:00'; + + $checkoutTimeData = $propertyAdditionalInfos->where('property_additional_info_key.additional_info_key', 'checkout_time')->first(); + $bookingDetail['data']['booking_property']['checkout_time'] = isset($checkinTimeData['value']) ? $checkoutTimeData['value'] : '12:00'; + } + unset($bookingDetail['data']['booking_property']['property_additional_infos']); + + + //BOOKING ADDON + $bookingAddons = $bookingDetail['data']['booking_addon']; + unset($bookingDetail['data']['booking_addon']); + + $bookingAddonList = []; + foreach ($bookingAddons as $bookingAddon) { + + $bookingAddonAttributeList = []; + $isHasAttribute = false; + $attributeArray = []; + if (!empty($bookingAddon['property_channel_addon']['property_addon']['attributeArray'])) { + $isHasAttribute = true; + $attributeArray = $bookingAddon['property_channel_addon']['property_addon']['attributeArray']; + $bookingAddonAttributes = json_decode($bookingAddon['attribute'], 1); + if (!is_null($bookingAddonAttributes)) { + foreach ($bookingAddonAttributes as $key => $bookingAddonAttribute) { + foreach ($bookingAddonAttribute as $bookingAddonAttributeKey => $bookingAddonAttributeValue) { + + if (isset($bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey])) { + $bookingAddonAttributeList[$key][$bookingAddonAttributeKey] = [ + 'name' => $bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey]['name'], + 'language_key' => $bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey]['language_key'], + 'value' => $bookingAddonAttributeValue, + ]; + } + + } + } + } + } + + $bookingAddonList[] = [ + 'id' => $bookingAddon['id'], + 'booking_id' => $bookingAddon['booking_id'], + 'property_channel_addon_id' => $bookingAddon['property_channel_addon_id'], + 'count' => $bookingAddon['count'], + 'amount' => $bookingAddon['amount'], + 'total' => $bookingAddon['total'], + 'currency_code' => $bookingAddon['currency_code'], + 'name' => $bookingAddon['property_channel_addon']['property_addon']['fact']['name'], + 'title' => $bookingAddon['property_channel_addon']['title'], + 'language_key' => $bookingAddon['property_channel_addon']['property_addon']['fact']['language_key'], + 'icon' => $bookingAddon['property_channel_addon']['property_addon']['fact']['icon'], + 'isHasAttribute' => $isHasAttribute, + 'attributeArray' => $attributeArray, + 'attribute' => $bookingAddonAttributeList + ]; + + } + + $bookingDetail['data']['booking_addon'] = $bookingAddonList; + //BOOKING ADDON + + //DAILY AMOUNT BY ROOM + $hasNonRefundableRoom = false; + foreach ($bookingDetail['data']['booking_room'] as $roomKey => $roomDetail) { + + if (!empty($roomDetail['room_rate_mapping']['property_room'])) { + $bookingDetail['data']['booking_room'][$roomKey]['room_name'] = $roomDetail['room_rate_mapping']['property_room']['name']; + } + + if (!empty($roomDetail['room_rate_mapping']['property_room_rate'])) { + $bookingDetail['data']['booking_room'][$roomKey]['room_rate_name'] = $roomDetail['room_rate_mapping']['property_room_rate']['name']; + } + + $diffInDays = Carbon::parse($roomDetail['checkin_date'])->diffInDays(Carbon::parse($roomDetail['checkout_date'])); + $baseRateDaily = moneyDoubleFormatDecimal($roomDetail['total'] / $diffInDays); + + $roomDailyAmount = []; + if (!empty($roomDetail['daily_amount'])) { + $roomDetailDailyAmount = json_decode($roomDetail['daily_amount'], 1); + if (!empty($roomDetailDailyAmount) && isset($roomDetailDailyAmount)) { + foreach ($roomDetailDailyAmount as $roomRateAmount) { + $roomDailyAmount[] = [ + 'date' => $roomRateAmount['date'], + 'amount' => $roomRateAmount['amount'], + 'currency_code' => $roomRateAmount['currency_code'], + ]; + } + } + } + + if (empty($roomDailyAmount)) { + $roomRateDetail = json_decode($roomDetail['rate_detail'], 1); + if (!empty($roomRateDetail) && isset($roomRateDetail['days'])) { + foreach ($roomRateDetail['days'] as $roomRateDay => $roomRateAmount) { + $roomDailyAmount[] = [ + 'date' => Carbon::parse($roomRateDay)->toDateString(), + 'amount' => $roomRateAmount, + 'currency_code' => $roomDetail['currency_code'], + ]; + } + } + } + + if (empty($roomDailyAmount)) { + $currentDate = $roomDetail['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $roomDailyAmount[] = [ + 'date' => $currentDate, + 'amount' => $baseRateDaily, + 'currency_code' => $roomDetail['currency_code'], + ]; + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + } + + $bookingDetail['data']['booking_room'][$roomKey]['daily_amount'] = $roomDailyAmount; + + $extraParam = null; + if (!empty($roomDetail['extra_param'])) { + $extraParamDecode = json_decode($roomDetail['extra_param'], 1); + + foreach ($extraParamDecode as $extraParamKey => $extraParamValue) { + + $extraParamTitle = explode('_', $extraParamKey); + foreach ($extraParamTitle as $extraParamTitleKey => $extraParamTitleValue) { + $extraParamTitle[$extraParamTitleKey] = ucwords($extraParamTitleValue); + } + $extraParamTitle = implode(' ', $extraParamTitle); + + $extraParam[$extraParamKey] = [ + 'title' => $extraParamTitle, + 'value' => $extraParamValue, + ]; + } + + } + + $bookingDetail['data']['booking_room'][$roomKey]['extra_param'] = $extraParam; + $bookingDetail['data']['booking_room'][$roomKey]['occupancyFormatted'] = occupancyCodeFormatted($roomDetail['occupancy_code']); + + $cancellationPolicy = !empty($roomDetail['cancellation_policy']) ? json_decode($roomDetail['cancellation_policy'], 1) : null; + + if (is_array($cancellationPolicy) && isset($cancellationPolicy['isNonRefundable']) && $cancellationPolicy['isNonRefundable']) { + $hasNonRefundableRoom = true; + } + + + //property_room_bed_group + //unset($bookingDetail['data']['booking_room'][$roomKey]['room_rate_mapping']['property_room']['property_room_bed_group']); + $propertyRoomBedGroup = collect($roomDetail['room_rate_mapping']['property_room']['property_room_bed_group'])->groupBy('bed_group'); + $propertyRoomBedGroup = $propertyRoomBedGroup ? $propertyRoomBedGroup->toArray() : null; + $bookingDetail['data']['booking_room'][$roomKey]['room_rate_mapping']['property_room']['property_room_bed_group'] = $propertyRoomBedGroup; + + + $bookingDetail['data']['booking_room'][$roomKey]['property_room_bed_group'] = null; + if(isset($propertyRoomBedGroup[$bookingDetail['data']['booking_room'][$roomKey]['property_room_bed_group_id']])) { + $bookingDetail['data']['booking_room'][$roomKey]['property_room_bed_group'] = $propertyRoomBedGroup[$bookingDetail['data']['booking_room'][$roomKey]['property_room_bed_group_id']]; + } + + + } + //DAILY AMOUNT BY ROOM + + $bookingChannel = $bookingDetail['data']['booking_channel']; + unset($bookingDetail['data']['booking_channel']); + $bookingDetail['data']['booking_channel'] = [ + 'name' => $bookingChannel['name'], + 'logo' => $bookingChannel['logoUrl'], + 'channel_category_id' => $bookingChannel['channel_category_id'], + ]; + + $bookingContactExtraParam = null; + if (!empty($bookingDetail['data']['booking_contact']['extra_param'])) { + $bookingContactExtraParam = json_decode($bookingDetail['data']['booking_contact']['extra_param'], 1); + } + $bookingDetail['data']['booking_contact']['extra_param'] = $bookingContactExtraParam; + + + $bookingDetail = $bookingDetail['data']; + + $bookingDetail['isOnlineCancellation'] = false; + if ($bookingDetail['status'] == 1 && in_array($bookingDetail['booking_channel']['channel_category_id'], [2]) && !$hasNonRefundableRoom) { + $bookingDetail['isOnlineCancellation'] = false; + } + + if ($bookingDetail['status'] == 1 && in_array($bookingDetail['booking_channel']['channel_category_id'], [3]) && in_array($bookingDetail['booking_property']['country'], ['GE'])) { + $bookingDetail['isOnlineCancellation'] = false; + } + + $payAtHotelCreditCardCheck = collect($bookingDetail['property_channel_mapping']['channel_booking_payment_type'])->where('is_get_payment_data',1)->where('payment_type_id',2)->isNotEmpty(); + $bookingDetail['credit_card_required'] = $payAtHotelCreditCardCheck; + unset($bookingDetail['property_channel_mapping']); + + //Wholesaler MARKUP CALCULATE + $getChannelPropertyDetail = $this->getChannelPropertyDetail($this->channelId, $bookingDetail['property_id']); + if (!$getChannelPropertyDetail['status']) { + throw new ApiErrorException(lang('Property and Channel are not matched.')); + } + $getChannelPropertyDetail = $getChannelPropertyDetail['status'] ? $getChannelPropertyDetail['data'] : null; + + if ($getChannelPropertyDetail && in_array($getChannelPropertyDetail['channel']['channel_category_id'], [7])) { + + $channelCurrencyCode = fillOnUndefined($bookingDetail, 'channel_currency_code', $bookingDetail['currency_code']); + $channelCurrencyExchange = fillOnUndefined($bookingDetail, 'channel_currency_exchange', 1); + + $channelMarkup = 1; + if (!is_null($bookingDetail['channel_markup'])) { + $channelMarkup = (($bookingDetail['channel_markup'] + 100) / 100); + } + + foreach ($bookingDetail['booking_room'] as $roomKey => $room) { + + foreach ($room['daily_amount'] as $dailKey => $dailyAmount) { + $bookingDetail['booking_room'][$roomKey]['daily_amount'][$dailKey]['currency_code'] = $channelCurrencyCode; + $bookingDetail['booking_room'][$roomKey]['daily_amount'][$dailKey]['amount'] = moneyDoubleFormatDecimal($dailyAmount['amount'] * $channelMarkup * $channelCurrencyExchange); + } + + + $bookingDetail['booking_room'][$roomKey]['amount'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'][$roomKey]['daily_amount'])->sum('amount')); + $bookingDetail['booking_room'][$roomKey]['discount_amount'] = moneyDoubleFormatDecimal($room['discount_amount'] * $channelMarkup * $channelCurrencyExchange); + $bookingDetail['booking_room'][$roomKey]['total'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'][$roomKey]['daily_amount'])->sum('amount')); + $bookingDetail['booking_room'][$roomKey]['currency_code'] = $channelCurrencyCode; + + + unset($bookingDetail['booking_room'][$roomKey]['rate_detail']); + + } + + $bookingDetail['currency_code'] = $channelCurrencyCode; + $bookingDetail['room_amount'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'])->sum('amount')); + $bookingDetail['addon_amount'] = moneyDoubleFormatDecimal($bookingDetail['addon_amount'] * $channelMarkup * $channelCurrencyExchange); + $bookingDetail['discount_amount'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'])->sum('discount_amount')); + $bookingDetail['total'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'])->sum('amount')); + + + $bookingDetail['booking_payment']['currency_code'] = $channelCurrencyCode; + $bookingDetail['booking_payment']['total'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'])->sum('total')); + + + } + //Wholesaler MARKUP CALCULATE + + unset($bookingDetail['channel_markup']); + unset($bookingDetail['channel_currency_code']); + unset($bookingDetail['channel_currency_exchange']); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $bookingDetail]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function bookingUpdate(Request $request, $bookingCode) + { + + DB::beginTransaction(); + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode], + ], + 'with' => [ + 'bookingRoom.roomRateMapping.propertyRoom', + 'bookingRoom.roomRateMapping.propertyRoom.propertyRoomBedGroup.propertyRoomBedType', + 'bookingRoom.roomRateMapping.propertyRoom.smokingPreference.propertyFact', + 'bookingRoom.propertyRoomBed','bookingRoom.smokingFact' + ], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) { + throw new ApiErrorException(lang('Booking not found')); + } + + $bookingDetail = $bookingDetail['data']; + + if(fillOnUndefined($params,'propertyId') != fillOnUndefined($bookingDetail,'property_id')) { + throw new ApiErrorException('PropertyI ID is not matching.'); + } + + $bookingRoomUpdateParam = ['smoking_fact_id','property_room_bed_id','property_room_bed_group_id']; + if(isset($params['booking_room']) && isset($params['booking_room']['id'])) { + + $bookingRoomIds = collect($bookingDetail['booking_room'])->pluck('id'); + $bookingRoomIds = $bookingRoomIds ? $bookingRoomIds->toArray() : null; + + if(empty($bookingRoomIds)) { + throw new ApiErrorException('Booking Room ID not found.'); + } + + if(!in_array($params['booking_room']['id'],$bookingRoomIds )) { + throw new ApiErrorException('Booking Room ID not matching.'); + } + + $bookingRoomDetail = collect($bookingDetail['booking_room'])->where('id',$params['booking_room']['id'])->first(); + + foreach ($bookingRoomUpdateParam as $updateColumn) { + + //smoking_fact_id + if($updateColumn == 'smoking_fact_id' && isset($params['booking_room']['smoking_fact_id'])) { + $smokingPreferenceIds = collect($bookingRoomDetail['room_rate_mapping']['property_room']['smoking_preference'])->pluck('fact_id')->toArray(); + if(!in_array($params['booking_room']['smoking_fact_id'],$smokingPreferenceIds)) { + throw new ApiErrorException('smoking_fact_id: ID params not matching.'); + } + $this->bookingRoomService->update($params['booking_room']['id'], ['smoking_fact_id' => $params['booking_room']['smoking_fact_id']]); + } + + //property_room_bed_id + if($updateColumn == 'property_room_bed_id' && isset($params['booking_room']['property_room_bed_id'])) { + $propertyRoomBedGroupIds = collect($bookingRoomDetail['room_rate_mapping']['property_room']['property_room_bed_group'])->pluck('id')->toArray(); + if(!in_array($params['booking_room']['property_room_bed_id'],$propertyRoomBedGroupIds)) { + throw new ApiErrorException('property_room_bed_id: ID params not matching.'); + } + $this->bookingRoomService->update($params['booking_room']['id'], ['property_room_bed_id' => $params['booking_room']['property_room_bed_id']]); + } + + + //property_room_bed_group_id + if($updateColumn == 'property_room_bed_group_id' && isset($params['booking_room']['property_room_bed_group_id'])) { + $propertyRoomBedGroupIds = collect($bookingRoomDetail['room_rate_mapping']['property_room']['property_room_bed_group'])->pluck('bed_group')->toArray(); + + if(!in_array($params['booking_room']['property_room_bed_group_id'],$propertyRoomBedGroupIds)) { + throw new ApiErrorException('property_room_bed_group_id: ID params not matching.'); + } + $this->bookingRoomService->update($params['booking_room']['id'], ['property_room_bed_group_id' => $params['booking_room']['property_room_bed_group_id']]); + } + + + + } + + + } else { + throw new ApiErrorException('Property Room ID not found.'); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + + + + public function bookingPaymentInstallment(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $paymentData = []; + + $paymentData['isExchangeAmount'] = false; + + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $propertyId = $this->bookingEnginePropertyId; + $cardBinNumber = $params['cardBinNumber']; + $amount = $params['amount']; + $currency = $params['currency']; + + $paymentData['amount'] = $amount; + $paymentData['currency'] = $currency; + $paymentData['installment'] = []; + + $paymentType = $this->propertyPaymentService->getPaymentTypeMapping($propertyId, $currency, $cardBinNumber); + + if (is_null($paymentType['propertyPaymentMappingId'])) { + throw new ApiErrorException(lang('Payment Type not found')); + } + + if ($params['currency'] != $paymentType['propertyPaymentMappingCurrency']) { + $exchangeRate = $this->propertyPaymentService->getExchangeRates(); + + if (isset($exchangeRate[$params['currency'] . $paymentType['propertyPaymentMappingCurrency']])) { + $exchangeRateValue = $exchangeRate[$params['currency'] . $paymentType['propertyPaymentMappingCurrency']]; + } else { + throw new ApiErrorException(lang('Currency exchange value not found.')); + } + + $paymentData['isExchangeAmount'] = true; + + $paymentData['amount'] = moneyDoubleFormatDecimal($amount * $exchangeRateValue); + $paymentData['currency'] = $paymentType['propertyPaymentMappingCurrency']; + + $paymentData['base_amount'] = $amount; + $paymentData['base_currency'] = $currency; + $paymentData['exchangeRate'] = $exchangeRateValue; + + } + + if (!empty($paymentType['propertyPaymentMappingInstallment'])) { + + $amountBeforeInstallment = $amount; + if ($paymentData['isExchangeAmount']) { + $amountBeforeInstallment = $paymentData['amount']; + } + + foreach ($paymentType['propertyPaymentMappingInstallment'] as $installmentData) { + $paymentData['installment'][$installmentData['installment']] = [ + 'installment' => $installmentData['installment'], + 'commission' => $installmentData['commission'], + 'amount' => moneyDoubleFormatDecimal($amountBeforeInstallment + ($amountBeforeInstallment * $installmentData['commission'] / 100)) + ]; + + } + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $paymentData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function couponCodeCheck(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $couponData = []; + + $channelId = $this->channelId; + $propertyId = $this->bookingEnginePropertyId; + + $getChannelPropertyDetail = $this->getChannelPropertyDetail($this->channelId, $propertyId); + if (!$getChannelPropertyDetail['status']) { + throw new ApiErrorException(lang('Property and Channel are not matched.')); + } + + $getChannelPropertyDetail = $getChannelPropertyDetail['data']; + + $propertyChannelCouponCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['channel_id']], + ['field' => 'code', 'condition' => '=', 'value' => $params['code']], + ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::now()->toDateString()], + ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $propertyChannelCoupon = $this->propertyChannelCouponService->select($propertyChannelCouponCriteria); + + if ($propertyChannelCoupon['status'] == 'success') { + $propertyChannelCoupon = $propertyChannelCoupon['data']; + } + + if (empty($propertyChannelCoupon)) { + throw new ApiErrorException(lang('Code that has expired or is not available.')); + } + + $codeChecked = fillOnUndefined($propertyChannelCoupon,'checked', 0); $codeChecked++; + $this->propertyChannelCouponService->update($propertyChannelCoupon['id'], ['checked' => $codeChecked]); + + //Reservation Date Check + if (!is_null($propertyChannelCoupon['reservation_start_date']) && !is_null($propertyChannelCoupon['reservation_end_date'])) { + if (isset($params['checkIn']) && isset($params['checkOut'])) { + $reservationDateCheck = Carbon::parse($propertyChannelCoupon['reservation_start_date'])->lessThanOrEqualTo(Carbon::parse($params['checkIn'])) + && Carbon::parse($propertyChannelCoupon['reservation_end_date'])->greaterThanOrEqualTo(Carbon::parse($params['checkOut'])); + if (!$reservationDateCheck) { + throw new ApiErrorException(lang('The code is not within the reservation period entered.')); + } + } + } + + + $couponData = [ + 'code' => $propertyChannelCoupon['code'], + 'type' => $propertyChannelCoupon['type'], + 'value' => $propertyChannelCoupon['value'], + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $couponData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function couponCodeNotify(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $channelId = $this->channelId; + $propertyId = $this->bookingEnginePropertyId; + + $getChannelPropertyDetail = $this->getChannelPropertyDetail($this->channelId, $propertyId); + if (!$getChannelPropertyDetail['status']) { + throw new ApiErrorException(lang('Property and Channel are not matched.')); + } + + $getChannelPropertyDetail = $getChannelPropertyDetail['data']; + + $propertyChannelCouponCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['channel_id']], + //['field' => 'code', 'condition' => '=', 'value' => $params['code']], + ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::now()->toDateString()], + ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_notify', 'condition' => '=', 'value' => 1], + ], + "orderBy" => [ + ["field" => "id", "value" => "DESC"] + ], + 'firstRow' => true + ]; + //dd($propertyChannelCouponCriteria); + + $propertyChannelCoupon = $this->propertyChannelCouponService->select($propertyChannelCouponCriteria); + + if ($propertyChannelCoupon['status'] == 'success') { + $propertyChannelCoupon = $propertyChannelCoupon['data']; + } + + if (empty($propertyChannelCoupon)) { + throw new ApiErrorException(lang('No active code to notify was found')); + } + + //Reservation Date Check + if (!is_null($propertyChannelCoupon['reservation_start_date']) && !is_null($propertyChannelCoupon['reservation_end_date'])) { + if (isset($params['checkIn']) && isset($params['checkOut'])) { + $reservationDateCheck = Carbon::parse($propertyChannelCoupon['reservation_start_date'])->lessThanOrEqualTo(Carbon::parse($params['checkIn'])) + && Carbon::parse($propertyChannelCoupon['reservation_end_date'])->greaterThanOrEqualTo(Carbon::parse($params['checkOut'])); + if (!$reservationDateCheck) { + throw new ApiErrorException(lang('The code is not within the reservation period entered.')); + } + } + } + + $couponData = [ + 'code' => $propertyChannelCoupon['code'], + 'type' => $propertyChannelCoupon['type'], + 'value' => $propertyChannelCoupon['value'], + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $couponData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function bookingAddonAttribute(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $channelId = $this->channelId; + $propertyId = $this->bookingEnginePropertyId; + $bookingCode = $params['bookingCode']; + $bookingAddonId = $params['bookingAddonId']; + + //TODO: Validator, + + $getChannelPropertyDetail = $this->getChannelPropertyDetail($channelId, $propertyId); + if (!$getChannelPropertyDetail['status']) { + throw new ApiErrorException(lang('Property and Channel are not matched.')); + } + + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode] + ], + 'with' => ['bookingAddon.propertyChannelAddon.propertyAddon.fact'], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) { + throw new ApiErrorException('Booking not found'); + } + + $bookingDetail = $bookingDetail['data']; + + if (empty($bookingDetail['booking_addon'])) { + throw new ApiErrorException('Booking Addon not found'); + } + + $bookingAddonCollect = collect($bookingDetail['booking_addon']); + + $selectedAddon = $bookingAddonCollect->where('id', $bookingAddonId)->first(); + + if (empty($selectedAddon)) { + throw new ApiErrorException('Booking Selected Addon not found'); + } + + $attributeArray = $selectedAddon['property_channel_addon']['property_addon']['attributeArray']; + + + $addonInfoAttribute = null; + if (isset($params['attribute'])) { + foreach ($params['attribute'] as $addonAttribute) { + if (count(array_intersect_key($addonAttribute, $attributeArray)) != count($addonAttribute)) { + throw new ApiErrorException('Additional product attribute values submitted do not match.'); + } + } + $addonInfoAttribute = $params['attribute']; + } + + + if ($selectedAddon['count'] != count($addonInfoAttribute)) { + throw new ApiErrorException('The number of additional items purchased and the number of additional information entered do not match'); + } + + $updateParam['attribute'] = null; + if (!is_null($addonInfoAttribute)) { + $updateParam['attribute'] = json_encode($addonInfoAttribute); + } + + $this->bookingAddonService->update($bookingAddonId, $updateParam); + + $bookingPropertyAddonUpdateMail = [ + 'propertyId' => $bookingDetail['property_id'], + 'bookingCode' => $bookingDetail['booking_code'], + 'addonLanguageKey' => $selectedAddon['property_channel_addon']['property_addon']['fact']['language_key'] + ]; + $this->mailer->onQueue('bookingPropertyAddonUpdateMail', new BookingPropertyAddonUpdateMail($bookingPropertyAddonUpdateMail)); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function bookingCancellation(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $confirmCodeGenerate = null; + $propertyBookingController = \Illuminate\Support\Facades\App::make("App\Http\Controllers\V1\PropertyBookingController"); + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $channelId = $this->channelId; + $propertyId = $this->bookingEnginePropertyId; + $bookingCode = $params['bookingCode']; + $confirmCode = fillOnUndefined($params, 'confirmCode'); + + $getChannelPropertyDetail = $this->getChannelPropertyDetail($channelId, $propertyId); + if (!$getChannelPropertyDetail['status']) { + throw new ApiErrorException(lang('Property and Channel are not matched.')); + } + + $getChannelPropertyDetail = $getChannelPropertyDetail['data']; + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode] + ], + 'with' => ['bookingChannel.propertyChannelCategory', 'bookingProperty', 'bookingRoom'], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success' || ($bookingDetail['status'] == 'success' && empty($bookingDetail['data']))) { + throw new ApiErrorException('Property and Booking Code are not matched.'); + } + + $bookingDetail = $bookingDetail['data']; + + if ($bookingDetail['status'] != 1) { + throw new ApiErrorException('Reservation status not available.'); + } + + + //Only GE Hotels + if ($bookingDetail['status'] == 1 && in_array($bookingDetail['booking_channel']['channel_category_id'], [3]) && in_array($bookingDetail['booking_property']['country'], ['GE'])) { + + + //Check Cancellation Policy + $isBookingRefundable = false; + foreach ($bookingDetail['booking_room'] as $bookingRoom) { + $cancellationPolicy = json_decode($bookingRoom['cancellation_policy'], 1); + + if (!empty($cancellationPolicy)) { + if ($cancellationPolicy['isNonRefundable']) { + break; + } + if ($cancellationPolicy['isFreeCancellation']) { + if (isset($cancellationPolicy['beforeArrivalDay'])) { + if (Carbon::now()->startOfDay()->lessThan(Carbon::parse($bookingRoom['checkin_date'])->subDays($cancellationPolicy['beforeArrivalDay'])->toDateString())) { + $isBookingRefundable = true; + } + } else { + $isBookingRefundable = true; + } + } + } + } + + if ($isBookingRefundable) { + + //Cancellation Process + $updateBookingParam = [ + 'locale' => $request->getLocale(), + 'property_id' => $bookingDetail['property_id'], + 'booking_id' => $bookingDetail['id'], + 'total' => $bookingDetail['total'], + 'currency_code' => $bookingDetail['currency_code'], + 'status' => 0 + ]; + + $request['requestParams'] = $updateBookingParam; + + $updateBooking = $propertyBookingController->updateBooking($request); + $updateBooking = $updateBooking->getData(); + + if ($updateBooking->status != 200) { + throw new ApiErrorException($updateBooking->message); + } + + $mailParams = ['booking_id' => $bookingDetail['id']]; + $this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($mailParams)); + + + $response = [ + 'status' => 1, + 'statusCode' => 200, + 'data' => [ + 'isCancellationRequest' => true + ], + 'message' => __('api-cancellation_confirm-message', [], $request->getLocale()) + ]; + + + } else { + + $mailParams = [ + 'bookingCode' => $bookingCode + ]; + + $this->mailer->send(new BookingCancellationRequestMail($mailParams)); + + $response = [ + 'status' => 1, + 'statusCode' => 200, + 'data' => [ + 'isCancellationRequest' => true + ], + 'message' => __('api-cancellation_request-message', [], $request->getLocale()) + ]; + + } + + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + //Just Offline Booking Engines + if (!in_array($bookingDetail['booking_channel']['channel_category_id'], [2, 7])) { + throw new ApiErrorException('Online reservation cancellation is not active for this channel.'); + } + + //TODO: Checkin tarihi geçmemiş olmalı kontrol eklenecek + + $cacheKey = md5('BookingCancellation-' . $bookingDetail['booking_code']); + + if (!empty($confirmCode) || in_array($bookingDetail['booking_channel']['channel_category_id'], [7])) { + + if (!in_array($bookingDetail['booking_channel']['channel_category_id'], [7])) { + + if (!Cache::has($cacheKey)) { + throw new ApiErrorException('Verification code not found, please try again.'); + } + + if ($confirmCode != Cache::get($cacheKey)) { + throw new ApiErrorException('The entered code could not be verified, please enter the updated code sent to your e-mail address.'); + } + + } + + //Cancellation Process + $updateBookingParam = [ + 'locale' => $request->getLocale(), + 'property_id' => $bookingDetail['property_id'], + 'booking_id' => $bookingDetail['id'], + 'total' => $bookingDetail['total'], + 'currency_code' => $bookingDetail['currency_code'], + 'status' => 0 + ]; + + $request['requestParams'] = $updateBookingParam; + + $updateBooking = $propertyBookingController->updateBooking($request); + $updateBooking = $updateBooking->getData(); + + if ($updateBooking->status != 200) { + throw new ApiErrorException($updateBooking->message); + } + + $mailParams = ['booking_id' => $bookingDetail['id']]; + $this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($mailParams)); + + Cache::forget($cacheKey); + + $response = [ + 'status' => 1, + 'statusCode' => 200, + 'data' => null, + 'message' => null + ]; + + } else { + + $confirmCodeGenerate = rand(1000, 9999); + Cache::put($cacheKey, $confirmCodeGenerate, 60 * 5);//600 10 dk + + //$confirmCode + $mailParams = [ + 'bookingCode' => $bookingCode, + 'confirmCode' => $confirmCodeGenerate + ]; + + $this->mailer->send(new BookingCancellationConfirmCodeMail($mailParams)); + + $response = [ + 'status' => 1, + 'statusCode' => 200, + 'data' => null, + 'message' => 'Please enter the cancellation verification code sent to your e-mail address.' + ]; + + } + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function bookingInvoice(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $channelId = $this->channelId; + $propertyId = $this->bookingEnginePropertyId; + $bookingCode = $params['bookingCode']; + + + $getChannelPropertyDetail = $this->getChannelPropertyDetail($channelId, $propertyId); + if (!$getChannelPropertyDetail['status']) { + throw new ApiErrorException(lang('Property and Channel are not matched.')); + } + + $getChannelPropertyDetail = $getChannelPropertyDetail['data']; + + $bookingDetailParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode] + ], + 'with' => ['bookingChannel.propertyChannelCategory', 'bookingProperty', 'bookingRoom', 'bookingContact'], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($bookingDetailParam); + + if ($bookingDetail['status'] != 'success' || ($bookingDetail['status'] == 'success' && empty($bookingDetail['data']))) { + throw new ApiErrorException('Property and Booking Code are not matched.'); + } + + $bookingDetail = $bookingDetail['data']; + + if ($bookingDetail['status'] != 1) { + throw new ApiErrorException('Reservation status not available.'); + } + + if ($bookingDetail['booking_contact']['invoice_request'] != 1) { + throw new ApiErrorException('Reservation invoice status not available.'); + } + + + $invoiceParam = [ + 'name_surname' => fillOnUndefined($params, 'name_surname'), + 'citizen_number' => fillOnUndefined($params, 'citizen_number'), + 'company' => fillOnUndefined($params, 'company'), + 'tax_number' => fillOnUndefined($params, 'tax_number'), + 'address' => fillOnUndefined($params, 'address'), + ]; + + $bookingInvoiceUpdate = $this->bookingContactService->update($bookingDetail['booking_contact']['id'], ['invoice' => json_encode($invoiceParam)]); + + if ($bookingInvoiceUpdate['status'] != 'success') { + throw new ApiErrorException('Invoice update operation could not be performed.'); + } + + $bookingPropertyAddonUpdateMail = [ + 'bookingCode' => $bookingDetail['booking_code'] + ]; + $this->mailer->onQueue('bookingInvoiceUpdateMail', new BookingInvoiceUpdateMail($bookingPropertyAddonUpdateMail)); + + $response = [ + 'status' => 1, + 'statusCode' => 200, + 'data' => null, + 'message' => null + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + + public function propertyList(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $ipAddress = $request->ip(); + $channelToken = $request->header('channelToken'); + + if (!$channelToken) { + return apiResponse(0, 'Token not provided.', null, 401); + } + + $ipRestriction = [ + //City Travel + '1500c5d5-b55e-4fa0-966b-698f4f168f48' => [ + '127.0.0.1', + '185.137.215.118', + '176.236.59.130', + '176.236.59.131', + '176.236.59.132', + '176.236.59.133', + '176.236.59.134', + '176.236.59.135', + '176.236.59.136', + '176.236.59.137', + '176.236.59.138', + '176.236.59.139', + '54.72.215.85', + '54.77.48.206', + '34.252.23.103', + '52.19.162.59', + '54.72.80.155', + '52.210.166.9', + '54.72.220.60', + '52.17.235.55', + '176.236.59.130', + '54.72.215.85', + '54.77.48.206', + '52.213.250.14', + '34.252.23.103' + ] + ]; + + if (isset($ipRestriction[$channelToken]) && !in_array($ipAddress, $ipRestriction[$channelToken])) { + return apiResponse(0, 'Cannot be accessed via this IP address: ' . $ipAddress, null, 401); + } + + $channelRequest = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => $channelToken], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + + $channelCheck = $this->channelService->select($channelRequest); + + if ($channelCheck['status'] != 'success' || empty($channelCheck['data'])) { + return apiResponse(0, 'Channel Token not found.', null, 401); + } + + $channelCheck = $channelCheck['data']; + + + $propertyBookingEngineParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelCheck['id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel', 'property.propertyBookingEngineToken', 'property.propertyType', 'property.propertyContact', 'property.propertyDestination'], + "orderBy" => [ + ["field" => "id", "value" => "DESC"] + ] + ]; + + $propertyBookingEngine = $this->propertyBookingEngineService->select($propertyBookingEngineParam); + $propertyBookingEngine = $propertyBookingEngine['status'] == 'success' ? $propertyBookingEngine['data'] : []; + + $propertyList = []; + foreach ($propertyBookingEngine as $bookingEngine) { + + $propertyList[] = [ + 'id' => $bookingEngine['property']['id'], + 'name' => $bookingEngine['property']['name'], + 'type' => $bookingEngine['property']['property_type']['name'], + 'country' => $bookingEngine['property']['country'], + 'destination' => $bookingEngine['property']['property_destination']['name'], + 'destination_id' => $bookingEngine['property']['destination_id'], + 'token' => $bookingEngine['token'], + 'mapping' => !empty($bookingEngine['property']['mapping']) ? json_decode($bookingEngine['property']['mapping'], 1) : null, + 'contact' => [ + 'address' => $bookingEngine['property']['property_contact']['address'], + 'phone' => $bookingEngine['property']['property_contact']['view_full_phone'], + 'email' => $bookingEngine['property']['property_contact']['email'], + 'latitude' => $bookingEngine['property']['property_contact']['latitude'], + 'longitude' => $bookingEngine['property']['property_contact']['longitude'], + ], + ]; + + } + + $response = [ + 'status' => 1, + 'statusCode' => 200, + 'data' => $propertyList, + 'message' => null + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} diff --git a/app/Http/Controllers/BookingEngine/V1/BookingEngineBaseController.php b/app/Http/Controllers/BookingEngine/V1/BookingEngineBaseController.php new file mode 100644 index 0000000..582d0a0 --- /dev/null +++ b/app/Http/Controllers/BookingEngine/V1/BookingEngineBaseController.php @@ -0,0 +1,109 @@ +cachePrefix . $searchKey; + Cache::put($searchKeyPrefix, $dataArray, $this->cacheExpireTime * 60);//5 min, 300 ttl, 600 10 dk + + if (Cache::has($searchKeyPrefix)) { + return true; + } else { + return false; + } + + } + + public function getCacheDataWithSearchKey($searchKey) + { + $cacheData = []; + $searchKeyPrefix = $this->cachePrefix . $searchKey; + if (Cache::has($searchKeyPrefix)) { + $cacheData = Cache::get($searchKeyPrefix); + + } + + return $cacheData; + + } + + public function rateKeyCodeDecode($rateKeyCode) + { + $rateKeyCodeExplode = explode('-', $rateKeyCode); + $data = [ + 'propertyId' => $rateKeyCodeExplode[0], + 'channelId' => $rateKeyCodeExplode[1], + 'availabilityTypeId' => $rateKeyCodeExplode[2], + 'roomId' => $rateKeyCodeExplode[3], + 'rateMappingId' => $rateKeyCodeExplode[4], + 'roomOccupancyCode' => $rateKeyCodeExplode[5], + 'checkInDate' => Carbon::parse($rateKeyCodeExplode[6])->format('Y-m-d'), + 'checkOutDate' => Carbon::parse($rateKeyCodeExplode[7])->format('Y-m-d'), + 'cancellationPolicyId' => $rateKeyCodeExplode[8], + 'paymentTypeId' => $rateKeyCodeExplode[9], + ]; + + return $data; + } + + public function getDateByDay($dates = []) + { + + $dateByDay = []; + + $diffInDays = Carbon::parse($dates['checkIn'])->floatDiffInDays(Carbon::parse($dates['checkOut'])); + + for ($i = 0; $i < $diffInDays; $i++) { + $dateByDay[] = Carbon::parse($dates['checkIn'])->addDay($i)->format('Y-m-d'); + } + + return $dateByDay; + + } + + + public function getRoomsByOccupancies($rooms = []) + { + $roomsByOccupancies = []; + + foreach ($rooms as $roomOrder => $room) { + + $roomKey = []; + $roomKey[] = str_repeat('A', $room['adults']); + if ($room['children'] > 0) { + foreach ($room['age'] as $childAge) { + $roomKey[] = 'C' . $childAge; + } + } + + $roomKey = implode($roomKey, ''); + + $roomsByOccupancies[$roomKey]['rooms'][] = $roomOrder; + $roomsByOccupancies[$roomKey]['roomCount'] = count($roomsByOccupancies[$roomKey]['rooms']); + $roomsByOccupancies[$roomKey]['occupancy'] = intval($room['adults'] + $room['children']); + $roomsByOccupancies[$roomKey]['adultCount'] = $room['adults']; + $roomsByOccupancies[$roomKey]['childCount'] = $room['children']; + $roomsByOccupancies[$roomKey]['childAges'] = fillOnUndefined($room, 'age', []); + $roomsByOccupancies[$roomKey]['occupancyCode'] = str_repeat('A', $room['adults']) . str_repeat('C', $room['children']); + } + + return $roomsByOccupancies; + + } +} diff --git a/app/Http/Controllers/BookingEngine/V1/PaymentLinkController.php b/app/Http/Controllers/BookingEngine/V1/PaymentLinkController.php new file mode 100644 index 0000000..02c4c24 --- /dev/null +++ b/app/Http/Controllers/BookingEngine/V1/PaymentLinkController.php @@ -0,0 +1,489 @@ +request = $request; + $this->propertyPaymentService = $propertyPaymentService; + $this->propertyService = $propertyService; + $this->languageService = $languageService; + $this->manualPaymentMailService = $manualPaymentMailService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + } + + + public function paymentLinkDetail(Request $request, $orderCode) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $paymentDetailParam = [ + 'criteria' => [ + ['field' => 'order_id', 'condition' => '=', 'value' => $orderCode], + ['field' => 'transaction_type', 'condition' => '=', 'value' => 'LNK'], + ['field' => 'status', 'condition' => '=', 'value' => 5], + ], + 'with' => ['relatedTransactions', 'paymentTypeMapping.paymentType'], + 'firstRow' => true + ]; + + $paymentDetail = $this->propertyPaymentService->selectPaymentTransaction($paymentDetailParam); + + if ($paymentDetail['status'] != 'success' || empty($paymentDetail['data'])) { + throw new ApiErrorException(lang('Payment not found')); + } + + $paymentDetail = $paymentDetail['data']; + + if ($paymentDetail['status'] != 5) { + throw new ApiErrorException(lang('Payment not link status')); + } + + $paymentDetail['is_payed'] = false; + if (!empty($paymentDetail['related_transactions'])) { + $relatedTransactions = collect($paymentDetail['related_transactions']); + if ($relatedTransactions->where('status', 1)->count() > 0) { + $paymentDetail['is_payed'] = true; + } + } + + $propertyId = $paymentDetail['property_id']; + + $propertyParam = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $propertyId], + ], + 'with' => ['propertyBrand', 'propertyContact'], + 'firstRow' => true + ]; + + $getProperty = $this->propertyService->select($propertyParam); + + if ($getProperty['status'] != "success") { + throw new ApiErrorException($getProperty['message']); + } + + $property = $getProperty['data']; + + $property['property_brand']['logo_url'] = $property['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $property['id'] . "/logo/" . $property['property_brand']['logo_name'] . '_250x250.' . $property['property_brand']['logo_file_ext'] : null; + $property['property_contact']['social_media_addresses'] = json_decode($property['property_contact']['social_media_addresses'], 1); + + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + $availableLanguages = Collect($availableLanguages['data'])->keyBy('code')->all(); + + + //contract_file + $property['contract_file'] = null; + $channelMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $property['id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + + $channelMapping = $this->propertyChannelMappingService->select($channelMappingRequest, ['id', 'contract_file', 'currency_code']); + if ($channelMapping['status'] == 'success' && !empty($channelMapping['data'])) { + $property['contract_file'] = Config::get('app.propertyFilesUrl') . $channelMapping['data']['contract_file']; + } + //contract_file + + $responseData = [ + 'payment_detail' => $paymentDetail, + 'property_detail' => $property, + 'available_languages' => $availableLanguages, + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function paymentLinkInitialize(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $orderCode = $params['orderCode']; + + $paymentDetailParam = [ + 'criteria' => [ + ['field' => 'order_id', 'condition' => '=', 'value' => $orderCode], + ['field' => 'transaction_type', 'condition' => '=', 'value' => 'LNK'], + ['field' => 'status', 'condition' => '=', 'value' => 5], + ], + 'firstRow' => true + ]; + + $paymentDetail = $this->propertyPaymentService->selectPaymentTransaction($paymentDetailParam); + + if ($paymentDetail['status'] != 'success' || empty($paymentDetail['data'])) { + throw new ApiErrorException(lang('Payment not found')); + } + + $paymentDetail = $paymentDetail['data']; + + if ($paymentDetail['status'] != 5) { + throw new ApiErrorException(lang('Payment not link status')); + } + + $paymentStatusCheckParam = [ + 'criteria' => [ + ['field' => 'order_id', 'condition' => '=', 'value' => $orderCode], + ['field' => 'transaction_type', 'condition' => '=', 'value' => 'LNK'], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $paymentStatusCheck = $this->propertyPaymentService->selectPaymentTransaction($paymentStatusCheckParam); + + if ($paymentStatusCheck['status'] == 'success' && !empty($paymentStatusCheck['data'])) { + throw new ApiErrorException(lang('Previously paid transaction')); + } + + + DB::beginTransaction(); + + $initializePaymentParam = [ + 'propertyId' => $paymentDetail['property_id'], + 'orderId' => $paymentDetail['order_id'], + 'installment' => 0, + 'amount' => (double)$paymentDetail['amount'], + 'currency' => $paymentDetail['currency'], + 'type' => 'LNK', + 'preferredPaymentTypeId' => $paymentDetail['payment_type_mapping_id'], + 'responseUrl' => $params['responseUrl'], + 'creditCard' => [ + 'name' => $params['creditCard']['cardHolder'], + 'number' => $params['creditCard']['cardNumber'], + 'month' => $params['creditCard']['cardExpireMonth'], + 'year' => $params['creditCard']['cardExpireYear'], + 'cvv' => $params['creditCard']['cardCvv'], + ] + ]; + + $initializePayment = $this->propertyPaymentService->initializePayment($initializePaymentParam); + + if (!$initializePayment['status']) { + throw new ApiErrorException(lang($initializePayment['message'])); + } + + $responseData = [ + 'orderCode' => $paymentDetail['order_id'], + 'paymentCode' => $initializePayment['data']['paymentCode'], + ]; + + if (isset($initializePayment['data']['redirectUrl'])) { + $responseData['redirectUrl'] = $initializePayment['data']['redirectUrl']; + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function paymentLinkConfirm(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + $orderCode = $params['orderCode']; + $paymentCode = $params['paymentCode']; + + $paymentDetailParam = [ + 'criteria' => [ + ['field' => 'order_id', 'condition' => '=', 'value' => $orderCode], + ['field' => 'code', 'condition' => '=', 'value' => $paymentCode], + ['field' => 'transaction_type', 'condition' => '=', 'value' => 'LNK'], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $paymentDetail = $this->propertyPaymentService->selectPaymentTransaction($paymentDetailParam); + + if ($paymentDetail['status'] != 'success' || empty($paymentDetail['data'])) { + throw new ApiErrorException(lang('Payment not confirmed')); + } + + $paymentDetailParam = [ + 'orderCode' => $orderCode, + 'language_code' => isset($params['language_code']) ? $params['language_code'] : 'en' + ]; + $this->manualPaymentMailService->process($paymentDetailParam); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function createManualPaymentForm(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $createPaymentLink = $this->propertyPaymentService->createManualPaymentForm($params); + + if ($createPaymentLink['status'] != "success") { + throw new ApiErrorException($createPaymentLink['message']); + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createPaymentLink['data']]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function createManualPayment(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $params['user_id'] = $request->credentials->user_id; + + $createPaymentLink = $this->propertyPaymentService->createManualPayment($params); + + if ($createPaymentLink['status'] != "success") { + throw new ApiErrorException($createPaymentLink['message']); + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createPaymentLink['data']]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function editManualPaymentForm(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $createPaymentLink = $this->propertyPaymentService->editManualPaymentForm($params); + + if ($createPaymentLink['status'] != "success") { + throw new ApiErrorException($createPaymentLink['message']); + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createPaymentLink['data']]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updateManualPayment(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $createPaymentLink = $this->propertyPaymentService->updateManualPayment($params); + + if ($createPaymentLink['status'] != "success") { + throw new ApiErrorException($createPaymentLink['message']); + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createPaymentLink['data']]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getManualPayment(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $createPaymentLink = $this->propertyPaymentService->getManualPayment($params); + + if ($createPaymentLink['status'] != "success") { + throw new ApiErrorException($createPaymentLink['message']); + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createPaymentLink['data']]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} diff --git a/app/Http/Controllers/BookingEngine/V1/PropertyBookingEngineController.php b/app/Http/Controllers/BookingEngine/V1/PropertyBookingEngineController.php new file mode 100644 index 0000000..5d98d59 --- /dev/null +++ b/app/Http/Controllers/BookingEngine/V1/PropertyBookingEngineController.php @@ -0,0 +1,357 @@ +propertyBookingEngineService = $propertyBookingEngineService; + $this->languageService = $languageService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->currencyService = $currencyService; + $this->propertyGroupService = $propertyGroupService; + $this->findCountryCodeService = $findCountryCodeService; + $this->generalTimezoneService = $generalTimezoneService; + } + + public function checkProperty(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->params; + $propertyRequest = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => $params['token']], + ], + 'with' => ['channel', + 'property.propertyBookingEngineGroupMapping.propertyGroup.propertyGroupMapping.property.propertyBookingEngineToken', + 'property.propertyBookingEngineGroupMapping.propertyGroup.propertyGroupMapping.property.propertyWeb', + 'propertyWeb', 'propertyChannelMapping.channelTax', 'propertyWebComponent', + 'property.propertyAdditionalInfos' + ], + 'firstRow' => 1 + ]; + + $propertyBookingEngine = $this->propertyBookingEngineService->select($propertyRequest, ['id', 'property_id', 'channel_id', 'token', 'parameters']); + + if ($propertyBookingEngine['status'] != 'success') { + throw new ApiErrorException($propertyBookingEngine['message']); + } + if (!$propertyBookingEngine['data']) { + throw new ApiErrorException('General error ...'); + } + + $propertyBookingEngine = $propertyBookingEngine['data']; + + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ] + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + $languageCodes = []; + foreach ($availableLanguages['data'] as $language) { + $languageCodes[$language['code']] = $language; + } + + + $properBookingEngineMapping = []; + if (isset($propertyBookingEngine['parametersArray']['property_group_id'])) { + + $propertyGroupRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'type', 'condition' => '=', 'value' => 'BEN'], + ['field' => 'id', 'condition' => '=', 'value' => $propertyBookingEngine['parametersArray']['property_group_id']] + ], + 'with' => ['propertyGroupMapping'], + 'firstRow' => true, + ]; + + $propertyGroup = $this->propertyGroupService->select($propertyGroupRequest); + $propertyGroup = $propertyGroup['status'] == 'success' && !empty($propertyGroup['data']) ? $propertyGroup['data']['property_group_mapping'] : []; + $propertyGroupPropertyIds = collect($propertyGroup)->pluck('property_id')->toArray(); + + $propertyGroupBookingEngineRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'channel_id', 'condition' => '=', 'value' => $propertyBookingEngine['channel_id']], + ], + 'with' => ['property.propertyBookingEngineGroupMapping.propertyGroup.propertyGroupMapping.property.propertyBookingEngineToken'], + 'whereIn' => [ + ['field' => 'property_id', 'value' => $propertyGroupPropertyIds] + ] + ]; + + $propertyGroupBookingEngine = $this->propertyBookingEngineService->select($propertyGroupBookingEngineRequest); + if ($propertyGroupBookingEngine['status'] == 'success') { + + foreach ($propertyGroupBookingEngine['data'] as $bookingEngine) { + $properBookingEngineMapping[] = [ + 'property_id' => $bookingEngine['property_id'], + 'name' => $bookingEngine['property']['name'], + 'token' => $bookingEngine['token'], + 'order_number' => count($properBookingEngineMapping) + ]; + } + } + + } + + if ($propertyBookingEngine['channel_id'] == 1) { + + $propertyBookingEngineGroupMapping = $propertyBookingEngine['property']['property_booking_engine_group_mapping']; + foreach ($propertyBookingEngineGroupMapping as $beGroupMapping) { + $group = $beGroupMapping['property_group']; + if ($group['type'] == 'MYW') { + $groupProperties = $group['property_group_mapping']; + foreach ($groupProperties as $groupProperty) { + if (isset($groupProperty['property']['property_booking_engine_token'])) { + + $arrayItem = [ + 'property_id' => $groupProperty['property_id'], + 'name' => $groupProperty['property']['name'], + 'token' => $groupProperty['property']['property_booking_engine_token']['token'], + 'order_number' => $groupProperty['order_number'] + ]; + + $arrayItem['web'] = null; + if (!empty($groupProperty['property']['property_web'])) { + if ($groupProperty['property']['property_web']['status'] && $groupProperty['property']['property_web']['is_published']) { + $arrayItem['web'] = $groupProperty['property']['property_web']['webProtocolUrl']; + } + } + + $properBookingEngineMapping[] = $arrayItem; + + } + } + + } + } + } + + if (count($properBookingEngineMapping) > 1) { + array_multisort(array_column($properBookingEngineMapping, 'order_number'), SORT_ASC, $properBookingEngineMapping); + } + + $channelMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyBookingEngine['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $propertyBookingEngine['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + + $channelMapping = $this->propertyChannelMappingService->select($channelMappingRequest, ['id', 'contract_file', 'currency_code']); + if ($channelMapping['status'] != 'success') { + throw new ApiErrorException($channelMapping['message']); + } + + + $channelDefaultCurrencyCode = $channelMapping['data']['currency_code']; + //IP Bases Pricing Channel Manipulation + if (isset($params['ipAddress']) && !empty($params['ipAddress']) && fillOnUndefined($params, 'referrer') != 'google') { + + // Find Country Code with IP + $ipResponse = $this->findCountryCodeService->findCountryWithIpAddress($params['ipAddress']); + + if ($ipResponse['status'] == 'success') { + + $propertyChannelMappingParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyBookingEngine['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'], + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingParam); + + $ipCountryCode = isset($ipResponse['data']['code']) ? $ipResponse['data']['code'] : 'tr'; + + if ($propertyChannelMapping['status'] == 'success') { + + $propertyChannelMappingCollect = collect($propertyChannelMapping['data']); + $countryChannel = $propertyChannelMappingCollect + ->where('channel.channel_category_id', 3) + ->where('channel.country_code', $ipCountryCode) + ->where('channel.parent_id', 1) + ->first(); + + if (!empty($countryChannel)) { + $channelDefaultCurrencyCode = $countryChannel['currency_code']; + } + + //countryCodeGroup + if (empty($countryChannel)) { + $countryChannelGroup = $propertyChannelMappingCollect + ->where('channel.channel_category_id', 3) + ->where('channel.country_code', 'group') + ->where('channel.parent_id', 1) + ->toArray(); + + if (!empty($countryChannelGroup)) { + foreach ($countryChannelGroup as $country) { + if (!empty($country['channel']['country_code_group'])) { + if (in_array($ipCountryCode, $country['channel']['countryCodeGroupArray'])) { + $channelDefaultCurrencyCode = $country['currency_code']; + break; + } + } + } + } + } + //countryCodeGroup + + } + + } + //channelToken Manipulation + + + } + + $exchangeCurrencyRates = $this->currencyService->exchangeCurrencyRates($channelDefaultCurrencyCode); + + $exchangeCurrencyRateList = []; + $exchangeCurrencyAvailable = ['EUR', 'USD', 'TRY', 'GBP', 'GEL', 'MAD', 'AZN']; + if ($exchangeCurrencyRates['status'] == 'success') { + foreach ($exchangeCurrencyRates['data'] as $currencyRate) { + if (!in_array($currencyRate['exc_currency_code'], $exchangeCurrencyAvailable)) { + continue; + } + $currencyKey = $currencyRate['currency_code'] . '-' . $currencyRate['exc_currency_code']; + $exchangeCurrencyRateList[$currencyKey] = [ + 'currency_code' => $currencyRate['currency_code'], + 'exc_currency_code' => $currencyRate['exc_currency_code'], + 'rate' => moneyFourFormatDecimal($currencyRate['rate']), + ]; + } + } + + $contractFileUrl = NULL; + if (!empty($channelMapping['data']['contract_file'])) { + $contractFileUrl = Config::get('app.propertyFilesUrl') . $channelMapping['data']['contract_file']; + } + + $channelTax = null; + $taxOptions = Config::get('app.taxOptions'); + if (!empty($propertyBookingEngine['property_channel_mapping']['channel_tax_id'])) { + $channelTax = [ + 'id' => $propertyBookingEngine['property_channel_mapping']['channel_tax']['id'], + 'code' => $propertyBookingEngine['property_channel_mapping']['channel_tax']['code'], + 'name' => $propertyBookingEngine['property_channel_mapping']['channel_tax']['name'], + 'language_key' => $propertyBookingEngine['property_channel_mapping']['channel_tax']['language_key'], + 'tax' => isset($taxOptions[$propertyBookingEngine['property']['country']]) ? $taxOptions[$propertyBookingEngine['property']['country']] : null + ]; + } + + //PropertyWebComponent + $propertyWebComponent = null; + if ($propertyBookingEngine['property_web_component']) { + + $propertyWebComponentSojernBookingEngine = collect($propertyBookingEngine['property_web_component']) + ->where('component_id', 10) + ->first(); + + if ($propertyWebComponentSojernBookingEngine) { + $propertyWebComponent['sojernbookingengine'] = $propertyWebComponentSojernBookingEngine['parameterArray']; + } + + $propertyWebComponentClarityBookingEngine = collect($propertyBookingEngine['property_web_component']) + ->where('component_id', 14) + ->first(); + + if ($propertyWebComponentClarityBookingEngine) { + $propertyWebComponent['claritybookingengine'] = $propertyWebComponentClarityBookingEngine['parameterArray']; + } + + } + + + $propertyTimeZoneDetail = null; + $timeZoneValue = collect($propertyBookingEngine['property']['property_additional_infos'])->where('additional_info_key_id', '13')->first(); + if ($timeZoneValue) { + $generalTimezoneRepositoryCriteria = ['criteria' => [['field' => 'id', 'condition' => '=', 'value' => $timeZoneValue['value']],], 'firstRow' => true]; + $propertyTimeZoneDetail = $this->generalTimezoneService->select($generalTimezoneRepositoryCriteria, ['location', 'action_type', 'hour', 'minute']); + if ($propertyTimeZoneDetail['status'] == 'success') { + $propertyTimeZoneDetail = $propertyTimeZoneDetail['data']; + } + + } + + $responseData = [ + 'property_id' => $propertyBookingEngine['property_id'], + 'property_name' => $propertyBookingEngine['property']['name'], + 'channel_id' => $propertyBookingEngine['channel_id'], + 'channel_name' => $propertyBookingEngine['channel']['name'], + 'channel_category_id' => $propertyBookingEngine['channel']['channel_category_id'], + 'booking_engine_token' => $propertyBookingEngine['token'], + 'channel_token' => $propertyBookingEngine['channel']['token'], + 'channel_contact' => $propertyBookingEngine['channel']['contact'], + 'default_language' => isset($propertyBookingEngine['property_web']['default_language']) ? $propertyBookingEngine['property_web']['default_language'] : null, + 'default_currency' => $channelDefaultCurrencyCode, + 'available_language_codes' => $languageCodes, + 'property_booking_engine_mapping' => count($properBookingEngineMapping) > 0 ? $properBookingEngineMapping : null, + 'contract_file_url' => $contractFileUrl, + 'exchange_currency_rate' => $exchangeCurrencyRateList, + 'booking_engine_parameters' => $propertyBookingEngine['parametersArray'], + 'channel_tax' => $channelTax, + 'property_web_component' => $propertyWebComponent, + 'property_timezone' => $propertyTimeZoneDetail + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } +} diff --git a/app/Http/Controllers/BookingEngine/V1/SearchController.php b/app/Http/Controllers/BookingEngine/V1/SearchController.php new file mode 100644 index 0000000..fca43f4 --- /dev/null +++ b/app/Http/Controllers/BookingEngine/V1/SearchController.php @@ -0,0 +1,2136 @@ +request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + + $this->channelId = $this->request->channelId; + $this->channelToken = $this->request->channelToken; + $this->bookingEngineToken = $this->request->bookingEngineToken; + $this->bookingEnginePropertyId = $this->request->bookingEnginePropertyId; + $this->findCountryCodeService = $findCountryCodeService; + $this->propertyPromotionService = $propertyPromotionService; + $this->propertyAddonService = $propertyAddonService; + $this->propertyBookingEngineSearchService = $propertyBookingEngineSearchService; + $this->propertyPaymentService = $propertyPaymentService; + $this->currencyService = $currencyService; + + $this->channelConnectedId = null; + + $this->cachePrefix = 'enwAPISearch:'; + $this->cacheExpireTime = 15;//minute + + } + + public function getPropertyRoomAndRoomRateMappingData($channelPropertyRoomRate = []) + { + + $propertyRoomAndRoomRateMappingData = []; + + foreach ($channelPropertyRoomRate as $propertyRoomRate) { + $propertyRoomFacts = []; + foreach ($propertyRoomRate['property_room_rate_mapping']['property_room']['property_room_fact_mapping'] as $propertyRoomFact) { + $propertyRoomFacts[$propertyRoomFact['fact_id']] = [ + 'name' => __($propertyRoomFact['property_fact']['language_key']), + 'icon' => $propertyRoomFact['property_fact']['icon'], + 'is_feature' => $propertyRoomFact['is_feature'], + ]; + } + + if (!empty($propertyRoomFacts)) { + unset($propertyRoomRate['property_room_rate_mapping']['property_room']['property_room_fact_mapping']); + $propertyRoomRate['property_room_rate_mapping']['property_room']['attributes'] = $propertyRoomFacts; + + } + + $propertyRoomBedGroups = []; + $propertyRoomBedGroupCollection = collect($propertyRoomRate['property_room_rate_mapping']['property_room']['property_room_bed_group'])->groupBy('bed_group')->sortBy('bed_group')->toArray(); + foreach ($propertyRoomBedGroupCollection as $propertyRoomBedGroupKey => $propertyRoomBedGroup) { + foreach ($propertyRoomBedGroup as $bedGroup) { + $propertyRoomBedGroups[$propertyRoomBedGroupKey][$bedGroup['id']] = [ + 'count' => $bedGroup['count'], + 'name' => __($bedGroup['property_room_bed_type']['language_key']), + 'icon' => $bedGroup['property_room_bed_type']['bedTypeIcon'], + ]; + } + } + + if (!empty($propertyRoomBedGroups)) { + unset($propertyRoomRate['property_room_rate_mapping']['property_room']['property_room_bed_group']); + $propertyRoomRate['property_room_rate_mapping']['property_room']['bedGroups'] = $propertyRoomBedGroups; + + } + + + $propertyRoomPhotos = []; + foreach ($propertyRoomRate['property_room_rate_mapping']['property_room']['property_room_photo_mapping'] as $propertyRoomPhoto) { + $propertyRoomPhotos[$propertyRoomPhoto['property_room_photo']['id']] = [ + 'url' => $propertyRoomPhoto['property_room_photo']['photoUrl'], + 'is_default' => $propertyRoomPhoto['property_room_photo']['is_default'], + 'photo_order' => $propertyRoomPhoto['property_room_photo']['photo_order'], + ]; + } + + $propertyRoomRate['property_room_rate_mapping']['property_room']['photos'] = $propertyRoomPhotos; + if (!empty($propertyRoomPhotos)) { + unset($propertyRoomRate['property_room_rate_mapping']['property_room']['property_room_photo_mapping']); + } + + $propertyRoomAndRoomRateMappingData['room'][$propertyRoomRate['property_room_rate_mapping']['property_room']['id']] = $propertyRoomRate['property_room_rate_mapping']['property_room']; + $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRate['property_room_rate_mapping']['id']] = $propertyRoomRate['property_room_rate_mapping']; + + + //Child Policy + $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRate['property_room_rate_mapping']['id']]['roomRateMappingChildPolicy'] = []; + if (!empty($propertyRoomRate['property_room_rate_channel_pricing_child_policy'])) { + + $roomRateMappingChildPolicy = []; + foreach ($propertyRoomRate['property_room_rate_channel_pricing_child_policy'] as $childPolicy) { + $roomRateMappingChildPolicy[$childPolicy['property_pricing_policy_child']['id']] = $childPolicy['property_pricing_policy_child']; + } + + $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRate['property_room_rate_mapping']['id']]['roomRateMappingChildPolicy'] = $roomRateMappingChildPolicy; + } + + + //Adult Policy + $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRate['property_room_rate_mapping']['id']]['roomRateMappingAdultPolicy'] = []; + if (!empty($propertyRoomRate['property_room_rate_channel_pricing_adult_policy'])) { + + $roomRateMappingAdultPolicy = []; + foreach ($propertyRoomRate['property_room_rate_channel_pricing_adult_policy'] as $adultPolicy) { + $roomRateMappingAdultPolicy[$adultPolicy['property_pricing_policy_adult']['id']] = $adultPolicy['property_pricing_policy_adult']; + } + + $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRate['property_room_rate_mapping']['id']]['roomRateMappingAdultPolicy'] = $roomRateMappingAdultPolicy; + } + + + $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRate['property_room_rate_mapping']['id']]['roomRateDescription'] = $propertyRoomRate['property_room_rate_mapping']['property_room_rate']['descriptionArray']; + + + $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRate['property_room_rate_mapping']['id']]['roomRateMappingCancellationPolicy'] = []; + if (!empty($propertyRoomRate['property_room_rate_channel_cancellation_policy'])) { + + $roomRateMappingCancellationPolicy = []; + foreach ($propertyRoomRate['property_room_rate_channel_cancellation_policy'] as $cancellationPolicy) { + + if ($cancellationPolicy['property_cancellation_policy']['status'] == 0) { + continue; + } + + $roomRateMappingCancellationPolicy[$cancellationPolicy['property_cancellation_policy']['id']] = [ + 'id' => $cancellationPolicy['cancellation_policy_id'], + 'name' => $cancellationPolicy['property_cancellation_policy']['name'], + 'beforeArrivalDay' => $cancellationPolicy['property_cancellation_policy']['before_arrival'], + 'isNonRefundable' => $cancellationPolicy['property_cancellation_policy']['is_nonrefundable'], + 'isFreeCancellation' => $cancellationPolicy['property_cancellation_policy']['is_free_cancellation'], + 'type' => $cancellationPolicy['property_cancellation_policy']['type'], + 'value' => $cancellationPolicy['property_cancellation_policy']['value'], + 'isAffectedPrice' => $cancellationPolicy['property_cancellation_policy']['is_affected_price'], + 'affectPriceActionType' => $cancellationPolicy['property_cancellation_policy']['affect_price_action_type'], + 'affectPriceType' => $cancellationPolicy['property_cancellation_policy']['affect_price_type'], + 'affectPriceValue' => $cancellationPolicy['property_cancellation_policy']['affect_price_value'], + 'is_date_range' => $cancellationPolicy['property_cancellation_policy']['is_date_range'], + 'start_date' => $cancellationPolicy['property_cancellation_policy']['start_date'], + 'finish_date' => $cancellationPolicy['property_cancellation_policy']['finish_date'], + ]; + } + + $roomRateMappingCancellationPolicy = collect($roomRateMappingCancellationPolicy)->sortByDesc('beforeArrivalDay')->toArray(); + + $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRate['property_room_rate_mapping']['id']]['roomRateMappingCancellationPolicy'] = $roomRateMappingCancellationPolicy; + } + + } + + return $propertyRoomAndRoomRateMappingData; + + } + + public function getChannelProperty($property = []) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['property.propertyBookingEngineToken', 'channelAvailabilityType', 'channelBookingType', 'channelRoomPricingType', 'channelBookingPaymentType.paymentType', 'channel'] + ]; + + if (!empty($property)) { + $requestParam['whereIn'][] = ['field' => 'property_id', 'value' => $property]; + } + + $getChannelProperty = $this->propertyChannelMappingService->select($requestParam); + + if ($getChannelProperty['status'] != 'success' || empty($getChannelProperty['data'])) { + throw new ApiErrorException(lang('Property not found')); + } + + //if (!empty($property)) { + foreach ($getChannelProperty['data'] as $channelPropertyKey => $channelProperty) { + if (!empty($property) && !in_array($channelProperty['property_id'], $property)) { + unset($getChannelProperty['data'][$channelPropertyKey]); + } else { + + $channelBookingPaymentType = []; + foreach ($channelProperty['channel_booking_payment_type'] as $paymentType) { + if ($paymentType['is_selected'] && $paymentType['status']) { + $channelBookingPaymentType[$paymentType['payment_type']['id']] = [ + 'paymentType' => __($paymentType['payment_type']['language_key']), + //'paymentTypeId' => $paymentType['payment_type_id'], + 'paymentTypeCode' => $paymentType['payment_type']['code'], + 'cancellationPolicy' => $paymentType['cancellationPolicyArray'], + 'isAffectedPrice' => $paymentType['is_affected_price'], + 'isGetPaymentData' => $paymentType['is_get_payment_data'], + 'actionType' => $paymentType['action_type'], + 'valueType' => $paymentType['value_type'], + 'value' => $paymentType['value'], + ]; + } + } + + $getChannelProperty['data'][$channelPropertyKey]['channel_booking_payment_type'] = $channelBookingPaymentType; + + + } + } + //} + + $getChannelProperty['data'] = array_values($getChannelProperty['data']); + + $response = [ + 'status' => true, + 'data' => $getChannelProperty['data'], + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + public function getChannelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $channelId = $this->channelId; + if (!empty($this->channelConnectedId)) { + $channelId = $this->channelConnectedId; + } + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + 'propertyRoomRateMapping.propertyRoom.propertyRoomFactMapping.propertyFact', + 'propertyRoomRateMapping.propertyRoom.propertyRoomPhotoMapping.propertyRoomPhoto', + 'propertyRoomRateMapping.propertyRoom.propertyRoomBedGroup.propertyRoomBedType', + 'propertyRoomRateChannelPricingAdultPolicy.propertyPricingPolicyAdult', + 'propertyRoomRateChannelPricingChildPolicy.propertyPricingPolicyChild', + 'propertyRoomRateChannelCancellationPolicy.propertyCancellationPolicy', + //'propertyRoomRateChannelActivePromotion', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException(lang('Property Room Rate not found')); + } + + $getChannelPropertyRoomRateCollect = collect($getChannelPropertyRoomRate['data']); + + + //Date Filtered + $channelPropertyRoomRateCollectNonDateFiltered = $getChannelPropertyRoomRateCollect + ->where('has_date', '=', 0); + + + $channelPropertyRoomRateCollectDateFiltered = $getChannelPropertyRoomRateCollect + ->where('has_date', '=', 1) + ->where('start_date', '<=', Carbon::now()->format('Y-m-d')) + ->where('end_date', '>=', Carbon::now()->format('Y-m-d')); + + $channelPropertyRoomRateCollectFiltered = $channelPropertyRoomRateCollectNonDateFiltered->merge($channelPropertyRoomRateCollectDateFiltered); + //Date Filtered + + + //Channel Room Rate Active Filtered + $channelPropertyRoomRateCollectFiltered = $channelPropertyRoomRateCollectFiltered->where('property_room_rate_mapping.status', 1); + + //Room Rate Filtered + $channelPropertyRoomRateCollectFiltered = $channelPropertyRoomRateCollectFiltered->where('property_room_rate_mapping.property_room.status', 1); + + $channelPropertyRoomRateCollectFiltered = $channelPropertyRoomRateCollectFiltered->toArray(); + + + $response = [ + 'status' => true, + 'data' => $channelPropertyRoomRateCollectFiltered + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + public function getPropertyRoomRatePrice($param = []) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $param['channel_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ['field' => 'stop_sell', 'condition' => '=', 'value' => 0], + ['field' => 'date', 'condition' => '>=', 'value' => $param['checkIn']], + ['field' => 'date', 'condition' => '<', 'value' => $param['checkOut']], + ['field' => 'amount', 'condition' => '<>', 'value' => null], + ['field' => 'amount', 'condition' => '<>', 'value' => 0], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'availability_type_id', 'value' => 'ASC'], + ['field' => 'property_room_id', 'value' => 'ASC'], + ['field' => 'room_rate_mapping_id', 'value' => 'ASC'], + ['field' => 'date', 'value' => 'ASC'] + ], + ]; + + $getPropertyRoomRatePrice = $this->propertyRoomRatePriceService->select($requestParam); + + if ($getPropertyRoomRatePrice['status'] != 'success' || empty($getPropertyRoomRatePrice['data'])) { + throw new ApiErrorException(lang('PropertyRoomRatePrice not found')); + } + + + //dd($getPropertyRoomRatePrice['data']); + + $response = [ + 'status' => true, + 'data' => $getPropertyRoomRatePrice['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + public function getPropertyRoomAndRoomRateAvailability($param = []) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']], + ['field' => 'availability', 'condition' => '>', 'value' => 0], + ['field' => 'stop_sell', 'condition' => '=', 'value' => 0], + ['field' => 'date', 'condition' => '>=', 'value' => $param['checkIn']], + ['field' => 'date', 'condition' => '<', 'value' => $param['checkOut']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'availability_type_id', 'value' => 'ASC'], + ['field' => 'property_room_id', 'value' => 'ASC'], + ['field' => 'room_rate_mapping_id', 'value' => 'ASC'], + ['field' => 'date', 'value' => 'ASC'] + ], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->propertyRoomAvailabilityService->select($requestParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success' || empty($getPropertyRoomAndRoomRateAvailability['data'])) { + throw new ApiErrorException(lang('PropertyRoomAndRoomRateAvailability not found')); + } + + $response = [ + 'status' => true, + 'data' => $getPropertyRoomAndRoomRateAvailability['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + + public function search(Request $request) + { + + //dd(Cache::get($this->cachePrefix.'4d354fd2-8fc3-0725-38bc-1f2f7e749ea8')); + + $minStayCheckProperty = null; + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500, 'errorCode' => null]; + try { + + $this->channelToken = empty($this->channelToken) ? $request->header('channelToken') : $this->channelToken; + $this->bookingEngineToken = empty($this->bookingEngineToken) ? $request->header('bookingEngineToken') : $this->bookingEngineToken; + $this->channelId = empty($this->channelId) ? $request->header('channelId') : $this->channelId; + $this->bookingEnginePropertyId = empty($this->bookingEnginePropertyId) ? $request->header('bookingEnginePropertyId') : $this->bookingEnginePropertyId; + $this->language = empty($this->language) ? $request->header('language') : $this->language; + + $searchKey = md5(getGuid()); + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(), 1); + + //TODO: Burada validator olacak, date zorunlu, en az 1 oda zorunlu + + if ($params['date']['checkIn'] < Carbon::now()->toDateString()) { + throw new ApiErrorException(lang('Check-in time cannot be earlier than today')); + } + + $isMinStayDisabled = (isset($params['min_stay_disabled']) && $params['min_stay_disabled']) ? true : false; + + if (Carbon::parse($params['date']['checkIn'])->diff(Carbon::parse($params['date']['checkOut']))->days > 60) { + throw new ApiErrorException(lang('You can book up to 60 days')); + } + + + $roomsByOccupancies = $this->getRoomsByOccupancies($params['rooms']); + $this->roomsByOccupancies = $roomsByOccupancies; + + $dateByDay = $this->getDateByDay($params['date']); + $this->dateByDay = $dateByDay; + + $channelAvailabilityType = $this->propertyRoomRateChannelMappingService->selectChannelAvailabilityType([]); + $channelAvailabilityType = $channelAvailabilityType['status'] == 'success' ? $channelAvailabilityType['data'] : []; + $channelAvailabilityType = collect($channelAvailabilityType)->groupBy(['id'])->toArray(); + + $daysLeftForCheckin = Carbon::parse($params['date']['checkIn'])->diff(Carbon::now()->toDateString())->days; + + //Eğer $this->bookingEngineToken var ise bu token a ait prporty çekilip gönderilen veriler ezilcek ve sadece o property için fiyat verieclek + if (!is_null($this->bookingEngineToken) && isset($this->bookingEnginePropertyId)) { + $params['property'] = []; + $params['property'][] = $this->bookingEnginePropertyId; + } + + //Wholesaler + if (in_array($request->bookingEngineChannelCategoryId, [7])) { + //$params['property'] = []; + return $response; + } + + $channelProperty = $this->getChannelProperty($params['property']); + + if (!$channelProperty['status']) { + throw new ApiErrorException($channelProperty['message']); + } + + + $searchPricesCacheWithKey = []; + $calculatedRoomPriceFormatted = []; + + + //Channel ile maplenmiş tüm oteller + foreach ($channelProperty['data'] as $channelProperty) { + + $payAtHotelCreditCardCheck = collect($channelProperty['channel_booking_payment_type'])->where('isGetPaymentData',1)->isNotEmpty(); + + //Wholesaler MARKUP + $channelPropertyMarkup = []; + if (in_array($channelProperty['channel']['channel_category_id'], [7])) { + + if (!is_null($channelProperty['markup']) && !empty($channelProperty['channel']['default_currency'])) { + + $channelPropertyMarkup['markup'] = 1; + if (!is_null($channelProperty['markup'])) { + $channelPropertyMarkup['markup'] = (($channelProperty['markup'] + 100) / 100); + } + + $lastExchangeRate = 1; + if ($channelProperty['currency_code'] != $channelProperty['channel']['default_currency']) { + $lastExchangeRate = $this->currencyService->lastExchangeRate($channelProperty['currency_code'], $channelProperty['channel']['default_currency']); + if ($lastExchangeRate['status'] == 'success') { + $lastExchangeRate = $lastExchangeRate['data']; + } + } + $channelPropertyMarkup['exchange'] = $lastExchangeRate; + $channelPropertyMarkup['currency_code'] = $channelProperty['channel']['default_currency']; + } else { + $channelPropertyMarkup = []; + } + } + //Wholesaler MARKUP + + //Bağlı kanala ait özelliklerin alınması + if (!is_null($channelProperty['connected_channel_id']) && $channelProperty['connected_channel_id'] != 5) { + + $requestConnectedChannelParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $channelProperty['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelProperty['connected_channel_id']], + ], + 'with' => ['channelAvailabilityType', 'channelBookingType', 'channelRoomPricingType', 'channelBookingPaymentType.paymentType'], + 'firstRow' => true + ]; + + $getConnectedChannel = $this->propertyChannelMappingService->select($requestConnectedChannelParam); + + if ($getConnectedChannel['status'] != 'success' || empty($getConnectedChannel['data'])) { + continue; + } + + $channelProperty['currency_code'] = $getConnectedChannel['data']['currency_code']; + $channelProperty['property_booking_type_id'] = $getConnectedChannel['data']['property_booking_type_id']; + $channelProperty['property_availability_type_id'] = $getConnectedChannel['data']['property_availability_type_id']; + $channelProperty['property_room_pricing_type_id'] = $getConnectedChannel['data']['property_room_pricing_type_id']; + + $channelProperty['channel_availability_type'] = $getConnectedChannel['data']['channel_availability_type']; + $channelProperty['channel_booking_type'] = $getConnectedChannel['data']['channel_booking_type']; + $channelProperty['channel_room_pricing_type'] = $getConnectedChannel['data']['channel_room_pricing_type']; + + + } + + //$channelBookingPaymentTypePolicy + $channelBookingPaymentTypePolicy = []; + if (!empty($channelProperty['channel_booking_payment_type'])) { + + foreach ($channelProperty['channel_booking_payment_type'] as $channelBookingPaymentTypeId => $channelBookingPaymentType) { + + $channelBookingPaymentTypePolicy[$channelBookingPaymentTypeId] = [ + 'name' => $channelBookingPaymentType['paymentType'], + 'code' => $channelBookingPaymentType['paymentTypeCode'], + 'isAffectedPrice' => $channelBookingPaymentType['isAffectedPrice'], + 'isGetPaymentData' => $channelBookingPaymentType['isGetPaymentData'], + 'affectPriceActionType' => $channelBookingPaymentType['actionType'], + 'affectPriceType' => $channelBookingPaymentType['valueType'], + 'affectPriceValue' => $channelBookingPaymentType['value'], + 'cancellationPolicy' => $channelBookingPaymentType['cancellationPolicy'], + 'paymentTypeMapping' => null, + ]; + + if ($channelBookingPaymentType['paymentTypeCode'] == 'CRD') { + $paymentMappingRequest = $this->propertyPaymentService->getPaymentMappingList(['property_id' => $channelProperty['property_id']]); + $paymentMappingRequest = $paymentMappingRequest['status'] == 'success' ? collect($paymentMappingRequest['data'])->pluck('pos_code')->toArray() : null; + $channelBookingPaymentTypePolicy[$channelBookingPaymentTypeId]['paymentTypeMapping'] = $paymentMappingRequest; + } + + } + + } + + + //Eğer bağlı bir kanal ise ve bu kanal acente tipinde ise + if (!is_null($channelProperty['connected_channel_id']) && $channelProperty['connected_channel_id'] != 5 && in_array($channelProperty['channel']['channel_category_id'], [2, 7])) { + $this->channelConnectedId = $channelProperty['connected_channel_id']; + } + + + //Kanala ait otelin aktif olan room rate leri + $getChannelPropertyRoomRate = $this->getChannelPropertyRoomRate($channelProperty['property_id']); + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + continue; + } + + + //Kanala ait otelin aktif olan room rate fiyatları + $propertyRoomRatePriceParam = [ + 'channel_id' => $channelProperty['channel_id'], + 'property_id' => $channelProperty['property_id'], + 'checkIn' => $params['date']['checkIn'], + 'checkOut' => $params['date']['checkOut'], + ]; + + //connected_channel_id manipulation + if (!is_null($channelProperty['connected_channel_id']) && $channelProperty['connected_channel_id'] != 5) { + $propertyRoomRatePriceParam['channel_id'] = $channelProperty['connected_channel_id']; + } + + // if($request->getClientIp() == '185.137.215.118') { + //dd($propertyRoomRatePriceParam); + //} + + $getPropertyRoomRatePrice = $this->getPropertyRoomRatePrice($propertyRoomRatePriceParam); + + if ($getPropertyRoomRatePrice['status'] != 'success' || empty($getPropertyRoomRatePrice['data'])) { + continue; + } + + //connected_channel_id manipulation + if (!is_null($channelProperty['connected_channel_id']) && !is_null($channelProperty['connected_channel_action']) && $channelProperty['connected_channel_id'] != 5) { + + $connectedChannelAction = json_decode($channelProperty['connected_channel_action'], 1); + + foreach ($getPropertyRoomRatePrice['data'] as $roomRatePriceKey => $roomRatePrice) { + + $roomRatePriceAffected = 0; + + if ($connectedChannelAction['type'] == 'PER') { + $roomRatePriceAffected = ($roomRatePrice['amount'] * $connectedChannelAction['value']) / 100; + } elseif ($connectedChannelAction['type'] == 'FIX') { + $roomRatePriceAffected = $connectedChannelAction['value']; + } + + if ($connectedChannelAction['action_type'] == 'INC') { + $getPropertyRoomRatePrice['data'][$roomRatePriceKey]['amount'] = $roomRatePrice['amount'] + $roomRatePriceAffected; + } + + if ($connectedChannelAction['action_type'] == 'DEC') { + $getPropertyRoomRatePrice['data'][$roomRatePriceKey]['amount'] = $roomRatePrice['amount'] - $roomRatePriceAffected; + } + + } + } + //connected_channel_id manipulation + + + $propertyRoomAndRoomRateAvailabilityParam = [ + 'property_id' => $channelProperty['property_id'], + 'checkIn' => $params['date']['checkIn'], + 'checkOut' => $params['date']['checkOut'], + ]; + + $getPropertyRoomAndRoomRateAvailability = $this->getPropertyRoomAndRoomRateAvailability($propertyRoomAndRoomRateAvailabilityParam); + + if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success' || empty($getPropertyRoomAndRoomRateAvailability['data'])) { + continue; + } + + + //Room ve Room Date Mapping datası + $propertyRoomAndRoomRateMappingData = $this->getPropertyRoomAndRoomRateMappingData($getChannelPropertyRoomRate['data']); + + + /**** PROMOTION START ****/ + $promotionGroupByDay = []; + $propertyPromotionMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $channelProperty['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyPromotion.promotionType', 'propertyRoomRateChannelMapping'] + ]; + + + $propertyPromotionMapping = $this->propertyPromotionService->selectPropertyPromotionMapping($propertyPromotionMappingCriteria); + if ($propertyPromotionMapping['status'] == 'success' && !empty($propertyPromotionMapping['data'])) { + $propertyPromotionMapping = $propertyPromotionMapping['data']; + } else { + $propertyPromotionMapping = []; + } + + //Eğer bağlı bir kanal var ise ve booking engine e bağlıysa burada promosyon çalıştırılmaz! + if (!is_null($channelProperty['connected_channel_id']) && $channelProperty['connected_channel_id'] == 1 && !in_array($channelProperty['channel']['channel_category_id'], [2, 3, 7])) { + $propertyPromotionMapping = []; + } + + //Hangi room rate channel datası için komisyon verilecek... + $roomRateChannelMappingIdList = array_values(array_unique(pickItemFromArray('room_rate_channel_mapping_id', $propertyPromotionMapping))); + + $propertyPromotionMapping = collect($propertyPromotionMapping); + $propertyPromotionMappingGrouped = $propertyPromotionMapping->groupBy(['property_promotion.promotion_type.type_code'])->toArray(); + + //dd($propertyPromotionMappingGrouped); + + foreach ($roomRateChannelMappingIdList as $roomRateChannelMappingId) { + foreach ($propertyPromotionMappingGrouped as $promotionType => $promotions) { + + $promotionsCollect = collect($promotions)->sortByDesc('property_promotion.amount'); + + foreach ($dateByDay as $day) { + + $promotionsCollectFiltered = null; + + if ($promotionType == 'PRD') { + + //burada promosyonun işleneceği eşleştirmeinin de oluşturulması lazım gün bazında + //"1-1-1-2021-12-28" + /* + "property_id" => 1 + "room_rate_mapping_id" => 1 + "channel_id" => 1 + "date" => "2021-12-28" + * */ + + $promotionsCollectFiltered = $promotionsCollect + ->where('room_rate_channel_mapping_id', '=', $roomRateChannelMappingId) + ->where('property_promotion.start_date', '<=', Carbon::now()->toDateString()) + ->where('property_promotion.end_date', '>=', Carbon::now()->toDateString()) + ->where('property_promotion.reservation_start_date', '<=', $day) + ->where('property_promotion.reservation_end_date', '>=', $day) + ->where('property_promotion.min_stay', '<=', count($dateByDay)) + ->filter(function ($propertyPromotion) use ($day) { + $weekDay = Carbon::parse($day)->weekday() == 0 ? 7 : Carbon::parse($day)->weekday(); + if (is_array($propertyPromotion['property_promotion']['daysArray']) && in_array($weekDay, $propertyPromotion['property_promotion']['daysArray'])) { + return $propertyPromotion; + } + }); + + if (count($promotionsCollectFiltered) > 1) { + if (isset($params['isMobile']) && $params['isMobile'] == 1) { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '=', 1); + } else { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '!=', 1); + } + + } else { + if ($promotionsCollectFiltered->pluck('property_promotion.is_mobile')->first() == 1) { + if (!isset($params['isMobile']) || $params['isMobile'] != 1) { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '!=', 1); + } + } + } + + $promotionsCollectFiltered = $promotionsCollectFiltered->sortByDesc('property_promotion.min_stay')->first(); + + $promotionHashKey = null; + if (!is_null($promotionsCollectFiltered)) { + + /*$isPromotionJustMobile = $promotionsCollectFiltered['property_promotion']['is_mobile']; + if($isPromotionJustMobile && (!isset($params['isMobile']) || empty($params['isMobile']))) { + continue; + }*/ + + $promotionKey = $promotionsCollectFiltered['property_room_rate_channel_mapping']['property_id'] . '-' . + $promotionsCollectFiltered['property_room_rate_channel_mapping']['room_rate_mapping_id'] . '-' . + $promotionsCollectFiltered['property_room_rate_channel_mapping']['channel_id'] . '-' . + Carbon::parse($day)->format('Ymd'); + + + if (!isset($promotionGroupByDay[$promotionKey]) && $promotionsCollectFiltered['property_promotion']['amount'] < 100) { + $promotionGroupByDay[$promotionKey] = [ + 'discountRate' => $promotionsCollectFiltered['property_promotion']['amount'], + 'type_code' => $promotionsCollectFiltered['property_promotion']['promotion_type']['type_code'], + 'id' => $promotionsCollectFiltered['property_promotion']['id'] + ]; + } elseif (isset($promotionGroupByDay[$promotionKey]) && $promotionsCollectFiltered['property_promotion']['amount'] > $promotionGroupByDay[$promotionKey]['discountRate']) { + $promotionGroupByDay[$promotionKey] = [ + 'discountRate' => $promotionsCollectFiltered['property_promotion']['amount'], + 'type_code' => $promotionsCollectFiltered['property_promotion']['promotion_type']['type_code'], + 'id' => $promotionsCollectFiltered['property_promotion']['id'] + ]; + } + + } + + } + + if ($promotionType == 'BFD') { + + //dd($dateByDay[0], Carbon::now()->toDateString(), Carbon::parse($dateByDay[0])->diff(Carbon::now())->days); + + $promotionsCollectFiltered = $promotionsCollect + ->where('room_rate_channel_mapping_id', '=', $roomRateChannelMappingId) + ->where('property_promotion.day_before', '<=', Carbon::parse($dateByDay[0])->diff(Carbon::now()->toDateString())->days) + ->where('property_promotion.min_stay', '<=', count($dateByDay)) + ->filter(function ($propertyPromotion) use ($day) { + $weekDay = Carbon::parse($day)->weekday() == 0 ? 7 : Carbon::parse($day)->weekday(); + if (is_array($propertyPromotion['property_promotion']['daysArray']) && in_array($weekDay, $propertyPromotion['property_promotion']['daysArray'])) { + return $propertyPromotion; + } + })->sortByDesc('property_promotion.day_before'); + + if (count($promotionsCollectFiltered) > 1) { + if (isset($params['isMobile']) && $params['isMobile'] == 1) { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '=', 1); + } else { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '!=', 1); + } + } else { + if ($promotionsCollectFiltered->pluck('property_promotion.is_mobile')->first() == 1) { + if (!isset($params['isMobile']) || $params['isMobile'] != 1) { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '!=', 1); + } + } + } + + $promotionsCollectFiltered = $promotionsCollectFiltered->first(); + + $promotionHashKey = null; + if (!is_null($promotionsCollectFiltered)) { + + /*$isPromotionJustMobile = $promotionsCollectFiltered['property_promotion']['is_mobile']; + if($isPromotionJustMobile && (!isset($params['isMobile']) || empty($params['isMobile']))) { + continue; + }*/ + + //excludeDatesArray + if (isset($promotionsCollectFiltered['property_promotion']['excludeDatesArray']) && in_array($day, $promotionsCollectFiltered['property_promotion']['excludeDatesArray'])) { + continue; + } + + $promotionKey = $promotionsCollectFiltered['property_room_rate_channel_mapping']['property_id'] . '-' . + $promotionsCollectFiltered['property_room_rate_channel_mapping']['room_rate_mapping_id'] . '-' . + $promotionsCollectFiltered['property_room_rate_channel_mapping']['channel_id'] . '-' . + Carbon::parse($day)->format('Ymd'); + + if (!isset($promotionGroupByDay[$promotionKey]) && $promotionsCollectFiltered['property_promotion']['amount'] < 100) { + $promotionGroupByDay[$promotionKey] = [ + 'discountRate' => $promotionsCollectFiltered['property_promotion']['amount'], + 'type_code' => $promotionsCollectFiltered['property_promotion']['promotion_type']['type_code'], + 'id' => $promotionsCollectFiltered['property_promotion']['id'] + ]; + } elseif (isset($promotionGroupByDay[$promotionKey]) && $promotionsCollectFiltered['property_promotion']['amount'] > $promotionGroupByDay[$promotionKey]['discountRate']) { + $promotionGroupByDay[$promotionKey] = [ + 'discountRate' => $promotionsCollectFiltered['property_promotion']['amount'], + 'type_code' => $promotionsCollectFiltered['property_promotion']['promotion_type']['type_code'], + 'id' => $promotionsCollectFiltered['property_promotion']['id'] + ]; + } + } + } + + if ($promotionType == 'LST') { + + //dd($dateByDay[0], Carbon::now()->toDateString(), Carbon::parse($dateByDay[0])->diff(Carbon::now())->days); + $promotionsCollectFiltered = $promotionsCollect + ->where('room_rate_channel_mapping_id', '=', $roomRateChannelMappingId) + ->where('property_promotion.day_before', '>=', Carbon::parse($dateByDay[0])->diff(Carbon::now()->toDateString())->days) + ->where('property_promotion.min_stay', '<=', count($dateByDay)) + ->where('property_promotion.is_time', null) + ->filter(function ($propertyPromotion) use ($day) { + $weekDay = Carbon::parse($day)->weekday() == 0 ? 7 : Carbon::parse($day)->weekday(); + if (is_array($propertyPromotion['property_promotion']['daysArray']) && in_array($weekDay, $propertyPromotion['property_promotion']['daysArray'])) { + return $propertyPromotion; + } + })->sortBy('property_promotion.day_before'); + + if (count($promotionsCollectFiltered) > 1) { + if (isset($params['isMobile']) && $params['isMobile'] == 1) { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '=', 1); + } else { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '!=', 1); + } + } else { + if ($promotionsCollectFiltered->pluck('property_promotion.is_mobile')->first() == 1) { + if (!isset($params['isMobile']) || $params['isMobile'] != 1) { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '!=', 1); + } + } + } + + //If there is a promotion with a fixed time, it gets priority. + $promotionsCollectFilteredCertainTime = $promotionsCollect + ->where('room_rate_channel_mapping_id', '=', $roomRateChannelMappingId) + ->where('property_promotion.day_before', '>=', Carbon::parse($dateByDay[0])->diff(Carbon::now()->toDateString())->days) + ->where('property_promotion.min_stay', '<=', count($dateByDay)) + ->where('property_promotion.is_time', 1) + ->where('property_promotion.start_time', '<=', Carbon::now()->format('H:i:s')) + ->where('property_promotion.end_time', '>=', Carbon::now()->format('H:i:s')) + ->filter(function ($propertyPromotion) use ($day) { + $weekDay = Carbon::parse($day)->weekday() == 0 ? 7 : Carbon::parse($day)->weekday(); + if (is_array($propertyPromotion['property_promotion']['daysArray']) && in_array($weekDay, $propertyPromotion['property_promotion']['daysArray'])) { + return $propertyPromotion; + } + })->sortByDesc('property_promotion.amount'); + + + if (!is_null($promotionsCollectFilteredCertainTime->first())) { + $promotionsCollectFiltered = $promotionsCollectFilteredCertainTime->first(); + } else { + $promotionsCollectFiltered = $promotionsCollectFiltered->first(); + } + + + $promotionHashKey = null; + if (!is_null($promotionsCollectFiltered)) { + + /*$isPromotionJustMobile = $promotionsCollectFiltered['property_promotion']['is_mobile']; + if($isPromotionJustMobile && (!isset($params['isMobile']) || empty($params['isMobile']))) { + continue; + }*/ + + $promotionKey = $promotionsCollectFiltered['property_room_rate_channel_mapping']['property_id'] . '-' . + $promotionsCollectFiltered['property_room_rate_channel_mapping']['room_rate_mapping_id'] . '-' . + $promotionsCollectFiltered['property_room_rate_channel_mapping']['channel_id'] . '-' . + Carbon::parse($day)->format('Ymd'); + + if (!isset($promotionGroupByDay[$promotionKey]) && $promotionsCollectFiltered['property_promotion']['amount'] < 100) { + $promotionGroupByDay[$promotionKey] = [ + 'discountRate' => $promotionsCollectFiltered['property_promotion']['amount'], + 'type_code' => $promotionsCollectFiltered['property_promotion']['promotion_type']['type_code'], + 'id' => $promotionsCollectFiltered['property_promotion']['id'] + ]; + } elseif (isset($promotionGroupByDay[$promotionKey]) && $promotionsCollectFiltered['property_promotion']['amount'] > $promotionGroupByDay[$promotionKey]['discountRate']) { + $promotionGroupByDay[$promotionKey] = [ + 'discountRate' => $promotionsCollectFiltered['property_promotion']['amount'], + 'type_code' => $promotionsCollectFiltered['property_promotion']['promotion_type']['type_code'], + 'id' => $promotionsCollectFiltered['property_promotion']['id'] + ]; + } + } + + } + + + if ($promotionType == 'DSC') { + + //Default + $defaultPromotionDiscount = $promotionsCollect + ->where('room_rate_channel_mapping_id', '=', $roomRateChannelMappingId) + ->where('property_promotion.is_time', null) + ->where('property_promotion.min_stay', '<=', count($dateByDay)) + ->filter(function ($propertyPromotion) use ($day) { + $weekDay = Carbon::parse($day)->weekday() == 0 ? 7 : Carbon::parse($day)->weekday(); + if (is_array($propertyPromotion['property_promotion']['daysArray']) && in_array($weekDay, $propertyPromotion['property_promotion']['daysArray'])) { + return $propertyPromotion; + } + })->sortByDesc('property_promotion.amount'); + + if (isset($params['isMobile']) && $params['isMobile'] == 1) { + $defaultPromotionDiscountMobile = $defaultPromotionDiscount->where('property_promotion.is_mobile', '=', 1); + if ($defaultPromotionDiscountMobile->isNotEmpty()) { + $defaultPromotionDiscount = $defaultPromotionDiscountMobile; + } + } else { + $defaultPromotionDiscount = $defaultPromotionDiscount->where('property_promotion.is_mobile', '!=', 1); + } + + + $promotionsCollectFiltered = $promotionsCollect + ->where('room_rate_channel_mapping_id', '=', $roomRateChannelMappingId) + ->where('property_promotion.is_time', 1) + ->where('property_promotion.start_time', '<=', Carbon::now()->format('H:i:s')) + ->where('property_promotion.end_time', '>=', Carbon::now()->format('H:i:s')) + ->where('property_promotion.min_stay', '<=', count($dateByDay)) + ->filter(function ($propertyPromotion) use ($day) { + $weekDay = Carbon::parse($day)->weekday() == 0 ? 7 : Carbon::parse($day)->weekday(); + if (is_array($propertyPromotion['property_promotion']['daysArray']) && in_array($weekDay, $propertyPromotion['property_promotion']['daysArray'])) { + return $propertyPromotion; + } + })->sortByDesc('property_promotion.amount'); + + + if (isset($params['isMobile']) && $params['isMobile'] == 1) { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '=', 1); + if ($promotionsCollectFiltered->isNotEmpty()) { + $promotionsCollectFiltered = $promotionsCollectFiltered; + } + } else { + $promotionsCollectFiltered = $promotionsCollectFiltered->where('property_promotion.is_mobile', '!=', 1); + } + + + if ($promotionsCollectFiltered->isEmpty()) { + $promotionsCollectFiltered = $defaultPromotionDiscount; + } + + + $promotionsCollectFiltered = $promotionsCollectFiltered->first(); + + $promotionHashKey = null; + if (!is_null($promotionsCollectFiltered)) { + + /*$isPromotionJustMobile = $promotionsCollectFiltered['property_promotion']['is_mobile']; + if($isPromotionJustMobile && (!isset($params['isMobile']) || empty($params['isMobile']))) { + continue; + }*/ + + //excludeDatesArray + if (isset($promotionsCollectFiltered['property_promotion']['excludeDatesArray']) && in_array($day, $promotionsCollectFiltered['property_promotion']['excludeDatesArray'])) { + continue; + } + + $promotionKey = $promotionsCollectFiltered['property_room_rate_channel_mapping']['property_id'] . '-' . + $promotionsCollectFiltered['property_room_rate_channel_mapping']['room_rate_mapping_id'] . '-' . + $promotionsCollectFiltered['property_room_rate_channel_mapping']['channel_id'] . '-' . + Carbon::parse($day)->format('Ymd'); + + if (!isset($promotionGroupByDay[$promotionKey]) && $promotionsCollectFiltered['property_promotion']['amount'] < 100) { + $promotionGroupByDay[$promotionKey] = [ + 'discountRate' => $promotionsCollectFiltered['property_promotion']['amount'], + 'type_code' => $promotionsCollectFiltered['property_promotion']['promotion_type']['type_code'], + 'id' => $promotionsCollectFiltered['property_promotion']['id'] + ]; + } elseif (isset($promotionGroupByDay[$promotionKey]) && $promotionsCollectFiltered['property_promotion']['amount'] > $promotionGroupByDay[$promotionKey]['discountRate']) { + $promotionGroupByDay[$promotionKey] = [ + 'discountRate' => $promotionsCollectFiltered['property_promotion']['amount'], + 'type_code' => $promotionsCollectFiltered['property_promotion']['promotion_type']['type_code'], + 'id' => $promotionsCollectFiltered['property_promotion']['id'] + ]; + } + } + + } + + //Log::debug($promotionGroupByDay); + + } + + } + } + //dd($promotionGroupByDay, $propertyPromotionMapping); + /**** PROMOTION FINISH ****/ + + //dd($promotionGroupByDay); + + + $getPropertyRoomRatePrice = collect($getPropertyRoomRatePrice['data']); + + + $minStayCheckProperty = $getPropertyRoomRatePrice->sortByDesc('min_stay')->groupBy('min_stay')->keys()->first(); + + //Kanala ait otelin aktif olan room rate lerin gruplanarak gün bazında fiyat var mı hesalanması + // Availability Type - Property Room - Room Rate Mapping + $getPropertyRoomRatePriceGrouped = $getPropertyRoomRatePrice->groupBy(['availability_type_id', 'property_room_id', 'room_rate_mapping_id', 'date'])->toArray(); + + foreach ($getPropertyRoomRatePriceGrouped as $getPropertyRoomRatePriceAvailabilityKey => $getPropertyRoomRatePriceAvailability) { + foreach ($getPropertyRoomRatePriceAvailability as $getPropertyRoomRatePriceAvailabilityRoomKey => $getPropertyRoomRatePriceAvailabilityRoom) { + + foreach ($getPropertyRoomRatePriceAvailabilityRoom as $getPropertyRoomRatePriceAvailabilityRoomRateKey => $getPropertyRoomRatePriceAvailabilityRoomRate) { + + unset($getPropertyRoomRatePriceGrouped[$getPropertyRoomRatePriceAvailabilityKey][$getPropertyRoomRatePriceAvailabilityRoomKey][$getPropertyRoomRatePriceAvailabilityRoomRateKey]); + + if (!empty(array_diff($this->dateByDay, array_keys($getPropertyRoomRatePriceAvailabilityRoomRate)))) { + unset($getPropertyRoomRatePriceGrouped[$getPropertyRoomRatePriceAvailabilityKey][$getPropertyRoomRatePriceAvailabilityRoomKey][$getPropertyRoomRatePriceAvailabilityRoomRateKey]); + } else { + + $getPropertyRoomRatePriceAvailabilityRoomRate = array_map(function ($propertyRoomRatePriceAvailabilityRoomRate) { + return reset($propertyRoomRatePriceAvailabilityRoomRate); + }, $getPropertyRoomRatePriceAvailabilityRoomRate); + + + //MIN_STAY CHECK + $minStayCheckCollect = collect($getPropertyRoomRatePriceAvailabilityRoomRate); + $minStayCheck = $minStayCheckCollect->sortByDesc('min_stay')->first(); + + //$isMinStayDisabled + if ($isMinStayDisabled) { + $minStayCheck['min_stay'] = 0; + } + + if (intval($minStayCheck['min_stay']) > count($this->dateByDay)) { + unset($getPropertyRoomRatePriceGrouped[$getPropertyRoomRatePriceAvailabilityKey][$getPropertyRoomRatePriceAvailabilityRoomKey][$getPropertyRoomRatePriceAvailabilityRoomRateKey]); + } else { + + //PROMOTION CALCULATE + foreach ($getPropertyRoomRatePriceAvailabilityRoomRate as $day => $dailyDetail) { + + $getPropertyRoomRatePriceAvailabilityRoomRate[$day]['baseAmount'] = $dailyDetail['amount']; + $getPropertyRoomRatePriceAvailabilityRoomRate[$day]['discountRate'] = 0; + + //TODO : Delete + /*$dailyDetailPromoCheckKey = $dailyDetail['property_id'] . '-' . + $dailyDetail['room_rate_mapping_id'] . '-' . + $dailyDetail['channel_id'] . '-' . + Carbon::parse($day)->format('Ymd'); + + if (array_key_exists($dailyDetailPromoCheckKey, $promotionGroupByDay)) { + $getPropertyRoomRatePriceAvailabilityRoomRate[$day]['discountRate'] = $promotionGroupByDay[$dailyDetailPromoCheckKey]['discountRate']; + $getPropertyRoomRatePriceAvailabilityRoomRate[$day]['amount'] = moneyDoubleFormatDecimal($dailyDetail['amount'] - ($dailyDetail['amount'] * ($promotionGroupByDay[$dailyDetailPromoCheckKey]['discountRate'] / 100))); + }*/ + + } + + $getPropertyRoomRatePriceGrouped[$getPropertyRoomRatePriceAvailabilityKey][$getPropertyRoomRatePriceAvailabilityRoomKey][$getPropertyRoomRatePriceAvailabilityRoomRateKey] = $getPropertyRoomRatePriceAvailabilityRoomRate; + + } + + } + + } + + if (empty($getPropertyRoomRatePriceGrouped[$getPropertyRoomRatePriceAvailabilityKey][$getPropertyRoomRatePriceAvailabilityRoomKey])) { + unset($getPropertyRoomRatePriceGrouped[$getPropertyRoomRatePriceAvailabilityKey][$getPropertyRoomRatePriceAvailabilityRoomKey]); + } + + } + + if (empty($getPropertyRoomRatePriceGrouped[$getPropertyRoomRatePriceAvailabilityKey])) { + unset($getPropertyRoomRatePriceGrouped[$getPropertyRoomRatePriceAvailabilityKey]); + } + } + //Kanala ait otelin aktif olan room rate lerin gruplanarak gün bazında fiyat var mı hesaplanması + + + //Kanalın müsaitlik type ına göre kullanılacak olanların seçilmesi + $selectedPropertyRoomRatePriceGrouped = []; + if (isset($getPropertyRoomRatePriceGrouped[$channelProperty['channel_availability_type']['id']])) { + $selectedPropertyRoomRatePriceGrouped[$channelProperty['channel_availability_type']['id']] = $getPropertyRoomRatePriceGrouped[$channelProperty['channel_availability_type']['id']]; + } + + + if ($channelProperty['channel_availability_type']['code'] == 'GRT') { + if (isset($getPropertyRoomRatePriceGrouped[1])) { + $selectedPropertyRoomRatePriceGrouped[1] = $getPropertyRoomRatePriceGrouped[1]; + } + } + //Kanalın müsaitlik type ına göre kullanılacak olanların seçilmesi + + + if (empty($selectedPropertyRoomRatePriceGrouped)) { + //Hiç fiyat çekilememiş ise diğer proprty ye geç + continue; + } + + + //TODO: burada her istenen oda tipi için fiyat hesaplanacak ve + $calculatedRoomPrice = []; + + // Availability Type - Property Room - Room Rate Mapping + foreach ($selectedPropertyRoomRatePriceGrouped as $propertyAvailabilityKey => $propertyAvailability) { + foreach ($propertyAvailability as $propertyRoomKey => $propertyRoom) { + + //Olmayan bir oda için fiyat gelirse devam et + if (!in_array($propertyRoomKey, array_keys($propertyRoomAndRoomRateMappingData['room']))) { + continue; + } + + foreach ($propertyRoom as $propertyRoomRateKey => $propertyRoomRate) { + + $roomsByOccupancyPrices = []; + foreach ($this->roomsByOccupancies as $roomsByOccupancyKey => $roomsByOccupancy) { + + + if ($roomsByOccupancy['adultCount'] > $propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['max_adult']) { + //Bu oda için istenen yetişkin sayısı kadar kapasite yok + continue; + } + + if ($roomsByOccupancy['childCount'] > $propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['max_child']) { + //Bu oda için istenen çocuk sayısı kadar kapasite yok + continue; + } + + if ($roomsByOccupancy['occupancy'] > $propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['max_occupancy']) { + //Bu oda için istenen misafir sayısı kadar kapasite yok + continue; + } + + if (isset($roomsByOccupancy['childAges']) && !is_null($propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['max_child_number'])) { + foreach ($roomsByOccupancy['childAges'] as $childAge) { + if ($childAge > $propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['max_child_number']) { + continue 2; + } + } + } + + + $roomRateExcludeOccupancy = !is_null($propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['exclude_occupancy']) ? json_decode($propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['exclude_occupancy'], 1) : []; + + if (in_array($roomsByOccupancy['occupancyCode'], $roomRateExcludeOccupancy)) { + //Bu odada istenmeyen bir occupancy grubu olduğu için kullanılmıyor + continue; + } + + + if (!isset($propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey])) { + //Mapping olmayan bir rate gelirse devam et + continue 2; + } + + if ($propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room']['occupancy_lock']) { + if ($propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['included_occupancy'] != $roomsByOccupancy['adultCount']) { + //Occupancy Lock Durumu + continue 1; + } + } + + + //Fiyatlama kısmı baş + $propertyRoomAndRoomRateAvailability = collect($getPropertyRoomAndRoomRateAvailability['data']); + + $roomRateDaily = []; + foreach ($propertyRoomRate as $dailyKey => $dailyRoomRate) { + + //Pool için genel kontenjan + if ($propertyAvailabilityKey == 1) { + $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability + ->where('date', $dailyRoomRate['date']) + ->where('property_room_id', $dailyRoomRate['property_room_id']) + ->where('availability_type_id', $dailyRoomRate['availability_type_id']) + ->where('status', 1) + ->first(); + + } else { + $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability + ->where('date', $dailyRoomRate['date']) + ->where('property_room_id', $dailyRoomRate['property_room_id']) + ->where('availability_type_id', $dailyRoomRate['availability_type_id']) + ->where('room_rate_mapping_id', $dailyRoomRate['room_rate_mapping_id']) + ->where('channel_id', $channelProperty['channel_id']) + ->where('status', 1) + ->first(); + } + + + if ($channelProperty['channel_room_pricing_type']['code'] == 'ROM') { + + $dailyRoomRateRackPrice = $dailyRoomRate['amount']; + $propertyRoomRateIncludedOccupancy = $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['included_occupancy']; + $oneAdultRoomRateRackPrice = $dailyRoomRateRackPrice / $propertyRoomRateIncludedOccupancy; + + //NEW Promotoin Affect + $dailyDetailPromoCheckKey = $dailyRoomRate['property_id'] . '-' . + $dailyRoomRate['room_rate_mapping_id'] . '-' . + $dailyRoomRate['channel_id'] . '-' . + Carbon::parse($dailyKey)->format('Ymd'); + + if (array_key_exists($dailyDetailPromoCheckKey, $promotionGroupByDay)) { + $dailyRoomRate['discountRate'] = $promotionGroupByDay[$dailyDetailPromoCheckKey]['discountRate']; + $dailyRoomRateRackPrice = moneyDoubleFormatDecimal($dailyRoomRateRackPrice - ($dailyRoomRateRackPrice * ($promotionGroupByDay[$dailyDetailPromoCheckKey]['discountRate'] / 100))); + } + //NEW Promotoin Affect + + $roomRateDaily[$dailyKey] = [ + 'date' => $dailyKey, + 'amount' => $dailyRoomRateRackPrice, + 'allotment' => fillOnUndefined($roomAndRoomRateAvailability, 'availability', 0), + 'rackRateAmount' => $dailyRoomRateRackPrice, + 'rackRateBaseAmount' => $dailyRoomRate['baseAmount'], + 'discountRate' => $dailyRoomRate['discountRate'], + 'oneAdultRackRateAmount' => $oneAdultRoomRateRackPrice, + 'min_stay' => $dailyRoomRate['min_stay'] + ]; + + } elseif ($channelProperty['channel_room_pricing_type']['code'] == 'PRS') { + + $roomPrice = 0; + $adultPrice = 0; + $childPrice = 0; + + $dailyRoomRateRackPrice = $dailyRoomRate['amount']; + $propertyRoomRateIncludedOccupancy = $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['included_occupancy']; + $oneAdultRoomRateRackPrice = $dailyRoomRateRackPrice / $propertyRoomRateIncludedOccupancy; + + //ADULT, CHILD POLICIES + $roomRateMappingAdultPolicy = $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['roomRateMappingAdultPolicy']; + $roomRateMappingChildPolicy = $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['roomRateMappingChildPolicy']; + + + //ADULT PRICE + $adultPrice = $dailyRoomRateRackPrice; + + if (!empty($roomRateMappingAdultPolicy)) { + + $roomRateMappingAdultPolicyCollect = collect($roomRateMappingAdultPolicy); + + $roomOccupancyAdultActionType = null; + $roomOccupancyAdultActionDiff = null; + if ($roomsByOccupancy['adultCount'] > $propertyRoomRateIncludedOccupancy) { + $roomOccupancyAdultActionType = 'INC'; + $roomOccupancyAdultActionDiff = $roomsByOccupancy['adultCount'] - $propertyRoomRateIncludedOccupancy; + $roomOccupancyAdultActionDiff = ceil($roomOccupancyAdultActionDiff); + } elseif ($roomsByOccupancy['adultCount'] < $propertyRoomRateIncludedOccupancy) { + $roomOccupancyAdultActionType = 'DEC'; + $roomOccupancyAdultActionDiff = $propertyRoomRateIncludedOccupancy - $roomsByOccupancy['adultCount']; + $roomOccupancyAdultActionDiff = floor($roomOccupancyAdultActionDiff); + } + + if (!is_null($roomOccupancyAdultActionType)) { + + $roomOccupancyAdultPolicy = $roomRateMappingAdultPolicyCollect + ->where('adult_action_type', $roomOccupancyAdultActionType) + ->where('adult', $roomOccupancyAdultActionDiff) + ->where('reservation_start_date', null) + ->where('reservation_end_date', null) + ->sortByDesc('id') + ->first(); + + $roomOccupancyAdultPolicyDate = $roomRateMappingAdultPolicyCollect + ->where('adult_action_type', $roomOccupancyAdultActionType) + ->where('adult', $roomOccupancyAdultActionDiff) + ->where('reservation_start_date', '<=', $dailyKey) + ->where('reservation_end_date', '>=', $dailyKey) + ->sortByDesc('id') + ->first(); + + if ($roomOccupancyAdultPolicyDate) { + $roomOccupancyAdultPolicy = $roomOccupancyAdultPolicyDate; + } + + + if (!empty($roomOccupancyAdultPolicy)) { + + if ($roomOccupancyAdultPolicy['type'] == 'PER') { + $roomOccupancyAdultActionDiffAffected = ($dailyRoomRateRackPrice * $roomOccupancyAdultPolicy['value']) / 100; + } elseif ($roomOccupancyAdultPolicy['type'] == 'FIX') { + $roomOccupancyAdultActionDiffAffected = $roomOccupancyAdultPolicy['value']; + } + + if ($roomOccupancyAdultPolicy['action_type'] == 'INC') { + $adultPrice = $dailyRoomRateRackPrice + $roomOccupancyAdultActionDiffAffected; + } + + if ($roomOccupancyAdultPolicy['action_type'] == 'DEC') { + $adultPrice = $dailyRoomRateRackPrice - $roomOccupancyAdultActionDiffAffected; + } + + } + + + } + + + } + + + //dd($adultPrice); + + + if ($adultPrice <= 0) { + //Eğer günlük fiyat 0 ya da - ye düşerse bu rate için hesaplama yapmayacak + continue 2; + } + + //CHILD PRICE + $childPriceRaw = []; + $roomRateMappingChildPolicyCollect = collect($roomRateMappingChildPolicy); + + //dd($roomRateMappingChildPolicyCollect); + + $childOrder = 1; + for ($i = 0; $i < $roomsByOccupancy['childCount']; $i++) { + + $childPriceRaw[$i] = $oneAdultRoomRateRackPrice; + + + if (!empty($roomRateMappingChildPolicy)) { + + $childAgePolicyAffected = $oneAdultRoomRateRackPrice; + + $childAgePolicy = $roomRateMappingChildPolicyCollect + ->where('adult', 0) + ->where('child_order', $childOrder) + ->where('child_age_start', '<=', $roomsByOccupancy['childAges'][$i]) + ->where('child_age_end', '>', $roomsByOccupancy['childAges'][$i]) + ->where('reservation_start_date', null) + ->where('reservation_end_date', null) + ->sortByDesc('id') + ->first(); + + $childAgePolicyPeriod = $roomRateMappingChildPolicyCollect + ->where('adult', 0) + ->where('child_order', $childOrder) + ->where('child_age_start', '<=', $roomsByOccupancy['childAges'][$i]) + ->where('child_age_end', '>', $roomsByOccupancy['childAges'][$i]) + ->where('reservation_start_date', '<=', $dailyKey) + ->where('reservation_end_date', '>=', $dailyKey) + ->sortByDesc('id') + ->first(); + + if ($childAgePolicyPeriod) { + $childAgePolicy = $childAgePolicyPeriod; + } + + $childAgePolicyAdult = $roomRateMappingChildPolicyCollect + ->where('adult', $roomsByOccupancy['adultCount']) + ->where('child_order', $childOrder) + ->where('child_age_start', '<=', $roomsByOccupancy['childAges'][$i]) + ->where('child_age_end', '>', $roomsByOccupancy['childAges'][$i]) + ->where('reservation_start_date', null) + ->where('reservation_end_date', null) + ->sortByDesc('id') + ->first(); + + if ($childAgePolicyAdult) { + $childAgePolicy = $childAgePolicyAdult; + } + + $childAgePolicyDate = $roomRateMappingChildPolicyCollect + ->where('adult', $roomsByOccupancy['adultCount']) + ->where('child_order', $childOrder) + ->where('child_age_start', '<=', $roomsByOccupancy['childAges'][$i]) + ->where('child_age_end', '>', $roomsByOccupancy['childAges'][$i]) + ->where('reservation_start_date', '<=', $dailyKey) + ->where('reservation_end_date', '>=', $dailyKey) + ->sortByDesc('id') + ->first(); + + if ($childAgePolicyDate) { + $childAgePolicy = $childAgePolicyDate; + } + + if (!empty($childAgePolicy)) { + if ($childAgePolicy['type'] == 'PER') { + $childAgePolicyAffected = ($oneAdultRoomRateRackPrice * $childAgePolicy['value']) / 100; + } elseif ($childAgePolicy['type'] == 'FIX') { + $childAgePolicyAffected = $childAgePolicy['value']; + } + + $childOrder++; + } + + $childPriceRaw[$i] = $childAgePolicyAffected; + + } + + + } + + + $childPrice = array_sum($childPriceRaw); + + $roomPrice = $adultPrice + $childPrice; + + + //NEW Promotoin Affect + $dailyDetailPromoCheckKey = $dailyRoomRate['property_id'] . '-' . + $dailyRoomRate['room_rate_mapping_id'] . '-' . + $dailyRoomRate['channel_id'] . '-' . + Carbon::parse($dailyKey)->format('Ymd'); + + if (array_key_exists($dailyDetailPromoCheckKey, $promotionGroupByDay)) { + $dailyRoomRate['discountRate'] = $promotionGroupByDay[$dailyDetailPromoCheckKey]['discountRate']; + $roomPrice = moneyDoubleFormatDecimal($roomPrice - ($roomPrice * ($promotionGroupByDay[$dailyDetailPromoCheckKey]['discountRate'] / 100))); + } + //NEW Promotoin Affect + + + $roomRateDaily[$dailyKey] = [ + 'date' => $dailyKey, + 'amount' => $roomPrice, + 'allotment' => fillOnUndefined($roomAndRoomRateAvailability, 'availability', 0), + 'rackRateAmount' => $dailyRoomRateRackPrice, + 'rackRateBaseAmount' => $dailyRoomRate['baseAmount'], + 'discountRate' => $dailyRoomRate['discountRate'], + 'oneAdultRackRateAmount' => $oneAdultRoomRateRackPrice, + 'min_stay' => $dailyRoomRate['min_stay'] + ]; + + } + + } + + + //Eğer günlere ait kontenjanlardan olmadığı bir gün gelir ise + $dailyRoomRateAvailabilityCheck = collect($roomRateDaily)->where('allotment', '<=', '0')->toArray(); + if (!empty($dailyRoomRateAvailabilityCheck)) { + continue; + } + + //Günler için de minimum doluluk oranına göre müsaitliğin alınması + $dailyRoomRateAvailabilityMin = collect($roomRateDaily)->sortBy('allotment')->first(); + $dailyRoomRateAllotment = $dailyRoomRateAvailabilityMin['allotment']; + + + //POLICY PRICES + $baseTotalPrice = array_sum(array_column($roomRateDaily, 'amount')); + $dailyAverageDiscount = floatval(array_sum(array_column($roomRateDaily, 'discountRate')) / count($roomRateDaily)); + $roomRateMappingCancellationPolicy = $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['roomRateMappingCancellationPolicy']; + + if (!empty($roomRateMappingCancellationPolicy)) { + foreach ($roomRateMappingCancellationPolicy as $roomRateMappingCancellationPolicyKey => $roomRateMappingCancellationPolicyValue) { + //Do not take into account if the number of rule days is greater than the remaining days. + /*if ($roomRateMappingCancellationPolicyValue['beforeArrivalDay'] > $daysLeftForCheckin && $roomRateMappingCancellationPolicyValue['isNonRefundable'] == 0) { + unset($roomRateMappingCancellationPolicy[$roomRateMappingCancellationPolicyKey]); + }*/ + + if ($roomRateMappingCancellationPolicyValue['is_date_range']) { + + $isDateInCancellationPolicyRange = false; + foreach ($this->dateByDay as $date) { + if ($date >= $roomRateMappingCancellationPolicyValue['start_date'] && $date <= $roomRateMappingCancellationPolicyValue['finish_date']) { + $isDateInCancellationPolicyRange = true; + break; + } + } + + if (!$isDateInCancellationPolicyRange) { + unset($roomRateMappingCancellationPolicy[$roomRateMappingCancellationPolicyKey]); + } + } + + } + } + + if (empty($roomRateMappingCancellationPolicy)) { + $roomRateMappingCancellationPolicy[] = [ + 'isAffectedPrice' => false, + 'isFreeCancellation' => true, + 'isNonRefundable' => false + ]; + } + + //Wholesalers can only use CHN payment type + if (in_array($channelProperty['channel']['channel_category_id'], [7])) { + + if (empty($channelBookingPaymentTypePolicy)) { + $channelBookingPaymentTypePolicy[2] = [ + 'name' => 'Channel Manager', + 'code' => 'CHN', + 'isAffectedPrice' => false + ]; + } + + } else { + + if (empty($channelBookingPaymentTypePolicy)) { + $channelBookingPaymentTypePolicy[2] = [ + 'name' => 'Pay at Hotel', + 'code' => 'HTL', + 'isAffectedPrice' => false + ]; + } + + } + + + //dd($roomRateMappingCancellationPolicy, $channelBookingPaymentTypePolicy); + + $policyPrices = []; + foreach ($roomRateMappingCancellationPolicy as $roomRateMappingCancellationPolicyKey => $roomRateMappingCancellationPolicy) { + foreach ($channelBookingPaymentTypePolicy as $channelBookingPaymentTypeKey => $channelBookingPaymentType) { + + //dd($channelBookingPaymentType['cancellationPolicy'], $roomRateMappingCancellationPolicyKey); + + + if (!empty($channelBookingPaymentType['cancellationPolicy'])) { + if (!in_array($roomRateMappingCancellationPolicyKey, $channelBookingPaymentType['cancellationPolicy'])) { + continue; + } + } + + + $cancellationPolicyAffectPrice = 0; + if ($roomRateMappingCancellationPolicy['isAffectedPrice']) { + + if ($roomRateMappingCancellationPolicy['affectPriceType'] == 'PER') { + $cancellationPolicyAffectPrice = ($baseTotalPrice * $roomRateMappingCancellationPolicy['affectPriceValue']) / 100; + } elseif ($roomRateMappingCancellationPolicy['affectPriceType'] == 'FIX') { + $cancellationPolicyAffectPrice = $roomRateMappingCancellationPolicy['affectPriceValue']; + } + + $cancellationPolicyAffectPrice = $roomRateMappingCancellationPolicy['affectPriceActionType'] == 'DEC' ? (-1 * $cancellationPolicyAffectPrice) : $cancellationPolicyAffectPrice; + + } + + $cancellationPolicyAffectPriceName = fillOnUndefined($roomRateMappingCancellationPolicy, 'name'); + if ($roomRateMappingCancellationPolicy['isNonRefundable']) { + $cancellationPolicyAffectPriceName = 'NonRefundable'; + } elseif ($roomRateMappingCancellationPolicy['isFreeCancellation']) { + $cancellationPolicyAffectPriceName = 'FreeCancellation'; + } + + $bookingPaymentTypeAffectPrice = 0; + if ($channelBookingPaymentType['isAffectedPrice']) { + + if ($channelBookingPaymentType['affectPriceType'] == 'PER') { + $bookingPaymentTypeAffectPrice = ($baseTotalPrice * $channelBookingPaymentType['affectPriceValue']) / 100; + } elseif ($channelBookingPaymentType['affectPriceType'] == 'FIX') { + $bookingPaymentTypeAffectPrice = $channelBookingPaymentType['affectPriceValue']; + } + + $bookingPaymentTypeAffectPrice = $channelBookingPaymentType['affectPriceActionType'] == 'DEC' ? (-1 * $bookingPaymentTypeAffectPrice) : $bookingPaymentTypeAffectPrice; + + } + + $bookingPaymentTypeAffectPriceName = fillOnUndefined($channelBookingPaymentType, 'name'); + + + //Bu dataları 10dk lık cache de tutacaksın, search_id ve $policyPriceKeyHashed bazında + $policyPriceKey = $channelProperty['property_id'] . '-' . $channelProperty['channel_id'] . '-' . $propertyAvailabilityKey . '-' . $propertyRoomKey . '-' . $propertyRoomRateKey . '-' . $roomsByOccupancyKey . '-' . Carbon::parse($params['date']['checkIn'])->format('Ymd') . '-' . Carbon::parse($params['date']['checkOut'])->format('Ymd') . '-' . $roomRateMappingCancellationPolicyKey . '-' . $channelBookingPaymentTypeKey; + $policyPriceKeyHashed = md5($policyPriceKey); + $policyPriceTotal = $baseTotalPrice + $cancellationPolicyAffectPrice + $bookingPaymentTypeAffectPrice; + + $policyPriceDaily = []; + $policyPriceRate = ($policyPriceTotal / $baseTotalPrice); + + foreach ($roomRateDaily as $daily) { + $policyPriceDaily[] = [ + 'date' => $daily['date'], + 'amount' => moneyDoubleFormatDecimal($daily['amount'] * $policyPriceRate), + 'currency_code' => $channelProperty['currency_code'], + 'min_stay' => fillOnUndefined($daily, 'min_stay', 0), + ]; + } + + $policyPrices[$policyPriceKeyHashed] = [ + 'rateKey' => $policyPriceKeyHashed, + 'rateKeyCode' => $policyPriceKey, + 'occupancyCode' => $roomsByOccupancyKey, + 'name' => $cancellationPolicyAffectPriceName . ' - ' . $bookingPaymentTypeAffectPriceName, + 'total' => moneyDoubleFormatDecimal($policyPriceTotal), + 'currency' => $channelProperty['currency_code'], + 'availability' => [ + 'name' => __($channelAvailabilityType[$propertyAvailabilityKey][0]['language_key']), + 'code' => $channelAvailabilityType[$propertyAvailabilityKey][0]['code'], + ], + 'room' => [ + 'name' => $propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['name'], + 'type' => __($propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['property_room_type']['language_key']), + 'allotment' => ($propertyAvailabilityKey == 1) ? $dailyRoomRateAllotment : null, + ], + 'rate' => [ + 'name' => $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room_rate']['name'], + 'boardCode' => $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room_rate']['id'], + 'boardName' => __($propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room_rate']['property_room_rate_accommodation']['language_key']), + 'accommodationCode' => $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room_rate']['property_room_rate_accommodation']['id'], + 'allotment' => ($propertyAvailabilityKey != 1) ? $dailyRoomRateAllotment : null, + ], + 'cancellationPolicy' => $roomRateMappingCancellationPolicy, + 'bookingPaymentType' => $channelBookingPaymentType, + 'policyPriceRate' => $policyPriceRate, + 'policyPriceDaily' => $policyPriceDaily, + ]; + + //Add to Cache + $searchPricesCacheWithKey['requestParams'] = $params; + $searchPricesCacheWithKey['roomPrices'][$policyPriceKeyHashed] = $policyPrices[$policyPriceKeyHashed]; + + } + + } + + $policyPrices = collect($policyPrices)->sortBy('total')->toArray(); + //POLICY PRICES + + $dailyCalculatedPrices = [ + 'prices' => $policyPrices, + 'baseTotalPrice' => $baseTotalPrice, + 'dailyAverageDiscount' => number_format($dailyAverageDiscount, 0, '', ''), + 'allotment' => $dailyRoomRateAllotment, + 'room' => [ + 'adults' => $roomsByOccupancy['adultCount'], + 'children' => $roomsByOccupancy['childCount'], + 'childAges' => $roomsByOccupancy['childAges'], + ], + 'daily' => $roomRateDaily, + ]; + + $calculatedRoomPrice + [$propertyAvailabilityKey] + [$propertyRoomKey] + [$propertyRoomRateKey] + [$roomsByOccupancyKey] = $dailyCalculatedPrices; + + //Fiyatlama kısmı son + + + } + + //dd($roomsByOccupancy, $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['included_occupancy']); + + } + } + } + + + //TODO: Burayı taşıyabilirz')); + foreach ($calculatedRoomPrice as $propertyAvailabilityKey => $propertyAvailability) { + + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['name'] = $channelProperty['property']['name']; + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['currency'] = $channelProperty['currency_code']; + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['token'] = $channelProperty['token']; + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['booking_engine_token'] = $channelProperty['property']['property_booking_engine_token']['token']; + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['credit_card_required'] = $payAtHotelCreditCardCheck; + + + + //Wholesaler Currency SET + if (!empty($channelPropertyMarkup)) { + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['currency'] = $channelPropertyMarkup['currency_code']; + } + //Wholesaler Currency SET + + + //$calculatedRoomPriceFormatted[$channelProperty['property']['id']]['paymentType'] = $channelProperty['channel_booking_payment_type']; + + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['name'] = __($channelAvailabilityType[$propertyAvailabilityKey][0]['language_key']); + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['code'] = $channelAvailabilityType[$propertyAvailabilityKey][0]['code']; + + + //En düşük fiyata göre sıralama + $propertyAvailability = collect($propertyAvailability); + $propertyAvailabilitySorted = $propertyAvailability->sortBy(function ($room, $roomKey) { + $firstRate = reset($room); + $firstRateOccupancy = reset($firstRate); + return $firstRateOccupancy['baseTotalPrice']; + }); + + $propertyAvailability = $propertyAvailabilitySorted->toArray(); + + + foreach ($propertyAvailability as $propertyRoomKey => $propertyRoom) { + + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['name'] = $propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['name']; + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['type'] = __($propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey]['property_room_type']['language_key']); + + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['attributes'] = fillOnUndefined($propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey], 'attributes', []); + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['bedGroups'] = fillOnUndefined($propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey], 'bedGroups', []); + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['photos'] = fillOnUndefined($propertyRoomAndRoomRateMappingData['room'][$propertyRoomKey], 'photos', []); + + //Wholesaler + if (in_array($channelProperty['channel']['channel_category_id'], [7]) && empty($this->bookingEngineToken) || fillOnUndefined($params, 'justPriceValues')) { + unset($calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['attributes']); + unset($calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['bedGroups']); + unset($calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['photos']); + } + + foreach ($propertyRoom as $propertyRoomRateKey => $propertyRoomRate) { + + + if ($propertyAvailabilityKey == 1) { + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['allotment'] = reset($propertyRoomRate)['allotment']; + } + + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['rates'][$propertyRoomRateKey]['name'] = $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room_rate']['name']; + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['rates'][$propertyRoomRateKey]['boardCode'] = $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room_rate']['property_room_rate_accommodation']['id']; + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['rates'][$propertyRoomRateKey]['boardName'] = __($propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room_rate']['property_room_rate_accommodation']['language_key']); + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['rates'][$propertyRoomRateKey]['rateDescription'] = isset($propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room_rate']['descriptionArray'][$this->language]) ? $propertyRoomAndRoomRateMappingData['roomRateMapping'][$propertyRoomRateKey]['property_room_rate']['descriptionArray'][$this->language]: null; + + if ($propertyAvailabilityKey != 1) { + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['rates'][$propertyRoomRateKey]['allotment'] = reset($propertyRoomRate)['allotment']; + } + + foreach ($propertyRoomRate as $propertyRoomOccupancyKey => $propertyRoomOccupancy) { + + $propertyRoomOccupancy['baseTotalPrice'] = moneyDoubleFormatDecimal($propertyRoomOccupancy['baseTotalPrice']); + foreach ($propertyRoomOccupancy['daily'] as $dailyKey => $daily) { + $propertyRoomOccupancy['daily'][$dailyKey]['amount'] = moneyDoubleFormatDecimal($daily['amount']); + $propertyRoomOccupancy['daily'][$dailyKey]['rackRateAmount'] = moneyDoubleFormatDecimal($daily['rackRateAmount']); + $propertyRoomOccupancy['daily'][$dailyKey]['oneAdultRackRateAmount'] = moneyDoubleFormatDecimal($daily['oneAdultRackRateAmount']); + } + + + //Wholesaler MARKUP CALCULATE + if (!empty($channelPropertyMarkup)) { + + foreach ($propertyRoomOccupancy['prices'] as $rateKey => $propertyRoomOccupancyPrice) { + + $currency = $channelPropertyMarkup['currency_code']; + $propertyRoomOccupancy['prices'][$rateKey]['currency'] = $currency; + + foreach ($propertyRoomOccupancyPrice['policyPriceDaily'] as $policyPriceDailyKey => $policyPriceDaily) { + + $policyPriceDailyAmount = moneyDoubleFormatDecimal($policyPriceDaily['amount'] * $channelPropertyMarkup['markup'] * $channelPropertyMarkup['exchange']); + + $propertyRoomOccupancy['prices'][$rateKey]['policyPriceDaily'][$policyPriceDailyKey]['amount'] = $policyPriceDailyAmount; + $propertyRoomOccupancy['prices'][$rateKey]['policyPriceDaily'][$policyPriceDailyKey]['currency_code'] = $currency; + + } + + $propertyRoomOccupancy['prices'][$rateKey]['total'] = collect($propertyRoomOccupancy['prices'][$rateKey]['policyPriceDaily'])->sum('amount'); + $propertyRoomOccupancy['prices'][$rateKey]['total'] = moneyDoubleFormatDecimal($propertyRoomOccupancy['prices'][$rateKey]['total']); + + + } + + foreach ($propertyRoomOccupancy['daily'] as $propertyRoomOccupancyDateKey => $propertyRoomOccupancyDate) { + + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['amount'] = $propertyRoomOccupancyDate['amount'] * $channelPropertyMarkup['markup']; + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['amount'] *= $channelPropertyMarkup['exchange']; + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['amount'] = moneyDoubleFormatDecimal($propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['amount']); + + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['rackRateAmount'] = $propertyRoomOccupancyDate['rackRateAmount'] * $channelPropertyMarkup['markup']; + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['rackRateAmount'] *= $channelPropertyMarkup['exchange']; + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['rackRateAmount'] = moneyDoubleFormatDecimal($propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['rackRateAmount']); + + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['rackRateBaseAmount'] = $propertyRoomOccupancyDate['rackRateBaseAmount'] * $channelPropertyMarkup['markup']; + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['rackRateBaseAmount'] *= $channelPropertyMarkup['exchange']; + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['rackRateBaseAmount'] = moneyDoubleFormatDecimal($propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['rackRateBaseAmount']); + + + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['oneAdultRackRateAmount'] = $propertyRoomOccupancyDate['oneAdultRackRateAmount'] * $channelPropertyMarkup['markup']; + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['oneAdultRackRateAmount'] *= $channelPropertyMarkup['exchange']; + $propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['oneAdultRackRateAmount'] = moneyDoubleFormatDecimal($propertyRoomOccupancy['daily'][$propertyRoomOccupancyDateKey]['oneAdultRackRateAmount']); + + } + + $propertyRoomOccupancy['baseTotalPrice'] = collect($propertyRoomOccupancy['daily'])->sum('amount'); + $propertyRoomOccupancy['baseTotalPrice'] = moneyDoubleFormatDecimal($propertyRoomOccupancy['baseTotalPrice']); + + } + //Wholesaler MARKUP CALCULATE + + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['availabilities'][$propertyAvailabilityKey]['rooms'][$propertyRoomKey]['rates'][$propertyRoomRateKey]['requestedRoomPrice'][$propertyRoomOccupancyKey] = $propertyRoomOccupancy; + + } + } + } + } + + + //ADDON Setup + if (!empty($calculatedRoomPriceFormatted)) { + $propertyChannelAddonCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $channelProperty['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelProperty['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyAddon.fact'] + ]; + $columns = ['id', 'property_id', 'channel_id', 'property_addon_id', 'amount', 'type', 'title', 'description', 'min_stay']; + $selectPropertyChannelAddon = $this->propertyAddonService->selectPropertyChannelAddon($propertyChannelAddonCriteria, $columns); + + $nightOfStay = Carbon::parse($params['date']['checkIn'])->diff(Carbon::parse($params['date']['checkOut']))->days; + + $propertyChannelAddon = []; + if ($selectPropertyChannelAddon['status'] == 'success') { + $propertyChannelAddon = $selectPropertyChannelAddon['data']; + } + + $propertyChannelAddonList = []; + foreach ($propertyChannelAddon as $addon) { + + + if (!is_null($addon['min_stay']) && $addon['min_stay'] <= $nightOfStay) { + $addon['amount'] = 0; + } + + $propertyChannelAddonList[$addon['property_addon_id']][] = [ + 'id' => $addon['id'], + 'property_addon_id' => $addon['property_addon_id'], + 'property_channel_addon_id' => $addon['id'], + 'title' => $addon['title'], + 'description' => $addon['description'], + 'descriptionArray' => $addon['descriptionArray'], + 'amount' => $addon['amount'], + 'currency_code' => $channelProperty['currency_code'], + 'name' => $addon['property_addon']['title'], + 'language_key' => $addon['property_addon']['fact']['language_key'], + 'icon' => $addon['property_addon']['fact']['icon'], + 'attribute' => $addon['property_addon']['attribute'], + 'attributeArray' => $addon['property_addon']['attributeArray'], + 'type' => $addon['type'], + 'count' => $addon['type'] == 'ONT' ? 1 : 10, + 'min_stay' => $addon['min_stay'], + ]; + } + + $propertyChannelAddonList = array_values($propertyChannelAddonList); + + $calculatedRoomPriceFormatted[$channelProperty['property']['id']]['addon'] = $propertyChannelAddonList; + } + //ADDON Setup + + + } + + //Add Prices to Cache With SearchKey + //If this parameter (noneCacheSearch) exists, it is not cached. + if (!isset($params['noneCacheSearch'])) { + $this->setCacheDataWithSearchKey($searchKey, $searchPricesCacheWithKey); + } + + + $ip = isset($params['ipAddress']) ? $params['ipAddress'] : config("app.myIP"); + $countryCodeWithIP = $this->findCountryCodeService->findCountryWithIpAddress($ip); + + $responseData = [ + 'searchKey' => $searchKey, + 'properties' => $calculatedRoomPriceFormatted, + 'requestParams' => $params, + 'countryCodeWithIP' => isset($countryCodeWithIP['data']['code']) ? $countryCodeWithIP['data']['code'] : '' + ]; + + if (empty($calculatedRoomPriceFormatted) && (integer)$minStayCheckProperty > 1 && count($dateByDay) < (integer)$minStayCheckProperty) { + throw new ApiErrorException(__('be-minstay-warning', ['minStay' => $minStayCheckProperty], $this->language), 201); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'errorCode' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + $response['errorCode'] = $e->getCode(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + //201: be-minstay-warning + + //For Booking Engine + if (isset($channelProperty['channel']['channel_category_id']) && in_array($channelProperty['channel']['channel_category_id'], [3, 7])) { + + $createSearchStatus = 0; + if (isset($response['data']['properties'])) { + $calculatedRoomPriceFormattedReset = reset($calculatedRoomPriceFormatted); + if (isset($calculatedRoomPriceFormattedReset['name'])) { + $createSearchStatus = 1; + } + } + + $createSearchExcludeIps = ['127.0.0.1', '185.137.215.118']; + if ((isset($params['ipAddress']) && !in_array($params['ipAddress'], $createSearchExcludeIps)) || in_array($channelProperty['channel_id'], [393])) { + + $isRobot = fillOnUndefined($params, 'isRobot'); + + if (isset($params['noneCacheSearch'])) { + $isRobot = true; + } + + if (!$isRobot) { + + $createSearchParam = [ + 'search_key' => $searchKey, + 'property_id' => fillOnUndefined($channelProperty, 'property_id'), + 'channel_id' => fillOnUndefined($channelProperty, 'channel_id', 1), + 'checkin_date' => $params['date']['checkIn'], + 'checkout_date' => $params['date']['checkOut'], + 'pax' => array_key_first($this->roomsByOccupancies), + 'rooms' => json_encode($params['rooms']), + 'booking_code' => fillOnUndefined($params, 'booking_code'), + 'best_amount' => fillOnUndefined($params, 'best_amount'), + 'currency_code' => fillOnUndefined($channelProperty, 'currency_code'), + 'ip_address' => fillOnUndefined($params, 'ipAddress'), + 'country_code' => isset($countryCodeWithIP['data']['code']) ? $countryCodeWithIP['data']['code'] : null, + 'language_code' => $this->language, + 'detail' => json_encode($params), + 'referrer' => fillOnUndefined($params, 'referrer'), + 'status' => $createSearchStatus, + ]; + + if (in_array($channelProperty['channel_id'], [393])) { + $createSearchParam['property_id'] = null; + } + + + $createSearch = $this->propertyBookingEngineSearchService->create($createSearchParam); + } + + } + + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode'], $response['errorCode']); + + + } + + + public function bestAvailableRate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $excludePropertyIds = [643,644,645,738,163,164,165,166,236,366]; + + $this->channelToken = empty($this->channelToken) ? $request->header('channelToken') : $this->channelToken; + $this->bookingEngineToken = empty($this->bookingEngineToken) ? $request->header('bookingEngineToken') : $this->bookingEngineToken; + $this->channelId = empty($this->channelId) ? $request->header('channelId') : $this->channelId; + $this->bookingEnginePropertyId = empty($this->bookingEnginePropertyId) ? $request->header('bookingEnginePropertyId') : $this->bookingEnginePropertyId; + $this->language = empty($this->language) ? $request->header('language') : $this->language; + + $params = json_decode($request->getContent(), 1); + + $cacheKeyParam[] = $this->bookingEnginePropertyId; + $cacheKeyParam[] = $params['period']; + $cacheKey = 'CRR:' . md5(implode(',', $cacheKeyParam)); + + if(in_array($this->bookingEnginePropertyId, $excludePropertyIds)) { + throw new ApiErrorException('Property Channel not found'); + } + + //Cache::forget($cacheKey); + if (Cache::has($cacheKey)) { + $responseData = Cache::get($cacheKey); + } else { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $this->bookingEnginePropertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['currency'], + 'firstRow' => true + ]; + + $getChannelProperty = $this->propertyChannelMappingService->select($requestParam); + $getChannelProperty = $getChannelProperty['status'] == 'success' && !empty($getChannelProperty['data']) ? $getChannelProperty['data'] : null; + + if (!$getChannelProperty) { + throw new ApiErrorException('Property Channel not found'); + } + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => 5], + ['field' => 'property_id', 'condition' => '=', 'value' => $this->bookingEnginePropertyId], + ['field' => 'stop_sell', 'condition' => '=', 'value' => 0], + ['field' => 'date', 'condition' => '>=', 'value' => Carbon::parse($params['period'])->format('Y-m-d')], + ['field' => 'date', 'condition' => '<', 'value' => Carbon::parse($params['period'])->addMonth()->format('Y-m-d')], + ['field' => 'amount', 'condition' => '<>', 'value' => null], + ['field' => 'amount', 'condition' => '<>', 'value' => 0], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'roomRateMapping.propertyRoomRate' + ], + 'orderBy' => [ + ['field' => 'date', 'value' => 'ASC'], + ['field' => 'amount', 'value' => 'ASC'] + ], + ]; + + $getPropertyRoomRatePrice = $this->propertyRoomRatePriceService->select($requestParam); + + + if ($getPropertyRoomRatePrice['status'] != 'success' || empty($getPropertyRoomRatePrice['data'])) { + throw new ApiErrorException(lang('PropertyRoomRatePrice not found')); + } + + $getPropertyRoomRatePrice = $getPropertyRoomRatePrice['status'] == 'success' && !empty($getPropertyRoomRatePrice['data']) ? $getPropertyRoomRatePrice['data'] : []; + + $firstDate = Carbon::parse($params['period'])->format('Y-m-d'); + $lastDate = Carbon::parse($params['period'])->addMonth()->format('Y-m-d'); + $dateDiff = Carbon::parse($firstDate)->diffInDays(Carbon::parse($lastDate)); + + $rateAndAvailability = []; + for ($i = 0; $i < $dateDiff; $i++) { + $date = Carbon::parse($firstDate)->addDays($i)->toDateString(); + + $bestPrice = collect($getPropertyRoomRatePrice) + ->where('date', $date) + ->where('room_rate_mapping.property_room_rate.name', 'Best Available Rate') + ->sortBy('amount')->first(); + $bestPrice = $bestPrice ? $bestPrice['amount'] : null; + + $rateAndAvailability[$date] = [ + 'rate' => round($bestPrice), + 'available' => $bestPrice ? true : false, + ]; + + } + + $responseData = [ + 'currency' => $getChannelProperty['currency']['code'], + 'currency_icon' => $getChannelProperty['currency']['symbol'], + 'date' => $rateAndAvailability + ]; + + if (count($rateAndAvailability) == collect($rateAndAvailability)->where('available', false)->count()) { + $responseData = null; + } + + Cache::put($cacheKey, $responseData, 60 * 60);//1h 60 * 60 + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + +} diff --git a/app/Http/Controllers/ChannelManager/Athena/v1/AthenaController.php b/app/Http/Controllers/ChannelManager/Athena/v1/AthenaController.php new file mode 100644 index 0000000..e93de45 --- /dev/null +++ b/app/Http/Controllers/ChannelManager/Athena/v1/AthenaController.php @@ -0,0 +1,488 @@ +username = 'athena'; + $this->password = 'AU3EUmA9LChTzAzv'; + + $this->request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyRoomService = $propertyRoomService; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerLogService = $channelManagerLogService; + + $payload = $this->request->getContent(); + $payloadJson = json_decode($payload, 1); + $this->param = $payloadJson; + + $this->channelId = 1; + $this->channelManagerId = 5; //Athena + + $getRequestUri = $request->getRequestUri(); + $getRequestUriExplode = explode('/', $getRequestUri); + $serviceRequestName = last($getRequestUriExplode); + + //channelManagerLogService + $logArray = ['update-room-availability', 'update-room-rate']; + $this->channelManagerLogId = null; + if (in_array($serviceRequestName, $logArray)) { + $insertDataLog = [ + 'property_id' => $this->param['hotel_id'], + 'channel_manager_id' => $this->channelManagerId, + 'type' => 'C2E', + 'service' => $serviceRequestName, + 'request' => json_encode($payloadJson), + 'response' => null, + 'status' => null + ]; + + + $channelManagerLog = $this->channelManagerLogService->create($insertDataLog); + if ($channelManagerLog['status'] == 'success') { + $this->channelManagerLogId = $channelManagerLog['data']['id']; + } + } + //channelManagerLogService + + + } + + public function checkAuthentication($username, $password) + { + + $response = ['status' => false, 'message' => '']; + + if ($this->username != $username || $this->password != $password) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function responseSuccess($responseData = null) + { + + $response = [ + 'status' => true, + 'message' => null, + 'data' => $responseData, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 1 + ]; + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function propertyChannelMapping($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + //['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + if (!is_null($channelManagerPropertyMapping['channel_manager_property_id'])) { + throw new ApiErrorException('This hotel can only be updated by ENW'); + } + + $response = [ + 'status' => true + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function roomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $response = []; + + $roomRates = []; + foreach ($channelPropertyRoomRate as $roomRate) { + + if($roomRate['property_room_rate_mapping']['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + + if($roomRate['status'] == 0) { + continue; + } + + if(empty($roomRate['property_room_rate_mapping'])) { + continue; + } + + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']]['room'] = [ + 'id' => $roomRate['property_room_rate_mapping']['property_room']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room']['name'], + 'status' => 'Active', + 'capacity' => $roomRate['property_room_rate_mapping']['property_room']['max_adult'], + 'capacity_child' => $roomRate['property_room_rate_mapping']['property_room']['max_child'], + ]; + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']] + ['rate'][$roomRate['property_room_rate_mapping']['id']] = [ + 'id' => $roomRate['property_room_rate_mapping']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['name'], + 'rate' => $roomRate['property_room_rate_mapping']['property_room_rate']['name'], + 'accommodationId' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['id'], + ]; + + } + + $roomKey = 0; + $response['rooms'] = []; + foreach ($roomRates as $roomId => $roomRate) { + + $response['rooms'][$roomKey] = [ + 'id' => $roomId, + 'room_name' => $roomRate['room']['name'], + ]; + + $roomRateKey = 0; + $response['rooms'][$roomKey]['rates'] = []; + foreach ($roomRate['rate'] as $roomRateMappingId => $rateData) { + + $response['rooms'][$roomKey]['rates'][$roomRateKey] = [ + 'id' => $roomRateMappingId, + 'rate_name' => $rateData['name'] . ' - ' . $rateData['rate'], + 'board_id' => $rateData['accommodationId'], + 'board_name' => $rateData['name'], + ]; + + $roomRateKey++; + + } + + $roomKey++; + } + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function channel(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $propertyChannelCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['parentChannel','propertyChannelCategory'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + ]; + + $propertyChannel = $this->propertyChannelService->select($propertyChannelCriteria); + if (!$propertyChannel['status']) { + throw new ApiErrorException($propertyChannel['message']); + } + + $propertyChannel = $propertyChannel['data']; + + $response = []; + + $response['channels'] = []; + foreach ($propertyChannel as $channel) { + + $response['channels'][] = [ + 'id' => $channel['id'], + 'name' => $channel['name'], + 'category_id' => $channel['property_channel_category']['id'], + 'category_name' => $channel['property_channel_category']['name'], + ]; + } + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + +} diff --git a/app/Http/Controllers/ChannelManager/Channex/v1/ChannexController.php b/app/Http/Controllers/ChannelManager/Channex/v1/ChannexController.php new file mode 100644 index 0000000..3962525 --- /dev/null +++ b/app/Http/Controllers/ChannelManager/Channex/v1/ChannexController.php @@ -0,0 +1,228 @@ +username = 'channex'; + $this->password = 'AU3EUmA9LChTzAzv'; + + $this->request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->mailer = $mailer; + $this->channexService = $channexService; + + $payloadJson = $this->request->all(); + $this->param = $payloadJson; + + Log::debug($payloadJson); + + $this->channelManagerId = 2; //Channex + + } + + public function checkAuthentication($username, $password) + { + + $response = ['status' => false, 'message' => '']; + + if ($this->username != $username || $this->password != $password) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function responseSuccess($responseData = null) + { + + $response = [ + 'status' => true, + 'message' => null, + 'data' => $responseData, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 1 + ]; + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + + public function channelManagerProperty($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'with' => ['property'], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property mapping not found'); + } + + + $response = [ + 'status' => true, + 'data' => $channelManagerPropertyMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function notification(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['property_id']; + + $propertyChannelMapping = $this->channelManagerProperty($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $channelDetails = $this->channexService->getChannelDetails($this->param['payload']['channel_id']); + if (!$channelDetails['status']) { + throw new ApiErrorException($channelDetails['message']); + } + $channelDetails = $channelDetails['data']; + + + $mailParams = [ + 'propertyId' => $propertyChannelMapping['property']['id'], + 'propertyName' => $propertyChannelMapping['property']['name'], + 'channelName' => $channelDetails['attributes']['channel'] + ]; + + $this->mailer->onQueue('channelManagerNotificationMail', new ChannelManagerNotificationMail($mailParams)); + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + +} diff --git a/app/Http/Controllers/ChannelManager/ElektraWeb/v1/ElektraWebController.php b/app/Http/Controllers/ChannelManager/ElektraWeb/v1/ElektraWebController.php new file mode 100644 index 0000000..a58d101 --- /dev/null +++ b/app/Http/Controllers/ChannelManager/ElektraWeb/v1/ElektraWebController.php @@ -0,0 +1,893 @@ +username = 'elektraweb'; + $this->password = 'XGgK2BSYCERDaVAx'; + + $this->request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyRoomService = $propertyRoomService; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerLogService = $channelManagerLogService; + + $payload = $this->request->getContent(); + $payloadJson = json_decode($payload, 1); + $this->param = $payloadJson; + + $this->channelId = 1; + $this->channelManagerId = 4; //ElektraWeb + + $getRequestUri = $request->getRequestUri(); + $getRequestUriExplode = explode('/', $getRequestUri); + $serviceRequestName = last($getRequestUriExplode); + + //channelManagerLogService + $logArray = ['update-room-availability', 'update-room-rate']; + $this->channelManagerLogId = null; + $this->channelManagerRequestTime = microtime(true); + if (in_array($serviceRequestName, $logArray)) { + $insertDataLog = [ + 'property_id' => $this->param['hotel_id'], + 'channel_manager_id' => $this->channelManagerId, + 'type' => 'C2E', + 'service' => $serviceRequestName, + 'request' => json_encode($payloadJson), + 'ip_address' => $this->request->ip(), + 'response' => null, + 'status' => null + ]; + + + $channelManagerLog = $this->channelManagerLogService->create($insertDataLog); + if ($channelManagerLog['status'] == 'success') { + $this->channelManagerLogId = $channelManagerLog['data']['id']; + } + } + //channelManagerLogService + + } + + public function checkAuthentication($username, $password) + { + + $response = ['status' => false, 'message' => '']; + + if ($this->username != $username || $this->password != $password) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function responseSuccess($responseData = null) + { + + $response = [ + 'status' => true, + 'message' => null, + 'data' => $responseData, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 1 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function propertyChannelMapping($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $getChannelPropertyRoomRate['data'] = collect($getChannelPropertyRoomRate['data'])->where('property_room_rate_mapping.property_room.status',1)->toArray(); + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + //TODO: burada otelinde kendisine bakmak lazım status + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + if (!is_null($channelManagerPropertyMapping['channel_manager_property_id'])) { + throw new ApiErrorException('This hotel can only be updated by ENW'); + } + + $response = [ + 'status' => true + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function roomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $response = []; + + $roomRates = []; + foreach ($channelPropertyRoomRate as $roomRate) { + + if($roomRate['property_room_rate_mapping']['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']]['room'] = [ + 'id' => $roomRate['property_room_rate_mapping']['property_room']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room']['name'], + 'status' => 'Active', + 'max_adult' => $roomRate['property_room_rate_mapping']['property_room']['max_adult'], + 'max_child' => $roomRate['property_room_rate_mapping']['property_room']['max_child'], + 'max_occupancy' => $roomRate['property_room_rate_mapping']['property_room']['max_occupancy'], + ]; + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']] + ['rate'][$roomRate['property_room_rate_mapping']['id']] = [ + 'id' => $roomRate['property_room_rate_mapping']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['name'], + 'rate' => $roomRate['property_room_rate_mapping']['property_room_rate']['name'], + 'accommodationId' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['id'], + 'included_occupancy' => $roomRate['property_room_rate_mapping']['included_occupancy'], + ]; + + } + + $roomKey = 0; + $response['rooms'] = []; + foreach ($roomRates as $roomId => $roomRate) { + + $response['rooms'][$roomKey] = [ + 'id' => $roomId, + 'room_name' => $roomRate['room']['name'], + 'max_adult' => $roomRate['room']['max_adult'], + 'max_child' => $roomRate['room']['max_child'], + 'max_occupancy' => $roomRate['room']['max_occupancy'], + ]; + + $roomRateKey = 0; + $response['rooms'][$roomKey]['rates'] = []; + foreach ($roomRate['rate'] as $roomRateMappingId => $rateData) { + + $response['rooms'][$roomKey]['rates'][$roomRateKey] = [ + 'id' => $roomRateMappingId, + 'rate_name' => $rateData['name'] . ' - ' . $rateData['rate'], + 'board_id' => $rateData['accommodationId'], + 'board_name' => $rateData['name'], + 'included_occupancy' => floor($rateData['included_occupancy']), + ]; + + $roomRateKey++; + + } + + $roomKey++; + } + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function updateRoomAvailability(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $roomAvailabilities = $this->param['rooms']; + + // Merge consecutive dates with same availability and stop_sell + usort($roomAvailabilities, function ($a, $b) { + if ($a['room_id'] === $b['room_id']) { + return strcmp($a['start_date'], $b['start_date']); + } + return $a['room_id'] <=> $b['room_id']; + }); + + $mergedAvailabilities = []; + $currentAvailability = null; + + foreach ($roomAvailabilities as $availability) { + if ($currentAvailability === null) { + $currentAvailability = $availability; + continue; + } + + $currentEnd = Carbon::parse($currentAvailability['end_date']); + $nextStart = Carbon::parse($availability['start_date']); + + $isConsecutive = $currentEnd->addDay()->toDateString() === $nextStart->toDateString(); + + $sameRoomId = ($currentAvailability['room_id'] === $availability['room_id']); + $sameAvailability = (isset($currentAvailability['availability']) && isset($availability['availability']) && $currentAvailability['availability'] === $availability['availability']) || (!isset($currentAvailability['availability']) && !isset($availability['availability'])); + $sameStopSell = (isset($currentAvailability['stop_sell']) && isset($availability['stop_sell']) && $currentAvailability['stop_sell'] === $availability['stop_sell']) || (!isset($currentAvailability['stop_sell']) && !isset($availability['stop_sell'])); + + if ($isConsecutive && $sameRoomId && $sameAvailability && $sameStopSell) { + $currentAvailability['end_date'] = $availability['end_date']; + } else { + $mergedAvailabilities[] = $currentAvailability; + $currentAvailability = $availability; + } + } + + if ($currentAvailability !== null) { + $mergedAvailabilities[] = $currentAvailability; + } + + $roomAvailabilities = $mergedAvailabilities; + $roomAvailabilitiesCollect = collect($roomAvailabilities); + + DB::beginTransaction(); + + $startDate = $roomAvailabilitiesCollect->sortBy('start_date')->first(); + $startDate = $startDate['start_date']; + + $endDate = $roomAvailabilitiesCollect->sortByDesc('end_date')->first(); + $endDate = $endDate['end_date']; + + if (Carbon::parse($startDate)->isBefore(Carbon::now()->toDateString()) || Carbon::parse($endDate)->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + foreach ($roomAvailabilities as $availability) { + + $roomId = $availability['room_id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + throw new ApiErrorException('Undefined or inactive room accommodation'); + } + + $totalInventoryAvailable = null; + if (isset($availability['availability'])) { + $totalInventoryAvailable = $availability['availability']; + } + + $startDate = Carbon::parse($availability['start_date'])->toDateString(); + $endDate = Carbon::parse($availability['end_date'])->toDateString(); + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $startDate, + 'end_date' => $endDate, + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + + if (!is_null($totalInventoryAvailable)) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'availability'; + $requestParams['value'] = $totalInventoryAvailable; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + if (isset($availability['stop_sell']) && !is_null($availability['stop_sell'])) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'room_stop_sell'; + $requestParams['value'] = $availability['stop_sell']; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + } + + DB::commit(); + + return $this->responseSuccess(['confirmCode' => $this->channelManagerLogId]); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function updateRoomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $roomRates = $this->param['rooms']; + $roomRatesCollect = collect($roomRates); + + $paramsListChannel = []; + $paramsListChannel[] = $this->channelId; + + //CONNECTED CHANNEL RATE UPDATE + /*$propertyChannelMappingConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'channel_id', 'condition' => '=', 'value' => 5],//JUST CM + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + ]; + + $propertyChannelMappingConnected = $this->propertyChannelMappingService->select($propertyChannelMappingConnectedCriteria); + if ($propertyChannelMappingConnected['status'] == 'success') { + + foreach ($propertyChannelMappingConnected['data'] as $channel) { + + if (!is_null($channel['channel']['parent_id'])) { + continue; + } + + $paramsListChannel[] = $channel['channel_id']; + + } + + }*/ + //CONNECTED CHANNEL RATE UPDATE + + + DB::beginTransaction(); + + foreach ($roomRates as $roomRate) { + + $roomId = $roomRate['room_id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + //throw new ApiErrorException('Undefined or inactive room accommodation'); + continue; + } + + $rates = $roomRate['rates']; + if (empty($rates)) { + continue; + } + + // Sort by rate_id and then start_date to ensure we process them in order for each rate_id + usort($rates, function ($a, $b) { + if ($a['rate_id'] === $b['rate_id']) { + return strcmp($a['start_date'], $b['start_date']); + } + return strcmp($a['rate_id'], $b['rate_id']); + }); + + $mergedRates = []; + $currentRate = null; + + foreach ($rates as $rate) { + if ($currentRate === null) { + $currentRate = $rate; + continue; + } + + $currentEnd = Carbon::parse($currentRate['end_date']); + $nextStart = Carbon::parse($rate['start_date']); + + $isConsecutive = $currentEnd->addDay()->toDateString() === $nextStart->toDateString(); + + $sameRateId = ($currentRate['rate_id'] === $rate['rate_id']); + $sameAmount = (isset($currentRate['amount']) && isset($rate['amount']) && (float)$currentRate['amount'] === (float)$rate['amount']) || (!isset($currentRate['amount']) && !isset($rate['amount'])); + $sameStopSell = (isset($currentRate['stop_sell']) && isset($rate['stop_sell']) && $currentRate['stop_sell'] === $rate['stop_sell']) || (!isset($currentRate['stop_sell']) && !isset($rate['stop_sell'])); + $sameMinStay = (isset($currentRate['min_stay']) && isset($rate['min_stay']) && $currentRate['min_stay'] === $rate['min_stay']) || (!isset($currentRate['min_stay']) && !isset($rate['min_stay'])); + + if ($isConsecutive && $sameRateId && $sameAmount && $sameStopSell && $sameMinStay) { + $currentRate['end_date'] = $rate['end_date']; + } else { + $mergedRates[] = $currentRate; + $currentRate = $rate; + } + } + + if ($currentRate !== null) { + $mergedRates[] = $currentRate; + } + + + $roomRatesCollect = collect($mergedRates); + + $startDate = $roomRatesCollect->sortBy('start_date')->first(); + $startDate = $startDate['start_date']; + + $endDate = $roomRatesCollect->sortByDesc('end_date')->first(); + $endDate = $endDate['end_date']; + + if (Carbon::parse($startDate)->isBefore(Carbon::now()->toDateString()) || Carbon::parse($endDate)->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + foreach ($mergedRates as $rate) { + + $startDate = Carbon::parse($rate['start_date'])->toDateString(); + $endDate = Carbon::parse($rate['end_date'])->toDateString(); + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $startDate, + 'end_date' => $endDate, + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + + $roomRateMappingId = $rate['rate_id']; + + $isCloseRoomRateMappingSale = null; + if (isset($rate['stop_sell'])) { + $isCloseRoomRateMappingSale = $rate['stop_sell'] == 1 ? true : false; + } + + + $channelRoomRateMappingCheck = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->isEmpty(); + if ($channelRoomRateMappingCheck) { + //throw new ApiErrorException('Undefined or inactive room accommodation'); + continue; + } + + $roomRates = []; + $roomRates[] = [ + 'room_id' => $roomId, + 'room_rate_mapping_id' => [ + $roomRateMappingId + ] + ]; + + foreach ($paramsListChannel as $paramChannelId) { + + //Eğer Rate var ise currency check yapılmalı ve PerDay var mı check edilmeli, burada sonra günceleme yaptırılabilri + if (isset($rate['amount'])) { + + $currency = $this->param['currency']; + + $currencyCheck = ($currency == $propertyChannelMapping['currency_code']) ? true : false; + + if (!$currencyCheck) { + throw new ApiErrorException('Exchange rate that does not match the channel exchange rate, channel exchange rate: ' . $propertyChannelMapping['currency_code']); + } + + $channelRoomRateMapping = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->first(); + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate'; + $requestParams['value'] = $rate['amount']; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + $requestParams['ip_address'] = $this->request->ip(); + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + //Room Rate Stop Sale + if (!is_null($isCloseRoomRateMappingSale)) { + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate_stop_sell'; + $requestParams['value'] = $isCloseRoomRateMappingSale ? 1 : 0; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + $requestParams['ip_address'] = $this->request->ip(); + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + } + + + //Kısıtlamalar var ise min los + if (isset($rate['min_stay'])) { + + //Minimum Konaklama Gün Sayısı + if (isset($rate['min_stay'])) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'min_stay'; + $requestParams['value'] = $rate['min_stay']; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + $requestParams['ip_address'] = $this->request->ip(); + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + + } + + } + + } + + + } + } + + DB::commit(); + + + return $this->responseSuccess(['confirmCode' => $this->channelManagerLogId]); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + +} diff --git a/app/Http/Controllers/ChannelManager/ElektraWeb/v1/_ElektraWebController.php b/app/Http/Controllers/ChannelManager/ElektraWeb/v1/_ElektraWebController.php new file mode 100644 index 0000000..b4c83f4 --- /dev/null +++ b/app/Http/Controllers/ChannelManager/ElektraWeb/v1/_ElektraWebController.php @@ -0,0 +1,809 @@ +username = 'elektraweb'; + $this->password = 'XGgK2BSYCERDaVAx'; + + $this->request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyRoomService = $propertyRoomService; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerLogService = $channelManagerLogService; + + $payload = $this->request->getContent(); + $payloadJson = json_decode($payload, 1); + $this->param = $payloadJson; + + $this->channelId = 1; + $this->channelManagerId = 4; //ElektraWeb + + $getRequestUri = $request->getRequestUri(); + $getRequestUriExplode = explode('/', $getRequestUri); + $serviceRequestName = last($getRequestUriExplode); + + //channelManagerLogService + $logArray = ['update-room-availability', 'update-room-rate']; + $this->channelManagerLogId = null; + $this->channelManagerRequestTime = microtime(true); + if (in_array($serviceRequestName, $logArray)) { + $insertDataLog = [ + 'property_id' => $this->param['hotel_id'], + 'channel_manager_id' => $this->channelManagerId, + 'type' => 'C2E', + 'service' => $serviceRequestName, + 'request' => json_encode($payloadJson), + 'ip_address' => $this->request->ip(), + 'response' => null, + 'status' => null + ]; + + + $channelManagerLog = $this->channelManagerLogService->create($insertDataLog); + if ($channelManagerLog['status'] == 'success') { + $this->channelManagerLogId = $channelManagerLog['data']['id']; + } + } + //channelManagerLogService + + } + + public function checkAuthentication($username, $password) + { + + $response = ['status' => false, 'message' => '']; + + if ($this->username != $username || $this->password != $password) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function responseSuccess($responseData = null) + { + + $response = [ + 'status' => true, + 'message' => null, + 'data' => $responseData, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 1 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function propertyChannelMapping($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $getChannelPropertyRoomRate['data'] = collect($getChannelPropertyRoomRate['data'])->where('property_room_rate_mapping.property_room.status',1)->toArray(); + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + //TODO: burada otelinde kendisine bakmak lazım status + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + if (!is_null($channelManagerPropertyMapping['channel_manager_property_id'])) { + throw new ApiErrorException('This hotel can only be updated by ENW'); + } + + $response = [ + 'status' => true + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function roomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $response = []; + + $roomRates = []; + foreach ($channelPropertyRoomRate as $roomRate) { + + if($roomRate['property_room_rate_mapping']['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']]['room'] = [ + 'id' => $roomRate['property_room_rate_mapping']['property_room']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room']['name'], + 'status' => 'Active', + 'max_adult' => $roomRate['property_room_rate_mapping']['property_room']['max_adult'], + 'max_child' => $roomRate['property_room_rate_mapping']['property_room']['max_child'], + 'max_occupancy' => $roomRate['property_room_rate_mapping']['property_room']['max_occupancy'], + ]; + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']] + ['rate'][$roomRate['property_room_rate_mapping']['id']] = [ + 'id' => $roomRate['property_room_rate_mapping']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['name'], + 'rate' => $roomRate['property_room_rate_mapping']['property_room_rate']['name'], + 'accommodationId' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['id'], + 'included_occupancy' => $roomRate['property_room_rate_mapping']['included_occupancy'], + ]; + + } + + $roomKey = 0; + $response['rooms'] = []; + foreach ($roomRates as $roomId => $roomRate) { + + $response['rooms'][$roomKey] = [ + 'id' => $roomId, + 'room_name' => $roomRate['room']['name'], + 'max_adult' => $roomRate['room']['max_adult'], + 'max_child' => $roomRate['room']['max_child'], + 'max_occupancy' => $roomRate['room']['max_occupancy'], + ]; + + $roomRateKey = 0; + $response['rooms'][$roomKey]['rates'] = []; + foreach ($roomRate['rate'] as $roomRateMappingId => $rateData) { + + $response['rooms'][$roomKey]['rates'][$roomRateKey] = [ + 'id' => $roomRateMappingId, + 'rate_name' => $rateData['name'] . ' - ' . $rateData['rate'], + 'board_id' => $rateData['accommodationId'], + 'board_name' => $rateData['name'], + 'included_occupancy' => floor($rateData['included_occupancy']), + ]; + + $roomRateKey++; + + } + + $roomKey++; + } + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function updateRoomAvailability(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $roomAvailabilities = $this->param['rooms']; + $roomAvailabilitiesCollect = collect($roomAvailabilities); + + DB::beginTransaction(); + + $startDate = $roomAvailabilitiesCollect->sortBy('start_date')->first(); + $startDate = $startDate['start_date']; + + $endDate = $roomAvailabilitiesCollect->sortByDesc('end_date')->first(); + $endDate = $endDate['end_date']; + + if (Carbon::parse($startDate)->isBefore(Carbon::now()->toDateString()) || Carbon::parse($endDate)->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + foreach ($roomAvailabilities as $availability) { + + $roomId = $availability['room_id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + throw new ApiErrorException('Undefined or inactive room accommodation'); + } + + $totalInventoryAvailable = null; + if (isset($availability['availability'])) { + $totalInventoryAvailable = $availability['availability']; + } + + $startDate = Carbon::parse($availability['start_date'])->toDateString(); + $endDate = Carbon::parse($availability['end_date'])->toDateString(); + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $startDate, + 'end_date' => $endDate, + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + + if (!is_null($totalInventoryAvailable)) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'availability'; + $requestParams['value'] = $totalInventoryAvailable; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + if (isset($availability['stop_sell']) && !is_null($availability['stop_sell'])) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'room_stop_sell'; + $requestParams['value'] = $availability['stop_sell']; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + } + + DB::commit(); + + return $this->responseSuccess(['confirmCode' => $this->channelManagerLogId]); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function updateRoomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $roomRates = $this->param['rooms']; + $roomRatesCollect = collect($roomRates); + + $paramsListChannel = []; + $paramsListChannel[] = $this->channelId; + + //CONNECTED CHANNEL RATE UPDATE + /*$propertyChannelMappingConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'channel_id', 'condition' => '=', 'value' => 5],//JUST CM + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + ]; + + $propertyChannelMappingConnected = $this->propertyChannelMappingService->select($propertyChannelMappingConnectedCriteria); + if ($propertyChannelMappingConnected['status'] == 'success') { + + foreach ($propertyChannelMappingConnected['data'] as $channel) { + + if (!is_null($channel['channel']['parent_id'])) { + continue; + } + + $paramsListChannel[] = $channel['channel_id']; + + } + + }*/ + //CONNECTED CHANNEL RATE UPDATE + + + DB::beginTransaction(); + + foreach ($roomRates as $roomRate) { + + $roomId = $roomRate['room_id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + //throw new ApiErrorException('Undefined or inactive room accommodation'); + continue; + } + + + $roomRatesCollect = collect($roomRate['rates']); + + $startDate = $roomRatesCollect->sortBy('start_date')->first(); + $startDate = $startDate['start_date']; + + $endDate = $roomRatesCollect->sortByDesc('end_date')->first(); + $endDate = $endDate['end_date']; + + if (Carbon::parse($startDate)->isBefore(Carbon::now()->toDateString()) || Carbon::parse($endDate)->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + foreach ($roomRate['rates'] as $rate) { + + $startDate = Carbon::parse($rate['start_date'])->toDateString(); + $endDate = Carbon::parse($rate['end_date'])->toDateString(); + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $startDate, + 'end_date' => $endDate, + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + + $roomRateMappingId = $rate['rate_id']; + + $isCloseRoomRateMappingSale = null; + if (isset($rate['stop_sell'])) { + $isCloseRoomRateMappingSale = $rate['stop_sell'] == 1 ? true : false; + } + + + $channelRoomRateMappingCheck = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->isEmpty(); + if ($channelRoomRateMappingCheck) { + //throw new ApiErrorException('Undefined or inactive room accommodation'); + continue; + } + + $roomRates = []; + $roomRates[] = [ + 'room_id' => $roomId, + 'room_rate_mapping_id' => [ + $roomRateMappingId + ] + ]; + + foreach ($paramsListChannel as $paramChannelId) { + + //Eğer Rate var ise currency check yapılmalı ve PerDay var mı check edilmeli, burada sonra günceleme yaptırılabilri + if (isset($rate['amount'])) { + + $currency = $this->param['currency']; + + $currencyCheck = ($currency == $propertyChannelMapping['currency_code']) ? true : false; + + if (!$currencyCheck) { + throw new ApiErrorException('Exchange rate that does not match the channel exchange rate, channel exchange rate: ' . $propertyChannelMapping['currency_code']); + } + + $channelRoomRateMapping = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->first(); + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate'; + $requestParams['value'] = $rate['amount']; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + $requestParams['ip_address'] = $this->request->ip(); + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + //Room Rate Stop Sale + if (!is_null($isCloseRoomRateMappingSale)) { + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate_stop_sell'; + $requestParams['value'] = $isCloseRoomRateMappingSale ? 1 : 0; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + $requestParams['ip_address'] = $this->request->ip(); + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + } + + + //Kısıtlamalar var ise min los + if (isset($rate['min_stay'])) { + + //Minimum Konaklama Gün Sayısı + if (isset($rate['min_stay'])) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'min_stay'; + $requestParams['value'] = $rate['min_stay']; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + $requestParams['ip_address'] = $this->request->ip(); + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + + } + + } + + } + + + } + } + + DB::commit(); + + + return $this->responseSuccess(['confirmCode' => $this->channelManagerLogId]); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + +} diff --git a/app/Http/Controllers/ChannelManager/Fina/v1/FinaController.php b/app/Http/Controllers/ChannelManager/Fina/v1/FinaController.php new file mode 100644 index 0000000..8302dc5 --- /dev/null +++ b/app/Http/Controllers/ChannelManager/Fina/v1/FinaController.php @@ -0,0 +1,488 @@ +username = 'fina'; + $this->password = '6T3VpfsNvLwWFY2gtXjz8y'; + + $this->request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyRoomService = $propertyRoomService; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerLogService = $channelManagerLogService; + + $payload = $this->request->getContent(); + $payloadJson = json_decode($payload, 1); + $this->param = $payloadJson; + + $this->channelId = 1; + $this->channelManagerId = 8; //Fina + + $getRequestUri = $request->getRequestUri(); + $getRequestUriExplode = explode('/', $getRequestUri); + $serviceRequestName = last($getRequestUriExplode); + + //channelManagerLogService + $logArray = ['update-room-availability', 'update-room-rate']; + $this->channelManagerLogId = null; + if (in_array($serviceRequestName, $logArray)) { + $insertDataLog = [ + 'property_id' => $this->param['hotel_id'], + 'channel_manager_id' => $this->channelManagerId, + 'type' => 'C2E', + 'service' => $serviceRequestName, + 'request' => json_encode($payloadJson), + 'response' => null, + 'status' => null + ]; + + + $channelManagerLog = $this->channelManagerLogService->create($insertDataLog); + if ($channelManagerLog['status'] == 'success') { + $this->channelManagerLogId = $channelManagerLog['data']['id']; + } + } + //channelManagerLogService + + + } + + public function checkAuthentication($username, $password) + { + + $response = ['status' => false, 'message' => '']; + + if ($this->username != $username || $this->password != $password) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function responseSuccess($responseData = null) + { + + $response = [ + 'status' => true, + 'message' => null, + 'data' => $responseData, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 1 + ]; + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function propertyChannelMapping($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + //['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + if (!is_null($channelManagerPropertyMapping['channel_manager_property_id'])) { + throw new ApiErrorException('This hotel can only be updated by ENW'); + } + + $response = [ + 'status' => true + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function roomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $response = []; + + $roomRates = []; + foreach ($channelPropertyRoomRate as $roomRate) { + + if($roomRate['property_room_rate_mapping']['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + + if($roomRate['status'] == 0) { + continue; + } + + if(empty($roomRate['property_room_rate_mapping'])) { + continue; + } + + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']]['room'] = [ + 'id' => $roomRate['property_room_rate_mapping']['property_room']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room']['name'], + 'status' => 'Active', + 'capacity' => $roomRate['property_room_rate_mapping']['property_room']['max_adult'], + 'capacity_child' => $roomRate['property_room_rate_mapping']['property_room']['max_child'], + ]; + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']] + ['rate'][$roomRate['property_room_rate_mapping']['id']] = [ + 'id' => $roomRate['property_room_rate_mapping']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['name'], + 'rate' => $roomRate['property_room_rate_mapping']['property_room_rate']['name'], + 'accommodationId' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['id'], + ]; + + } + + $roomKey = 0; + $response['rooms'] = []; + foreach ($roomRates as $roomId => $roomRate) { + + $response['rooms'][$roomKey] = [ + 'id' => $roomId, + 'room_name' => $roomRate['room']['name'], + ]; + + $roomRateKey = 0; + $response['rooms'][$roomKey]['rates'] = []; + foreach ($roomRate['rate'] as $roomRateMappingId => $rateData) { + + $response['rooms'][$roomKey]['rates'][$roomRateKey] = [ + 'id' => $roomRateMappingId, + 'rate_name' => $rateData['name'] . ' - ' . $rateData['rate'], + 'board_id' => $rateData['accommodationId'], + 'board_name' => $rateData['name'], + ]; + + $roomRateKey++; + + } + + $roomKey++; + } + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function channel(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $propertyChannelCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['parentChannel','propertyChannelCategory'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + ]; + + $propertyChannel = $this->propertyChannelService->select($propertyChannelCriteria); + if (!$propertyChannel['status']) { + throw new ApiErrorException($propertyChannel['message']); + } + + $propertyChannel = $propertyChannel['data']; + + $response = []; + + $response['channels'] = []; + foreach ($propertyChannel as $channel) { + + $response['channels'][] = [ + 'id' => $channel['id'], + 'name' => $channel['name'], + 'category_id' => $channel['property_channel_category']['id'], + 'category_name' => $channel['property_channel_category']['name'], + ]; + } + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + +} diff --git a/app/Http/Controllers/ChannelManager/HotelRunner/v1/HotelRunnerController.php b/app/Http/Controllers/ChannelManager/HotelRunner/v1/HotelRunnerController.php new file mode 100644 index 0000000..c1747ca --- /dev/null +++ b/app/Http/Controllers/ChannelManager/HotelRunner/v1/HotelRunnerController.php @@ -0,0 +1,825 @@ +username = 'hotelrunner'; + $this->password = '2otNDLCgJz9Tgdga'; + + $this->request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyRoomService = $propertyRoomService; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerLogService = $channelManagerLogService; + + $payload = $this->request->getContent(); + $payloadXML = simplexml_load_string($payload); + $payloadParam = json_decode(json_encode($payloadXML), 1); + + $this->param = $payloadParam; + + $this->channelId = 1; + $this->channelManagerId = 3; //HotelRunner + + $getRequestUri = $request->getRequestUri(); + $getRequestUriExplode = explode('/', $getRequestUri); + $serviceRequestName = last($getRequestUriExplode); + + + //channelManagerLogService + $logArray = ['room-inventory-update']; + $this->channelManagerLogId = null; + $this->channelManagerRequestTime = microtime(true); + if (in_array($serviceRequestName, $logArray)) { + $insertDataLog = [ + 'property_id' => $this->param['hotel_id'], + 'channel_manager_id' => $this->channelManagerId, + 'type' => 'C2E', + 'service' => $serviceRequestName, + 'request' => $payload, + 'response' => null, + 'ip_address' => $this->request->ip(), + 'status' => null + ]; + + + $channelManagerLog = $this->channelManagerLogService->create($insertDataLog); + if ($channelManagerLog['status'] == 'success') { + $this->channelManagerLogId = $channelManagerLog['data']['id']; + } + } + //channelManagerLogService + + + //Authentication + $errors = []; + if ($this->username != $payloadParam['username'] || $this->password != $payloadParam['password']) { + $errors[] = [ + //'code' => 100, + 'message' => 'Your username or password is incorrect.' + ]; + } + + if (!empty($errors)) { + $this->responseError($errors); + } + + + } + + + public function responseError($errors) + { + + $xmlResponse = new \SimpleXMLElement(''); + + $xmlResponse->addChild('status', -1); + + foreach ($errors as $error) { + $xmlResponseError = $xmlResponse->addChild('message', $error['message']); + if (isset($error['code'])) { + $xmlResponseError->addAttribute('code', $error['code']); + } + } + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => $xmlResponse->asXML(), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + header('Content-type: text/xml'); + echo $xmlResponse->asXML(); + + die(); + } + + public function responseSuccess($xmlPayload) + { + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => $xmlPayload->asXML(), + 'status' => 1 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + header('Content-type: text/xml'); + //echo $xmlPayload->asXML(); + echo html_entity_decode($xmlPayload->asXML()); + + die(); + } + + public function propertyChannelMapping($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $getChannelPropertyRoomRate['data'] = collect($getChannelPropertyRoomRate['data'])->where('property_room_rate_mapping.property_room.status',1)->toArray(); + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + if (!is_null($channelManagerPropertyMapping['channel_manager_property_id'])) { + throw new ApiErrorException('This hotel can only be updated by ENW'); + } + + $response = [ + 'status' => true + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function roomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $xmlResponse = new \SimpleXMLElement(''); + + $roomRates = []; + foreach ($channelPropertyRoomRate as $roomRate) { + + if($roomRate['property_room_rate_mapping']['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']]['room'] = [ + 'id' => $roomRate['property_room_rate_mapping']['property_room']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room']['name'], + 'status' => 'Active', + 'capacity' => $roomRate['property_room_rate_mapping']['property_room']['max_adult'], + 'capacity_child' => $roomRate['property_room_rate_mapping']['property_room']['max_child'], + ]; + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']] + ['rate'][$roomRate['property_room_rate_mapping']['id']] = [ + 'id' => $roomRate['property_room_rate_mapping']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['name'], + 'rate' => $roomRate['property_room_rate_mapping']['property_room_rate']['name'], + 'accommodationId' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['id'], + 'included_occupancy' => $roomRate['property_room_rate_mapping']['included_occupancy'], + ]; + + } + + foreach ($roomRates as $roomId => $roomRate) { + + $room = $xmlResponse->addChild('room'); + $room->addAttribute('id', $roomId); + $room->addAttribute('room_name', $roomRate['room']['name']); + + + $rates = $room->addChild('rates'); + foreach ($roomRate['rate'] as $roomRateMappingId => $rateData) { + $rate = $rates->addChild('rate'); + $rate->addAttribute('id', $roomRateMappingId); + $rate->addAttribute('board_id', $rateData['accommodationId']); + $rate->addAttribute('rate_name', $rateData['name'] . ' - ' . $rateData['rate'].' - '. $rateData['included_occupancy'].' Person'); + $rate->addAttribute('allocation_group', $roomId); + $rate->addAttribute('stop_sale_group', $roomId . ':' . $roomRateMappingId); + $rate->addAttribute('min_stay_group', $roomId . ':' . $roomRateMappingId); + $rate->addAttribute('included_occupancy', $rateData['included_occupancy']); + } + + } + + + $this->responseSuccess($xmlResponse); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + $errors[] = [ + 'message' => $response['message'] + ]; + + $this->responseError($errors); + + } + + } + + public function availabilityRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $startDate = $this->param['start_date']; + $finishDate = $this->param['end_date']; + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $requestParams = [ + 'property_id' => $propertyId, + 'room_id' => null, + 'room_rate_mapping_id' => null, + 'channel_id' => $this->channelId, + 'start_date' => $startDate, + 'end_date' => $finishDate, + ]; + + $propertyRoomType = $this->propertyRoomService->getPropertyRoomInventory($requestParams); + if ($propertyRoomType['status'] != 'success') { + throw new ApiErrorException($propertyRoomType['message']); + } + + $propertyRoomType = $propertyRoomType['data']; + $propertyRoomRateAvailability = collect($propertyRoomType); + + + $roomRates = []; + foreach ($propertyRoomRateAvailability as $room) { + + foreach ($room['property_room_rate_mapping'] as $roomRateMapping) { + + + foreach ($room['room_availability'] as $date => $roomAvailability) { + $roomRates[$room['id']]['availability'][$date] = $roomAvailability['value']; + } + + $roomRates[$room['id']]['rate'][$roomRateMapping['id']] = [ + 'roomName' => $room['name'], + 'roomRateName' => $roomRateMapping['name'], + //'minStay' => $roomRateMapping['min_stay'], + //'maxStay' => $roomRateMapping['max_stay'], + 'currencyCode' => $roomRateMapping['currency_code'], + ]; + + + $roomRatePrices = reset($roomRateMapping['prices']); + foreach ($roomRatePrices['price'] as $date => $roomRatePrice) { + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['price'][$date] = $roomRatePrice['value']; + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['stopSell'][$date] = $roomRatePrice['stop_sell']; + } + + foreach ($roomRatePrices['stop_sell'] as $date => $stopSell) { + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['stopSell'][$date] = $stopSell['value']; + } + + foreach ($roomRatePrices['min_stay'] as $date => $minStay) { + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['minstay'][$date] = $minStay['value']; + } + + } + } + + + $xmlResponse = new \SimpleXMLElement(''); + + $diffInDays = Carbon::parse($startDate)->diffInDays(Carbon::parse($finishDate)); + + if ($diffInDays > 180) { + throw new ApiErrorException('A maximum of 180 days of data can be retrieved.'); + } + + $currentDate = $startDate; + for ($i = 0; $i <= $diffInDays; $i++) { + + + foreach ($roomRates as $roomId => $roomRateMapping) { + + foreach ($roomRateMapping['rate'] as $roomRateMappingId => $roomRate) { + + $inventory = $xmlResponse->addChild('inventory'); + + $inventory->addAttribute('room_id', $roomId); + $inventory->addAttribute('rate_id', $roomRateMappingId); + $inventory->addAttribute('date', $currentDate); + $inventory->addAttribute('price', $roomRate['price'][$currentDate]); + $inventory->addAttribute('allocation', $roomRateMapping['availability'][$currentDate]); + $inventory->addAttribute('stop_sale', $roomRate['stopSell'][$currentDate] == 1 ? 1 : 0); + $inventory->addAttribute('min_stay', $roomRate['minstay'][$currentDate]); + } + } + + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + + } + + + $this->responseSuccess($xmlResponse); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + $errors[] = [ + 'message' => $response['message'] + ]; + + $this->responseError($errors); + + } + + } + + public function availabilityRateUpdate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $availRateUpdates = singleElementXMLArray($this->param['inventories']['inventory']); + + DB::beginTransaction(); + + $dateRange = []; + $dateRange['startDate'] = $this->param['start_date']; + $dateRange['finishDate'] = $this->param['end_date']; + + if (Carbon::parse($dateRange['startDate'])->isBefore(Carbon::now()->toDateString()) || Carbon::parse($dateRange['finishDate'])->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + foreach ($availRateUpdates as $availRateUpdate) { + + $roomId = $availRateUpdate['@attributes']['room_id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + throw new ApiErrorException('Undefined or inactive room accommodation'); + } + + $totalInventoryAvailable = null; + if (isset($availRateUpdate['@attributes']['allocation'])) { + $totalInventoryAvailable = $availRateUpdate['@attributes']['allocation']; + } + + $currentDate = Carbon::parse($availRateUpdate['@attributes']['date'])->toDateString(); + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $currentDate, + 'end_date' => $currentDate, + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + + if (!is_null($totalInventoryAvailable)) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'availability'; + $requestParams['value'] = $totalInventoryAvailable; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + $roomRateMappingId = $availRateUpdate['@attributes']['rate_id']; + + $isCloseRoomRateMappingSale = null; + if (isset($availRateUpdate['@attributes']['stop_sale'])) { + $isCloseRoomRateMappingSale = $availRateUpdate['@attributes']['stop_sale'] == 1 ? true : false; + } + + $channelRoomRateMappingCheck = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->isEmpty(); + if ($channelRoomRateMappingCheck) { + throw new ApiErrorException('Undefined or inactive room accommodation'); + } + + $roomRates = []; + $roomRates[] = [ + 'room_id' => $roomId, + 'room_rate_mapping_id' => [ + $roomRateMappingId + ] + ]; + + + $paramsListChannel = []; + $paramsListChannel[] = $this->channelId; + + //CONNECTED CHANNEL RATE UPDATE + $propertyChannelMappingConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParamBase['property_id']], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $requestParamBase['channel_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => 5],//JUST CM + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + ]; + + $propertyChannelMappingConnected = $this->propertyChannelMappingService->select($propertyChannelMappingConnectedCriteria); + if ($propertyChannelMappingConnected['status'] == 'success') { + + foreach ($propertyChannelMappingConnected['data'] as $channel) { + + if (!is_null($channel['channel']['parent_id'])) { + continue; + } + + $paramsListChannel[] = $channel['channel_id']; + + } + + } + //CONNECTED CHANNEL RATE UPDATE + + + foreach ($paramsListChannel as $paramChannelId) { + + //Eğer Rate var ise currency check yapılmalı ve PerDay var mı check edilmeli, burada sonra günceleme yaptırılabilri + if (isset($availRateUpdate['@attributes']['price'])) { + + $currency = $this->param['currency']; + + $currencyCheck = ($currency == $propertyChannelMapping['currency_code']) ? true : false; + + if (!$currencyCheck) { + throw new ApiErrorException('Exchange rate that does not match the channel exchange rate, channel exchange rate: ' . $propertyChannelMapping['currency_code']); + } + + $channelRoomRateMapping = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->first(); + + $amountPerDay = $availRateUpdate['@attributes']['price']; + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate'; + $requestParams['value'] = $amountPerDay; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + //Room Rate Stop Sale + if (!is_null($isCloseRoomRateMappingSale)) { + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate_stop_sell'; + $requestParams['value'] = $isCloseRoomRateMappingSale ? 1 : 0; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + } + + + //Kısıtlamalar var ise min los + if (isset($availRateUpdate['@attributes']['min_stay'])) { + + //Minimum Konaklama Gün Sayısı + if (isset($availRateUpdate['@attributes']['min_stay'])) { + + $minStay = $availRateUpdate['@attributes']['min_stay']; + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'min_stay'; + $requestParams['value'] = $minStay; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + + } + + } + + } + + + } + + DB::commit(); + + $xmlResponse = new \SimpleXMLElement(''); + + $xmlResponse->addChild('status', 0); + $xmlResponse->addChild('message', 'success'); + $xmlResponse->addChild('ConfirmCode', $this->channelManagerLogId); + + $this->responseSuccess($xmlResponse); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + $errors[] = [ + 'message' => $response['message'] + ]; + + $this->responseError($errors); + + } + + } + + +} diff --git a/app/Http/Controllers/ChannelManager/HyperGuest/v1/HyperGuestController.php b/app/Http/Controllers/ChannelManager/HyperGuest/v1/HyperGuestController.php new file mode 100644 index 0000000..194a3c9 --- /dev/null +++ b/app/Http/Controllers/ChannelManager/HyperGuest/v1/HyperGuestController.php @@ -0,0 +1,635 @@ +request = $request; + $this->restClient = $restClient; + $this->mailer = $mailer; + $this->param = $this->request->all(); + + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyRoomService = $propertyRoomService; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerLogService = $channelManagerLogService; + + if (App::environment() == 'production') { + $this->url = 'https://pdm.hyperguest.io/api/'; + $this->urlstatic = 'https://hg-static.hyperguest.com/'; + $this->bearerToken = '63d51938dc3749788ac3e88ea6d58950'; + } else { + $this->url = 'https://pdm.hyperguest.com/api/'; + $this->urlstatic = 'https://hg-static.hyperguest.com/'; + $this->bearerToken = '63d51938dc3749788ac3e88ea6d58950'; + } + + + $this->channelId = 1; + $this->channelManagerId = 10; + $this->channelManagerName = 'HyperGuest'; + + $getRequestUri = $request->getRequestUri(); + $getRequestUriExplode = explode('/', $getRequestUri); + $serviceRequestName = last($getRequestUriExplode); + + //channelManagerLogService + $logArray = ['room-inventory-update']; + $this->channelManagerLogId = null; + $this->channelManagerRequestTime = microtime(true); + if (in_array($serviceRequestName, $logArray)) { + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($this->param['propertyId']); + + if ($channelManagerPropertyCheck['status']) { + $insertDataLog = [ + 'property_id' => $channelManagerPropertyCheck['data']['property_id'], + 'channel_manager_id' => $this->channelManagerId, + 'type' => 'C2E', + 'service' => $serviceRequestName, + 'request' => json_encode($this->param), + 'response' => null, + 'ip_address' => $this->request->ip(), + 'status' => null + ]; + + $channelManagerLog = $this->channelManagerLogService->create($insertDataLog); + if ($channelManagerLog['status'] == 'success') { + $this->channelManagerLogId = $channelManagerLog['data']['id']; + } + } + + } + //channelManagerLogService + + } + + public function request($type, $method, $jsonPayload) + { + $response = ['status' => false, 'message' => '']; + + try { + $this->restClient = new Client(['http_errors' => false]); + + if ($type == 'POST') { + + $parameter = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->bearerToken + ] + ]; + + if (!empty($jsonPayload)) { + $parameter['body'] = $jsonPayload; + } + + $res = $this->restClient->request('POST', $this->url . $method, $parameter); + + $getResponseBody = $res->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponse = json_decode($getResponse, 1); + + } else if ($type == 'GET') { + + + $res = $this->restClient->request('GET', $this->url . $method, + [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->bearerToken + ], + 'query' => $jsonPayload + ] + ); + + $getResponseBody = $res->getBody(); + $getResponse = $getResponseBody->getContents(); + $getResponse = json_decode($getResponse, 1); + + + } + + if ($res->getStatusCode() != 200) { + $errors = singleElementArray($getResponse['errors']); + $firstError = reset($errors); + throw new Exception($firstError['code'] . ': ' . $firstError['title']); + } + + + $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, 'data', [])]; + + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + return $response; + + } + + public function responseError($errors) + { + + $response = ['success' => false, 'message' => null]; + $response['message'] = implode(',', $errors); + + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + + return response()->json($response); + } + + public function responseSuccess($data = []) + { + + $response = ['success' => true, 'message' => null]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 1 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + } + + public function propertyChannelMapping($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'with' => ['channelManagerRoomRate.propertyRoomRateMapping'], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + $response = [ + 'status' => true, + 'data' => $channelManagerPropertyMapping + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function availabilityRateUpdate(Request $request) + { + + $errors = []; + $response = ['status' => false, 'message' => '']; + + try { + + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($this->param['propertyId']); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelManagerPropertyCheck = $channelManagerPropertyCheck['data']; + $channelManagerPropertyRoomRateCollect = collect($channelManagerPropertyCheck['channel_manager_room_rate']); + + $propertyId = $channelManagerPropertyCheck['property_id']; + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $availRateUpdates = $this->param['ARIUpdate']; + + DB::beginTransaction(); + + /*$dateRange = []; + $dateRange['startDate'] = $this->param['start_date']; + $dateRange['finishDate'] = $this->param['end_date']; + + if (Carbon::parse($dateRange['startDate'])->isBefore(Carbon::now()->toDateString()) || Carbon::parse($dateRange['finishDate'])->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + }*/ + + foreach ($availRateUpdates as $availRateUpdate) { + + $roomIdCheck = $channelManagerPropertyRoomRateCollect->where('channel_manager_room_id', $availRateUpdate['roomTypeCode'])->first(); + if (empty($roomIdCheck)) { + continue; + } + + $roomId = $roomIdCheck['property_room_rate_mapping']['room_id']; + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + continue; + } + + $totalInventoryAvailable = null; + if (isset($availRateUpdate['numberOfAvailableRooms'])) { + $totalInventoryAvailable = $availRateUpdate['numberOfAvailableRooms']; + } + + + $currentDate = Carbon::parse($availRateUpdate['date'])->toDateString(); + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $currentDate, + 'end_date' => $currentDate, + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + + if (!is_null($totalInventoryAvailable)) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'availability'; + $requestParams['value'] = $totalInventoryAvailable; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + foreach ($availRateUpdate['ratePlans'] as $ratePlan) { + + + $roomRateMappingIdCheck = $channelManagerPropertyRoomRateCollect->where('channel_manager_room_id', $availRateUpdate['roomTypeCode'])->where('channel_manager_room_rate_id', $ratePlan['ratePlanCode'])->first(); + if (empty($roomRateMappingIdCheck)) { + continue; + } + + $roomRateMappingId = $roomRateMappingIdCheck['property_room_rate_mapping_id']; + + $channelRoomRateMappingCheck = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->isEmpty(); + if ($channelRoomRateMappingCheck) { + continue; + } + + + $includedOccupancy = (int)$roomRateMappingIdCheck['included_occupancy']; + $ratePlanPrice = collect($ratePlan['prices'])->where('numberOfGuests.adults', $includedOccupancy) + ->where('numberOfGuests.adults', $includedOccupancy) + ->where('numberOfGuests.children', 0) + ->where('numberOfGuests.infants', 0) + ->first(); + + + if (empty($ratePlanPrice)) { + continue; + } + + $ratePlanPrice = $ratePlanPrice['price']; + + $roomRates = []; + $roomRates[] = [ + 'room_id' => $roomId, + 'room_rate_mapping_id' => [ + $roomRateMappingId + ] + ]; + + + $paramsListChannel = []; + $paramsListChannel[] = $this->channelId; + + //CONNECTED CHANNEL RATE UPDATE + $propertyChannelMappingConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParamBase['property_id']], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $requestParamBase['channel_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => 5],//JUST CM + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + ]; + + $propertyChannelMappingConnected = $this->propertyChannelMappingService->select($propertyChannelMappingConnectedCriteria); + if ($propertyChannelMappingConnected['status'] == 'success') { + + foreach ($propertyChannelMappingConnected['data'] as $channel) { + + if (!is_null($channel['channel']['parent_id'])) { + continue; + } + + $paramsListChannel[] = $channel['channel_id']; + + } + + } + //CONNECTED CHANNEL RATE UPDATE + + + foreach ($paramsListChannel as $paramChannelId) { + + //Eğer Rate var ise currency check yapılmalı ve PerDay var mı check edilmeli, burada sonra günceleme yaptırılabilri + if (isset($ratePlanPrice)) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate'; + $requestParams['value'] = $ratePlanPrice; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + //Room Rate Stop Sale + $isCloseRoomRateMappingSale = null; + if (isset($ratePlan['isOpen'])) { + $isCloseRoomRateMappingSale = $ratePlan['isOpen'] == 1 ? false : true; + } + + if (!is_null($isCloseRoomRateMappingSale)) { + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate_stop_sell'; + $requestParams['value'] = $isCloseRoomRateMappingSale ? 1 : 0; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + } + + + //Minimum Konaklama Gün Sayısı + if (isset($ratePlan['minLOS'])) { + + $minStay = $ratePlan['minLOS']; + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'min_stay'; + $requestParams['value'] = $minStay; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + + } + + + } + + + } + + + } + + DB::commit(); + + + //$this->channelManagerLogId + + return $this->responseSuccess(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + $errors[] = $response['message']; + + return $this->responseError($errors); + + } + + } + + +} diff --git a/app/Http/Controllers/ChannelManager/OneC/v1/OneCController.php b/app/Http/Controllers/ChannelManager/OneC/v1/OneCController.php new file mode 100644 index 0000000..9289892 --- /dev/null +++ b/app/Http/Controllers/ChannelManager/OneC/v1/OneCController.php @@ -0,0 +1,640 @@ +username = '1CHotel'; + $this->password = 'w2Dffb9EGXPZfiUDxWKZqB'; + + $this->request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyRoomService = $propertyRoomService; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerLogService = $channelManagerLogService; + + $payload = $this->request->getContent(); + $payloadJson = json_decode($payload, 1); + $this->param = $payloadJson; + + $this->channelId = 1; + $this->channelManagerId = 9; //1C Hotels + + $getRequestUri = $request->getRequestUri(); + $getRequestUriExplode = explode('/', $getRequestUri); + $serviceRequestName = last($getRequestUriExplode); + + //channelManagerLogService + $logArray = ['update-room-availability', 'update-room-rate']; + $this->channelManagerLogId = null; + if (in_array($serviceRequestName, $logArray)) { + $insertDataLog = [ + 'property_id' => $this->param['hotel_id'], + 'channel_manager_id' => $this->channelManagerId, + 'type' => 'C2E', + 'service' => $serviceRequestName, + 'request' => json_encode($payloadJson), + 'response' => null, + 'status' => null + ]; + + + $channelManagerLog = $this->channelManagerLogService->create($insertDataLog); + if ($channelManagerLog['status'] == 'success') { + $this->channelManagerLogId = $channelManagerLog['data']['id']; + } + } + //channelManagerLogService + + + } + + public function checkAuthentication($username, $password) + { + + $response = ['status' => false, 'message' => '']; + + if ($this->username != $username || $this->password != $password) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function responseSuccess($responseData = null) + { + + $response = [ + 'status' => true, + 'message' => null, + 'data' => $responseData, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 1 + ]; + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function propertyChannelMapping($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + //['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + if (!is_null($channelManagerPropertyMapping['channel_manager_property_id'])) { + throw new ApiErrorException('This hotel can only be updated by ENW'); + } + + $response = [ + 'status' => true + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function roomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $response = []; + + $roomRates = []; + foreach ($channelPropertyRoomRate as $roomRate) { + + if($roomRate['property_room_rate_mapping']['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + + if($roomRate['status'] == 0) { + continue; + } + + if(empty($roomRate['property_room_rate_mapping'])) { + continue; + } + + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']]['room'] = [ + 'id' => $roomRate['property_room_rate_mapping']['property_room']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room']['name'], + 'status' => 'Active', + 'capacity' => $roomRate['property_room_rate_mapping']['property_room']['max_adult'], + 'capacity_child' => $roomRate['property_room_rate_mapping']['property_room']['max_child'], + ]; + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']] + ['rate'][$roomRate['property_room_rate_mapping']['id']] = [ + 'id' => $roomRate['property_room_rate_mapping']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['name'], + 'rate' => $roomRate['property_room_rate_mapping']['property_room_rate']['name'], + 'accommodationId' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['id'], + ]; + + } + + $roomKey = 0; + $response['rooms'] = []; + foreach ($roomRates as $roomId => $roomRate) { + + $response['rooms'][$roomKey] = [ + 'id' => $roomId, + 'room_name' => $roomRate['room']['name'], + ]; + + $roomRateKey = 0; + $response['rooms'][$roomKey]['rates'] = []; + foreach ($roomRate['rate'] as $roomRateMappingId => $rateData) { + + $response['rooms'][$roomKey]['rates'][$roomRateKey] = [ + 'id' => $roomRateMappingId, + 'rate_name' => $rateData['name'] . ' - ' . $rateData['rate'], + 'board_id' => $rateData['accommodationId'], + 'board_name' => $rateData['name'], + ]; + + $roomRateKey++; + + } + + $roomKey++; + } + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function channel(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $propertyChannelCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['parentChannel','propertyChannelCategory'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + ]; + + $propertyChannel = $this->propertyChannelService->select($propertyChannelCriteria); + if (!$propertyChannel['status']) { + throw new ApiErrorException($propertyChannel['message']); + } + + $propertyChannel = $propertyChannel['data']; + + $response = []; + + $response['channels'] = []; + foreach ($propertyChannel as $channel) { + + $response['channels'][] = [ + 'id' => $channel['id'], + 'name' => $channel['name'], + 'category_id' => $channel['property_channel_category']['id'], + 'category_name' => $channel['property_channel_category']['name'], + ]; + } + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function updateRoomAvailability(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $roomAvailabilities = $this->param['rooms']; + $roomAvailabilitiesCollect = collect($roomAvailabilities); + + DB::beginTransaction(); + + $startDate = $roomAvailabilitiesCollect->sortBy('start_date')->first(); + $startDate = $startDate['start_date']; + + $endDate = $roomAvailabilitiesCollect->sortByDesc('end_date')->first(); + $endDate = $endDate['end_date']; + + if (Carbon::parse($startDate)->isBefore(Carbon::now()->toDateString()) || Carbon::parse($endDate)->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + foreach ($roomAvailabilities as $availability) { + + $roomId = $availability['room_id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + throw new ApiErrorException('Undefined or inactive room accommodation'); + } + + $totalInventoryAvailable = null; + if (isset($availability['availability'])) { + $totalInventoryAvailable = $availability['availability']; + } + + $startDate = Carbon::parse($availability['start_date'])->toDateString(); + $endDate = Carbon::parse($availability['end_date'])->toDateString(); + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $startDate, + 'end_date' => $endDate, + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + //Days Filter + if (isset($availability['days']) && !empty($availability['days']) && is_array($availability['days']) && count($availability['days']) <= 7) { + + $requestParams = [ + 'days' => $availability['days'] + ]; + + $validator = Validator::make($requestParams, ['days' => 'required|array|in:Mon,Tue,Wed,Thu,Fri,Sat,Sun']);//Mon,Tue,Wed,Thu,Fri,Sat,Sun + if (empty($validator->errors()->messages())) { + $requestParamBase['include_days'] = $availability['days']; + } + + } + + + if (!is_null($totalInventoryAvailable)) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'availability'; + $requestParams['value'] = $totalInventoryAvailable; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + if (isset($availability['stop_sell']) && !is_null($availability['stop_sell'])) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'room_stop_sell'; + $requestParams['value'] = $availability['stop_sell']; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + } + + DB::commit(); + + return $this->responseSuccess(['confirmCode' => $this->channelManagerLogId]); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + +} diff --git a/app/Http/Controllers/ChannelManager/Reseliva/v1/ReselivaController.php b/app/Http/Controllers/ChannelManager/Reseliva/v1/ReselivaController.php new file mode 100644 index 0000000..a018d06 --- /dev/null +++ b/app/Http/Controllers/ChannelManager/Reseliva/v1/ReselivaController.php @@ -0,0 +1,1069 @@ +username = 'reseliva'; + $this->password = 'e3HYUTUSGcPwIRzV'; + + $this->request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyRoomService = $propertyRoomService; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerLogService = $channelManagerLogService; + + $payload = $this->request->getContent(); + $payloadXML = simplexml_load_string($payload); + $payloadParam = json_decode(json_encode($payloadXML), 1); + + $serviceRequestName = str_replace('RQ', '', $payloadXML->getName()); + $this->serviceRequestName = $serviceRequestName; + + $this->param = $payloadParam; + + $this->channelId = 1; + $this->channelManagerId = 1; //Reseliva, TODO: burada mapping yapılmış mı yapılmamış mı kontrol edilmeli, her şey verilmemeli + + + //channelManagerLogService + $this->channelManagerLogId = null; + $this->channelManagerRequestTime = microtime(true); + $insertDataLog = [ + 'property_id' => $this->param['Hotel']['@attributes']['id'], + 'channel_manager_id' => $this->channelManagerId, + 'type' => 'C2E', + 'service' => $serviceRequestName, + 'request' => $payload, + 'response' => null, + 'ip_address' => $this->request->ip(), + 'status' => null + ]; + + $channelManagerLog = $this->channelManagerLogService->create($insertDataLog); + if ($channelManagerLog['status'] == 'success') { + $this->channelManagerLogId = $channelManagerLog['data']['id']; + } + //channelManagerLogService + + + //Authentication + $errors = []; + if ($this->username != $payloadParam['Authentication']['@attributes']['username'] || $this->password != $payloadParam['Authentication']['@attributes']['password']) { + $errors[] = [ + //'code' => 100, + 'message' => 'Your username or password is incorrect.' + ]; + } + + if (!empty($errors)) { + $this->responseError($errors); + } + + + } + + + public function responseError($errors) + { + + $serviceRequestName = $this->serviceRequestName; + $xmlResponse = new \SimpleXMLElement('<' . $serviceRequestName . 'RS>'); + + foreach ($errors as $error) { + $xmlResponseError = $xmlResponse->addChild('Error', $error['message']); + if (isset($error['code'])) { + $xmlResponseError->addAttribute('code', $error['code']); + } + } + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => $xmlResponse->asXML(), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + + echo $xmlResponse->asXML(); + + die(); + } + + public function responseSuccess($xmlPayload) + { + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => $xmlPayload->asXML(), + 'status' => 1 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + echo $xmlPayload->asXML(); + + die(); + } + + public function propertyChannelMapping($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $getChannelPropertyRoomRate['data'] = collect($getChannelPropertyRoomRate['data'])->where('property_room_rate_mapping.property_room.status',1)->toArray(); + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + if (!is_null($channelManagerPropertyMapping['channel_manager_property_id'])) { + throw new ApiErrorException('This hotel can only be updated by ENW'); + } + + $response = [ + 'status' => true + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function roomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyId = $this->param['Hotel']['@attributes']['id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $serviceRequestName = $this->serviceRequestName; + $xmlResponse = new \SimpleXMLElement('<' . $serviceRequestName . 'RS>'); + + $ProductList = $xmlResponse->addChild('ProductList'); + + $ProductListHotel = $ProductList->addChild('Hotel'); + $ProductListHotel->addAttribute('id', $propertyId); + $ProductListHotel->addAttribute('currency', $propertyChannelMapping['currency_code']); + $ProductListHotel->addAttribute('child_age_group_number', 1); //TODO: + + + $roomRates = []; + foreach ($channelPropertyRoomRate as $roomRate) { + + if ($roomRate['property_room_rate_mapping']['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']]['room'] = [ + 'id' => $roomRate['property_room_rate_mapping']['property_room']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room']['name'], + 'status' => 'Active', + 'capacity' => $roomRate['property_room_rate_mapping']['property_room']['max_adult'], + 'capacity_child' => $roomRate['property_room_rate_mapping']['property_room']['max_child'], + ]; + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']] + ['rate'][$roomRate['property_room_rate_mapping']['id']] = [ + 'id' => $roomRate['property_room_rate_mapping']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['name'], + 'rate' => $roomRate['property_room_rate_mapping']['property_room_rate']['name'] + ]; + + } + + foreach ($roomRates as $roomId => $roomRate) { + + $RoomType = $ProductList->addChild('RoomType'); + $RoomType->addAttribute('id', $roomId); + $RoomType->addAttribute('name', $roomRate['room']['name']); + $RoomType->addAttribute('status', $roomRate['room']['status']); + $RoomType->addAttribute('capacity', $roomRate['room']['capacity']); + $RoomType->addAttribute('capacity_child', $roomRate['room']['capacity_child']); + + foreach ($roomRate['rate'] as $roomRateMappingId => $rate) { + $RatePlan = $RoomType->addChild('RatePlan'); + $RatePlan->addAttribute('id', $roomRateMappingId); + $RatePlan->addAttribute('name', $rate['name'] . ' - ' . $rate['rate']); + } + + } + + $this->responseSuccess($xmlResponse); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + $errors[] = [ + 'message' => $response['message'] + ]; + + $this->responseError($errors); + + } + + } + + public function availabilityRateUpdate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyId = $this->param['Hotel']['@attributes']['id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $availRateUpdates = singleElementXMLArray($this->param['AvailRateUpdate']); + + + DB::beginTransaction(); + + + foreach ($availRateUpdates as $availRateUpdate) { + + $dateRange = []; + $dateRange['startDate'] = $availRateUpdate['DateRange']['@attributes']['from']; + $dateRange['finishDate'] = $availRateUpdate['DateRange']['@attributes']['to']; + + if (Carbon::parse($dateRange['startDate'])->isBefore(Carbon::now()->toDateString()) || Carbon::parse($dateRange['finishDate'])->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + $roomTypes = singleElementXMLArray($availRateUpdate['RoomType']); + + foreach ($roomTypes as $roomType) { + + $roomId = $roomType['@attributes']['id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + throw new ApiErrorException('Tanımlı ya da aktif olmayan oda'); + } + + + $totalInventoryAvailable = null; + if (isset($roomType['Inventory']['@attributes']['totalInventoryAvailable'])) { + $totalInventoryAvailable = $roomType['Inventory']['@attributes']['totalInventoryAvailable']; + } + + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $dateRange['startDate'], + 'end_date' => $dateRange['finishDate'], + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + + if (!is_null($totalInventoryAvailable)) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'availability'; + $requestParams['value'] = $totalInventoryAvailable; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + if (isset($roomType['RatePlan'])) { + + $ratePlans = singleElementXMLArray($roomType['RatePlan']); + + foreach ($ratePlans as $ratePlan) { + + $roomRateMappingId = $ratePlan['@attributes']['id']; + + $isCloseRoomRateMappingSale = null; + if (isset($ratePlan['@attributes']['closed'])) { + $isCloseRoomRateMappingSale = $ratePlan['@attributes']['closed'] == 'true' ? true : false; + } + + $channelRoomRateMappingCheck = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->isEmpty(); + if ($channelRoomRateMappingCheck) { + throw new ApiErrorException('Tanımlı ya da aktif olmayan oda konaklama'); + } + + + $roomRates = []; + $roomRates[] = [ + 'room_id' => $roomId, + 'room_rate_mapping_id' => [ + $roomRateMappingId + ] + ]; + + + $paramsListChannel = []; + $paramsListChannel[] = 1; + //CONNECTED CHANNEL RATE UPDATE + $propertyChannelMappingConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParamBase['property_id']], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $requestParamBase['channel_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => 5],//JUST CM + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + ]; + + $propertyChannelMappingConnected = $this->propertyChannelMappingService->select($propertyChannelMappingConnectedCriteria); + if ($propertyChannelMappingConnected['status'] == 'success') { + + foreach ($propertyChannelMappingConnected['data'] as $channel) { + + if (!is_null($channel['channel']['parent_id'])) { + continue; + } + + $paramsListChannel[] = $channel['channel_id']; + + } + + } + //CONNECTED CHANNEL RATE UPDATE + + + foreach ($paramsListChannel as $paramChannelId) { + + + //Eğer Rate var ise currency check yapılmalı ve PerDay var mı check edilmeli, burada sonra günceleme yaptırılabilri + if (isset($ratePlan['Rate'])) { + + $currency = $ratePlan['Rate']['@attributes']['currency']; + + $currencyCheck = ($currency == $propertyChannelMapping['currency_code']) ? true : false; + + if (!$currencyCheck) { + throw new ApiErrorException('Kanal döviz kuru ile eşleşmeyen döviz kuru, kanal döviz kuru: ' . $propertyChannelMapping['currency_code']); + } + + $channelRoomRateMapping = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->first(); + + + $amountPerDay = $ratePlan['Rate']['PerDay']['@attributes']['rate']; + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate'; + $requestParams['value'] = $amountPerDay; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + //Room Rate Stop Sale + if (!is_null($isCloseRoomRateMappingSale)) { + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate_stop_sell'; + $requestParams['value'] = $isCloseRoomRateMappingSale ? 1 : 0; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + } + + + //Kısıtlamalar var ise min los + if (isset($ratePlan['Restrictions'])) { + + //Minimum Konaklama Gün Sayısı + if (isset($ratePlan['Restrictions']['@attributes']['minLOS'])) { + + $minStay = $ratePlan['Restrictions']['@attributes']['minLOS']; + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'min_stay'; + $requestParams['value'] = $minStay; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + + } + + } + + } + + + } + + } + + + } + + } + + DB::commit(); + + $serviceRequestName = $this->serviceRequestName; + $xmlResponse = new \SimpleXMLElement('<' . $serviceRequestName . 'RS>'); + + $xmlResponse->addChild('Success'); + $xmlResponse->addChild('ConfirmCode', $this->channelManagerLogId); + + $this->responseSuccess($xmlResponse); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + $errors[] = [ + 'message' => $response['message'] + ]; + + $this->responseError($errors); + + } + + } + + public function availabilityRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyId = $this->param['Hotel']['@attributes']['id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $startDate = $this->param['ParamSet']['AvailRateRetrieval']['@attributes']['from']; + $finishDate = $this->param['ParamSet']['AvailRateRetrieval']['@attributes']['to']; + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $requestParams = [ + 'property_id' => $propertyId, + 'room_id' => null, + 'room_rate_mapping_id' => null, + 'channel_id' => $this->channelId, + 'start_date' => $startDate, + 'end_date' => $finishDate, + + ]; + + $propertyRoomType = $this->propertyRoomService->getPropertyRoomInventory($requestParams); + if ($propertyRoomType['status'] != 'success') { + throw new ApiErrorException($propertyRoomType['message']); + } + + $propertyRoomType = $propertyRoomType['data']; + $propertyRoomRateAvailability = collect($propertyRoomType); + + + $roomRates = []; + foreach ($propertyRoomRateAvailability as $room) { + + foreach ($room['property_room_rate_mapping'] as $roomRateMapping) { + + + foreach ($room['room_availability'] as $date => $roomAvailability) { + $roomRates[$room['id']]['availability'][$date] = $roomAvailability['value']; + } + + $roomRates[$room['id']]['rate'][$roomRateMapping['id']] = [ + 'roomName' => $room['name'], + 'roomRateName' => $roomRateMapping['name'], + //'minStay' => $roomRateMapping['min_stay'], + //'maxStay' => $roomRateMapping['max_stay'], + 'currencyCode' => $roomRateMapping['currency_code'], + ]; + + + $roomRatePrices = reset($roomRateMapping['prices']); + foreach ($roomRatePrices['price'] as $date => $roomRatePrice) { + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['price'][$date] = $roomRatePrice['value']; + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['stopSell'][$date] = $roomRatePrice['stop_sell']; + } + + foreach ($roomRatePrices['stop_sell'] as $date => $stopSell) { + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['stopSell'][$date] = $stopSell['value']; + } + + foreach ($roomRatePrices['min_stay'] as $date => $minStay) { + $roomRates[$room['id']]['rate'][$roomRateMapping['id']]['minstay'][$date] = $minStay['value']; + } + + } + } + + $serviceRequestName = $this->serviceRequestName; + $xmlResponse = new \SimpleXMLElement('<' . $serviceRequestName . 'RS>'); + + $AvailRateList = $xmlResponse->addChild('AvailRateList'); + + $AvailRateListHotel = $AvailRateList->addChild('Hotel'); + $AvailRateListHotel->addAttribute('id', $propertyId); + + + $diffInDays = Carbon::parse($startDate)->diffInDays(Carbon::parse($finishDate)); + + if ($diffInDays > 180) { + throw new ApiErrorException('A maximum of 180 days of data can be retrieved.'); + } + + $currentDate = $startDate; + for ($i = 0; $i <= $diffInDays; $i++) { + + //Burada her roomid, roomratemappingid, availability, stopsell, kur, fiyat hepsi var ise kayda girece + + $AvailRate = $AvailRateList->addChild('AvailRate'); + $AvailRate->addAttribute('date', $currentDate); + + + foreach ($roomRates as $roomId => $roomRateMapping) { + + $RoomType = $AvailRate->addChild('RoomType'); + $RoomType->addAttribute('id', $roomId); + + $Inventory = $RoomType->addChild('Inventory'); + $Inventory->addAttribute('totalInventoryAvailable', $roomRateMapping['availability'][$currentDate]); + + foreach ($roomRateMapping['rate'] as $roomRateMappingId => $roomRate) { + + + $RatePlan = $RoomType->addChild('RatePlan'); + $RatePlan->addAttribute('id', $roomRateMappingId); + $RatePlan->addAttribute('closed', $roomRate['stopSell'][$currentDate] == 1 ? 'true' : 'false'); + + + $Rate = $RatePlan->addChild('Rate'); + $Rate->addAttribute('currency', $roomRate['currencyCode']); + + $PerDay = $Rate->addChild('PerDay'); + $PerDay->addAttribute('rate', $roomRate['price'][$currentDate]); + + $Restrictions = $RatePlan->addChild('Restrictions'); + $Restrictions->addAttribute('minLOS', $roomRate['minstay'][$currentDate]); + + + } + + + } + + + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + + + } + + + $this->responseSuccess($xmlResponse); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + $errors[] = [ + 'message' => $response['message'] + ]; + + $this->responseError($errors); + + } + + } + + + public function booking(Request $request, $param = []) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyId = $this->param['Hotel']['@attributes']['id']; + $bookingId = $this->param['Booking']['@attributes']['id']; + $type = $this->param['Booking']['@attributes']['type']; + + if (!in_array($type, ['Book', 'Modify', 'Cancel'])) { + throw new ApiErrorException('Unknown process type:' . $type); + } + + + $this->serviceRequestName = 'BookingRetrieval'; + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $requestData = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $bookingId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ], + 'with' => ['bookingContact', 'bookingChannel', 'bookingPayment', 'bookingPaymentType', 'bookingStatus', 'bookingRoom.roomPax.paxCountry'], + 'firstRow' => true + ]; + + $bookingDetail = $this->bookingService->select($requestData); + + if ($bookingDetail['status'] != 'success') { + throw new ApiErrorException('Property Booking Data not found'); + } + + $bookingDetail = $bookingDetail['data']; + + $serviceRequestName = 'BookingRetrieval'; + $xmlResponse = new \SimpleXMLElement('<' . $serviceRequestName . 'RS>'); + + + if (in_array($bookingDetail['status'], [0, 3])) { + + $Bookings = $xmlResponse->addChild('Bookings'); + $Booking = $Bookings->addChild('Booking'); + + $Booking->addAttribute('id', $bookingDetail['id']); + $Booking->addAttribute('type', 'Cancel'); + $Booking->addAttribute('cancelDateTime', Carbon::createFromTimestamp($bookingDetail['updated_at'])->toISOString()); + + $Hotel = $Booking->addChild('Hotel'); + $Hotel->addAttribute('id', $bookingDetail['property_id']); + + foreach ($bookingDetail['booking_room'] as $room) { + + $RoomStay = $Booking->addChild('RoomStay'); + $RoomStay->addAttribute('roomTypeID', $room['room_id']); + $RoomStay->addAttribute('ratePlanID', $room['room_rate_mapping_id']); + + $StayDate = $RoomStay->addChild('StayDate'); + $StayDate->addAttribute('arrival', $room['checkin_date']); + $StayDate->addAttribute('departure', $room['checkout_date']); + + } + + } else { + + + $Bookings = $xmlResponse->addChild('Bookings'); + $Booking = $Bookings->addChild('Booking'); + + $Booking->addAttribute('id', $bookingDetail['id']); + + //Book ve Modify Aynı olacak, Cancel + if ($type == 'Book') { + $Booking->addAttribute('type', 'Book'); + $Booking->addAttribute('createDateTime', Carbon::createFromTimestamp($bookingDetail['created_at'])->toISOString()); + } + + if ($type == 'Modify') { + $Booking->addAttribute('type', 'Modify'); + $Booking->addAttribute('modifyDateTime', Carbon::createFromTimestamp($bookingDetail['updated_at'])->toISOString()); + } + + $Hotel = $Booking->addChild('Hotel'); + $Hotel->addAttribute('id', $bookingDetail['property_id']); + + + foreach ($bookingDetail['booking_room'] as $room) { + + $RoomStay = $Booking->addChild('RoomStay'); + $RoomStay->addAttribute('roomTypeID', $room['room_id']); + $RoomStay->addAttribute('ratePlanID', $room['room_rate_mapping_id']); + + $StayDate = $RoomStay->addChild('StayDate'); + $StayDate->addAttribute('arrival', $room['checkin_date']); + $StayDate->addAttribute('departure', $room['checkout_date']); + + + $roomPaxCollect = collect($room['room_pax']); + + $GuestCount = $RoomStay->addChild('GuestCount'); + $GuestCount->addAttribute('adult', $roomPaxCollect->where('type', 'ADT')->count()); + + $diffInDays = Carbon::parse($room['checkin_date'])->diffInDays(Carbon::parse($room['checkout_date'])); + + //dd($room['total'], $diffInDays, $room); + + $PerDayRates = $RoomStay->addChild('PerDayRates'); + $PerDayRates->addAttribute('currency', $room['currency_code']); + + $baseRateDaily = moneyDoubleFormatDecimal($room['total'] / $diffInDays); + + $currentDate = $room['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $PerDayRate = $PerDayRates->addChild('PerDayRate'); + $PerDayRate->addAttribute('stayDate', $currentDate); + $PerDayRate->addAttribute('baseRate', $baseRateDaily); + $PerDayRate->addAttribute('hotelServiceFees', 0); + + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + + $Total = $RoomStay->addChild('Total'); + $Total->addAttribute('amountAfterTaxes', $room['total']); + $Total->addAttribute('amountOfTaxes', 0); + $Total->addAttribute('currency', $room['currency_code']); + + $RoomStay->addChild('RoomNotes', $bookingDetail['booking_contact']['note']); + + } + + + $PrimaryGuest = $Booking->addChild('PrimaryGuest'); + + $Name = $PrimaryGuest->addChild('Name'); + $Name->addAttribute('givenName', $bookingDetail['booking_contact']['name']); + $Name->addAttribute('surname', $bookingDetail['booking_contact']['surname']); + + $Phone = $PrimaryGuest->addChild('Phone'); + $Phone->addAttribute('countryCode', $bookingDetail['booking_contact']['phone_code']); + //$Phone->addAttribute('cityAreaCode'); + $Phone->addAttribute('number', $bookingDetail['booking_contact']['phone_number']); + + $PrimaryGuest->addChild('Email', $bookingDetail['booking_contact']['email']); + + + $Total = $Booking->addChild('Total'); + $Total->addAttribute('amountAfterTaxes', $bookingDetail['total']); + $Total->addAttribute('amountOfTaxes', 0); + $Total->addAttribute('currency', $bookingDetail['currency_code']); + + + $Booking->addChild('SpecialRequest'); + $Booking->addChild('BookingNotes', $bookingDetail['booking_contact']['note']); + + + } + + + $this->responseSuccess($xmlResponse); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + $errors[] = [ + 'message' => $response['message'] + ]; + + $this->responseError($errors); + + } + + } + + +} diff --git a/app/Http/Controllers/ChannelManager/SistemOtel/v1/SistemOtelController.php b/app/Http/Controllers/ChannelManager/SistemOtel/v1/SistemOtelController.php new file mode 100644 index 0000000..a2da5a9 --- /dev/null +++ b/app/Http/Controllers/ChannelManager/SistemOtel/v1/SistemOtelController.php @@ -0,0 +1,827 @@ +username = 'sistemotel'; + $this->password = 'ys6paYBmEWa5q7Mt'; + + $this->request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyRoomService = $propertyRoomService; + $this->bookingService = $bookingService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerLogService = $channelManagerLogService; + + $payload = $this->request->getContent(); + $payloadJson = json_decode($payload, 1); + $this->param = $payloadJson; + + $this->channelId = 1; + $this->channelManagerId = 6; //SistemOtel + + $getRequestUri = $request->getRequestUri(); + $getRequestUriExplode = explode('/', $getRequestUri); + $serviceRequestName = last($getRequestUriExplode); + + //channelManagerLogService + $logArray = ['update-room-availability', 'update-room-rate']; + $this->channelManagerLogId = null; + $this->channelManagerRequestTime = microtime(true); + if (in_array($serviceRequestName, $logArray)) { + $insertDataLog = [ + 'property_id' => $this->param['hotel_id'], + 'channel_manager_id' => $this->channelManagerId, + 'type' => 'C2E', + 'service' => $serviceRequestName, + 'request' => json_encode($payloadJson), + 'response' => null, + 'ip_address' => $this->request->ip(), + 'status' => null + ]; + + + $channelManagerLog = $this->channelManagerLogService->create($insertDataLog); + if ($channelManagerLog['status'] == 'success') { + $this->channelManagerLogId = $channelManagerLog['data']['id']; + } + } + //channelManagerLogService + + + } + + public function checkAuthentication($username, $password) + { + + $response = ['status' => false, 'message' => '']; + + if ($this->username != $username || $this->password != $password) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function responseSuccess($responseData = null) + { + + $response = [ + 'status' => true, + 'message' => null, + 'data' => $responseData, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 1 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + return response()->json($response); + + } + + public function propertyChannelMapping($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = [ + 'status' => true, + 'data' => $propertyChannelMapping['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelPropertyRoomRate($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType', + ] + ]; + + $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam); + + if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $getChannelPropertyRoomRate['data'] = collect($getChannelPropertyRoomRate['data'])->where('property_room_rate_mapping.property_room.status',1)->toArray(); + + $response = [ + 'status' => true, + 'data' => $getChannelPropertyRoomRate['data'] + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function channelManagerPropertyCheck($propertyId) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $requestParam = + [ + 'criteria' => + [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($requestParam); + + + if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) { + throw new ApiErrorException('Property Room Rate not found'); + } + + $channelManagerPropertyMapping = $channelManagerPropertyMapping['data']; + + if (!is_null($channelManagerPropertyMapping['channel_manager_property_id'])) { + throw new ApiErrorException('This hotel can only be updated by ENW'); + } + + $response = [ + 'status' => true + ]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + + public function roomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + try { + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + $response = []; + + $roomRates = []; + foreach ($channelPropertyRoomRate as $roomRate) { + + if($roomRate['property_room_rate_mapping']['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']]['room'] = [ + 'id' => $roomRate['property_room_rate_mapping']['property_room']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room']['name'], + 'status' => 'Active', + 'capacity' => $roomRate['property_room_rate_mapping']['property_room']['max_adult'], + 'capacity_child' => $roomRate['property_room_rate_mapping']['property_room']['max_child'], + ]; + + $roomRates[$roomRate['property_room_rate_mapping']['room_id']] + ['rate'][$roomRate['property_room_rate_mapping']['id']] = [ + 'id' => $roomRate['property_room_rate_mapping']['id'], + 'name' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['name'], + 'rate' => $roomRate['property_room_rate_mapping']['property_room_rate']['name'], + 'accommodationId' => $roomRate['property_room_rate_mapping']['property_room_rate']['property_room_rate_accommodation']['id'], + ]; + + } + + $roomKey = 0; + $response['rooms'] = []; + foreach ($roomRates as $roomId => $roomRate) { + + $response['rooms'][$roomKey] = [ + 'id' => $roomId, + 'room_name' => $roomRate['room']['name'], + ]; + + $roomRateKey = 0; + $response['rooms'][$roomKey]['rates'] = []; + foreach ($roomRate['rate'] as $roomRateMappingId => $rateData) { + + $response['rooms'][$roomKey]['rates'][$roomRateKey] = [ + 'id' => $roomRateMappingId, + 'rate_name' => $rateData['name'] . ' - ' . $rateData['rate'], + 'board_id' => $rateData['accommodationId'], + 'board_name' => $rateData['name'], + ]; + + $roomRateKey++; + + } + + $roomKey++; + } + + return $this->responseSuccess($response); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function updateRoomAvailability(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $roomAvailabilities = $this->param['rooms']; + $roomAvailabilitiesCollect = collect($roomAvailabilities); + + DB::beginTransaction(); + + $startDate = $roomAvailabilitiesCollect->sortBy('start_date')->first(); + $startDate = $startDate['start_date']; + + $endDate = $roomAvailabilitiesCollect->sortByDesc('end_date')->first(); + $endDate = $endDate['end_date']; + + if (Carbon::parse($startDate)->isBefore(Carbon::now()->toDateString()) || Carbon::parse($endDate)->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + foreach ($roomAvailabilities as $availability) { + + $roomId = $availability['room_id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + throw new ApiErrorException('Undefined or inactive room accommodation'); + } + + $totalInventoryAvailable = null; + if (isset($availability['availability'])) { + $totalInventoryAvailable = $availability['availability']; + } + + $startDate = Carbon::parse($availability['start_date'])->toDateString(); + $endDate = Carbon::parse($availability['end_date'])->toDateString(); + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $startDate, + 'end_date' => $endDate, + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + //Days Filter + if (isset($availability['days']) && !empty($availability['days']) && is_array($availability['days']) && count($availability['days']) <= 7) { + + $requestParams = [ + 'days' => $availability['days'] + ]; + + $validator = Validator::make($requestParams, ['days' => 'required|array|in:Mon,Tue,Wed,Thu,Fri,Sat,Sun']);//Mon,Tue,Wed,Thu,Fri,Sat,Sun + if (empty($validator->errors()->messages())) { + $requestParamBase['include_days'] = $availability['days']; + } + + } + + + if (!is_null($totalInventoryAvailable)) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'availability'; + $requestParams['value'] = $totalInventoryAvailable; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + if (isset($availability['stop_sell']) && !is_null($availability['stop_sell'])) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'room_stop_sell'; + $requestParams['value'] = $availability['stop_sell']; + $requestParams['room_rates'] = [ + ['room_id' => $roomId] + ]; + + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + } + + DB::commit(); + + return $this->responseSuccess(['confirmCode' => $this->channelManagerLogId]); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + public function updateRoomRate(Request $request) + { + + $response = ['status' => false, 'message' => '']; + + try { + + //checkAuthentication + $checkAuthentication = $this->checkAuthentication($this->param['username'], $this->param['password']); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $propertyId = $this->param['hotel_id']; + + $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($propertyId); + if (!$channelManagerPropertyCheck['status']) { + throw new ApiErrorException($channelManagerPropertyCheck['message']); + } + + $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId); + if (!$channelPropertyRoomRate['status']) { + throw new ApiErrorException($channelPropertyRoomRate['message']); + } + + $channelPropertyRoomRate = $channelPropertyRoomRate['data']; + $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate); + + + $propertyChannelMapping = $this->propertyChannelMapping($propertyId); + if (!$propertyChannelMapping['status']) { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + $roomRates = $this->param['rooms']; + $roomRatesCollect = collect($roomRates); + + + DB::beginTransaction(); + + foreach ($roomRates as $roomRate) { + + $roomId = $roomRate['room_id']; + + $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty(); + if ($roomCheck) { + throw new ApiErrorException('Undefined or inactive room accommodation'); + } + + + $roomRatesCollect = collect($roomRate['rates']); + + $startDate = $roomRatesCollect->sortBy('start_date')->first(); + $startDate = $startDate['start_date']; + + $endDate = $roomRatesCollect->sortByDesc('end_date')->first(); + $endDate = $endDate['end_date']; + + if (Carbon::parse($startDate)->isBefore(Carbon::now()->toDateString()) || Carbon::parse($endDate)->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Dates to be updated cannot be earlier than today'); + } + + foreach ($roomRate['rates'] as $rate) { + + $startDate = Carbon::parse($rate['start_date'])->toDateString(); + $endDate = Carbon::parse($rate['end_date'])->toDateString(); + + $requestParamBase = [ + 'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'), + 'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'), + 'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1), + 'start_date' => $startDate, + 'end_date' => $endDate, + 'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + //Days Filter + if (isset($rate['days']) && !empty($rate['days']) && is_array($rate['days']) && count($rate['days']) <= 7) { + + $requestParams = [ + 'days' => $rate['days'] + ]; + + $validator = Validator::make($requestParams, ['days' => 'required|array|in:Mon,Tue,Wed,Thu,Fri,Sat,Sun']); + if (empty($validator->errors()->messages())) { + $requestParamBase['include_days'] = $rate['days']; + } + + } + + + $roomRateMappingId = $rate['rate_id']; + + $isCloseRoomRateMappingSale = null; + if (isset($rate['stop_sell'])) { + $isCloseRoomRateMappingSale = $rate['stop_sell'] == 1 ? true : false; + } + + + $channelRoomRateMappingCheck = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->isEmpty(); + if ($channelRoomRateMappingCheck) { + throw new ApiErrorException('Undefined or inactive room accommodation'); + } + + $roomRates = []; + $roomRates[] = [ + 'room_id' => $roomId, + 'room_rate_mapping_id' => [ + $roomRateMappingId + ] + ]; + + + $paramsListChannel = []; + $paramsListChannel[] = $this->channelId; + + //CONNECTED CHANNEL RATE UPDATE + $propertyChannelMappingConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParamBase['property_id']], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $requestParamBase['channel_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => 5],//JUST CM + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + ]; + + $propertyChannelMappingConnected = $this->propertyChannelMappingService->select($propertyChannelMappingConnectedCriteria); + if ($propertyChannelMappingConnected['status'] == 'success') { + + foreach ($propertyChannelMappingConnected['data'] as $channel) { + + if (!is_null($channel['channel']['parent_id'])) { + continue; + } + + $paramsListChannel[] = $channel['channel_id']; + + } + + } + //CONNECTED CHANNEL RATE UPDATE + + + foreach ($paramsListChannel as $paramChannelId) { + + //Eğer Rate var ise currency check yapılmalı ve PerDay var mı check edilmeli, burada sonra günceleme yaptırılabilri + if (isset($rate['amount'])) { + + $currency = $this->param['currency']; + + $currencyCheck = ($currency == $propertyChannelMapping['currency_code']) ? true : false; + + if (!$currencyCheck) { + throw new ApiErrorException('Exchange rate that does not match the channel exchange rate, channel exchange rate: ' . $propertyChannelMapping['currency_code']); + } + + $channelRoomRateMapping = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->first(); + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate'; + $requestParams['value'] = $rate['amount']; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + + //Room Rate Stop Sale + if (!is_null($isCloseRoomRateMappingSale)) { + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'rate_stop_sell'; + $requestParams['value'] = $isCloseRoomRateMappingSale ? 1 : 0; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + } + + + //Kısıtlamalar var ise min los + if (isset($rate['min_stay'])) { + + //Minimum Konaklama Gün Sayısı + if (isset($rate['min_stay'])) { + + $requestParams = []; + $requestParams = $requestParamBase; + $requestParams['update_type'] = 'min_stay'; + $requestParams['value'] = $rate['min_stay']; + $requestParams['room_rates'] = $roomRates; + + $requestParams['channel_id'] = $paramChannelId; + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + + } + + } + + } + + + } + } + + DB::commit(); + + + return $this->responseSuccess(['confirmCode' => $this->channelManagerLogId]); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + DB::rollBack(); + + if (!$response['status']) { + return $this->responseError($response['message']); + } + + } + + +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..0a7059f --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,12 @@ +username = 'google'; + $this->password = 'P8MUQtNP6TkKMz3E'; + + $this->channelManagerId = 14;//Google + + $this->request = $request; + $this->currencyService = $currencyService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->propertyChannelCouponService = $propertyChannelCouponService; + + $payload = $this->request->getContent(); + parse_str(urldecode($payload), $payloadDecode); + $this->param = $payloadDecode; + + $this->authorization = $request->header('authorization'); + + $this->tax = 10; + + + $this->mealCodeMapping = [ + 10 => 'AI', + 11 => 'BB', + 13 => 'RO', + 14 => 'FB', + 15 => 'HB', + ]; + + $this->paymentTypeMapping = [ + 'CRD' => 'prepaid', + 'HTL' => 'postpaid' + ]; + + + } + + + public function checkAuthentication($authorization) + { + + $response = ['status' => false, 'message' => '']; + + $basicAuth = 'Basic ' . base64_encode($this->username . ':' . $this->password); + + if ($authorization != $basicAuth) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + + return response()->json($response); + + } + + public function requestParamConverter($requestParam = []) + { + + $searchRequestJson = []; + + $searchRequestJson['date']['checkIn'] = Carbon::parse($requestParam['Checkin'])->toDateString(); + $searchRequestJson['date']['checkOut'] = Carbon::parse($requestParam['Checkin'])->addDays($requestParam['Nights'])->toDateString(); + + + $rooms = []; + $rooms[0]['adults'] = (integer)$requestParam['Context']['OccupancyDetails']['NumAdults']; + $rooms[0]['children'] = (integer)($requestParam['Context']['Occupancy'] - $requestParam['Context']['OccupancyDetails']['NumAdults']); + + $rooms[0]['age'] = []; + if (isset($requestParam['Context']['OccupancyDetails']['Children']['Child'])) { + $requestParam['Context']['OccupancyDetails']['Children']['Child'] = singleElementXMLArray($requestParam['Context']['OccupancyDetails']['Children']['Child']); + } else { + $requestParam['Context']['OccupancyDetails']['Children']['Child'] = []; + } + + foreach ($requestParam['Context']['OccupancyDetails']['Children']['Child'] as $child) { + $rooms[0]['age'][] = $child['@attributes']['age']; + } + + $searchRequestJson['rooms'] = $rooms; + + if (isset($requestParam['PropertyList']['Property'])) { + $requestParam['PropertyList']['Property'] = singleElementXMLArray($requestParam['PropertyList']['Property']); + } else { + $requestParam['PropertyList']['Property'] = []; + } + + + $searchRequestJson['property'] = $requestParam['PropertyList']['Property']; + + return $searchRequestJson; + + } + + public function deepLinkGenerator($token, $requestParam = []) + { + + $deepLink = 'https://be.extranetwork.com/' . $token . '/' . fillOnUndefined($requestParam, 'locale', 'en') . '/search/'; + + + $deepLink .= Carbon::parse($requestParam['date']['checkIn'])->format('Ymd') . '/'; + $deepLink .= Carbon::parse($requestParam['date']['checkOut'])->format('Ymd') . '/'; + + + $roomOccupancy = $this->roomOccupancy($requestParam['rooms']); + + $deepLink .= implode(',', $roomOccupancy);; + + $deepLink .= '?utm_source=google'; + + return $deepLink; + + } + + + public function hotel(Request $request) + { + + $response = ['status' => true, 'message' => '']; + + + $listings = new \SimpleXMLElement(''); + + $listings->addChild('language', 'en'); + + + try { + + + $channelManagerPropertyMappingCriteria['criteria'] = [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ]; + $channelManagerPropertyMappingCriteria['with'] = [ + 'property.propertyType', 'property.propertyContact', 'property.propertyWeb', 'property.country', + 'property.propertyRooms', 'property.propertyPhotos', 'property.propertyAdditionalInfos', 'property.propertyBrand' + ]; + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $propertyMapping) { + + if ($propertyMapping['status'] != 1) { + continue; + } + + $metaAddress = json_decode($propertyMapping['property']['property_contact']['meta'], 1); + + $listing = $listings->addChild('listing'); + + $listing->addChild('id', $propertyMapping['property']['id']); + $listing->addChild('name', htmlspecialchars($propertyMapping['property']['name'], ENT_XML1, 'UTF-8')); + + $address = $listing->addChild('address'); + $address->addAttribute('format', 'simple'); + + $component = $address->addChild('component', htmlspecialchars(fillOnUndefined($metaAddress, 'street'), ENT_XML1, 'UTF-8')); + $component->addAttribute('name', 'addr1'); + + //$component = $address->addChild('component'); + //$component->addAttribute('name', 'addr2'); + + $component = $address->addChild('component', fillOnUndefined($metaAddress, 'city')); + $component->addAttribute('name', 'city'); + + //$component = $address->addChild('component', $propertyMapping['property']['country']['name']); + //$component->addAttribute('name','region'); + + $component = $address->addChild('component', fillOnUndefined($metaAddress, 'zip_code')); + $component->addAttribute('name', 'postal_code'); + + $listing->addChild('country', $propertyMapping['property']['country']['country_code']); + $listing->addChild('latitude', $propertyMapping['property']['property_contact']['latitude']); + $listing->addChild('longitude', $propertyMapping['property']['property_contact']['longitude']); + + if (!empty($propertyMapping['property']['property_contact']['view_full_phone'])) { + $phone = $listing->addChild('phone'); + $phone->addAttribute('number', $propertyMapping['property']['property_contact']['view_full_phone']); + } + + $listing->addChild('category', 'hotel'); + + if (!empty($propertyMapping['property']['property_brand']['name'])) { + $listing->addChild('brand', htmlspecialchars($propertyMapping['property']['property_brand']['name'], ENT_XML1, 'UTF-8')); + } + + if (!empty($propertyMapping['property']['property_web']['webProtocolUrl'])) { + $listing->addChild('website', $propertyMapping['property']['property_web']['webProtocolUrl']); + } + + $content = $listing->addChild('content'); + + $text = $content->addChild('text'); + $text->addAttribute('type', 'description'); + $text->addChild('body', htmlspecialchars($propertyMapping['property']['name'], ENT_XML1, 'UTF-8')); + + $attributes = $content->addChild('attributes'); + $clientAttrCountry = $attributes->addChild('client_attr', $propertyMapping['property']['country']['country_code']); + $clientAttrCountry->addAttribute('name', 'country'); + + $clientAttrMaxNightStay = $attributes->addChild('client_attr', '0'); + $clientAttrMaxNightStay->addAttribute('name', 'max_night_stay'); + + $clientAttrMinNightStay = $attributes->addChild('client_attr', '1'); + $clientAttrMinNightStay->addAttribute('name', 'minimum_night_stay'); + + $roomCountTotal = 0; + if (!empty($propertyMapping['property']['property_additional_infos'])) { + foreach ($propertyMapping['property']['property_additional_infos'] as $info) { + if ($info['additional_info_key_id'] == 3) { + $roomCountTotal = $info['value']; + break; + } + } + } + + $clientAttrRooms = $attributes->addChild('client_attr', $roomCountTotal); + $clientAttrRooms->addAttribute('name', 'number_of_rooms'); + + $clientAttrLatLong = $attributes->addChild('client_attr', $propertyMapping['property']['property_contact']['latitude'] . ', ' . $propertyMapping['property']['property_contact']['longitude']); + $clientAttrLatLong->addAttribute('name', 'latlong'); + + if (!empty($propertyMapping['property']['property_photos'])) { + $photos = $propertyMapping['property']['property_photos']; + + // Filter photos with status 1 + $photos = array_filter($photos, function ($p) { + return isset($p['status']) && $p['status'] == 1; + }); + + // Sort photos by is_default (desc), photo_order (asc), and created_at (desc) + usort($photos, function ($a, $b) { + return ($b['is_default'] ?? 0) <=> ($a['is_default'] ?? 0) + ?: ($a['photo_order'] ?? 0) <=> ($b['photo_order'] ?? 0) + ?: ($b['created_at'] ?? 0) <=> ($a['created_at'] ?? 0); + }); + + // Limit to first 10 photos + $photos = array_slice($photos, 0, 10); + + foreach ($photos as $photo) { + $imageUrl = $photo['photoUrl']['default'] ?? $photo['photo_url']['default'] ?? null; + + if ($imageUrl) { + $image = $content->addChild('image'); + $image->addAttribute('type', 'ad'); + $image->addAttribute('url', $imageUrl); + + $width = '1440'; + $height = '959'; + + if (!empty($photo['photo_resolution'])) { + $resolution = explode('x', $photo['photo_resolution']); + if (count($resolution) == 2) { + $width = trim($resolution[0]); + $height = trim($resolution[1]); + } + } + + $image->addAttribute('width', $width); + $image->addAttribute('height', $height); + + $image->addChild('link', $imageUrl); + $image->addChild('title', 'Unknown title'); + + $date = $image->addChild('date'); + if (!empty($photo['created_at'])) { + $photoCreatedAt = Carbon::createFromTimestamp($photo['created_at']); + } else { + $photoCreatedAt = Carbon::now(); + } + + $date->addAttribute('day', $photoCreatedAt->day); + $date->addAttribute('month', $photoCreatedAt->month); + $date->addAttribute('year', $photoCreatedAt->year); + } + } + } + + + } + + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + header('Content-Type: application/xml; charset=UTF-8'); + echo $listings->asXML(); + exit; + + + } + +} diff --git a/app/Http/Controllers/MetaSearch/Trivago/v1/TrivagoController.php b/app/Http/Controllers/MetaSearch/Trivago/v1/TrivagoController.php new file mode 100644 index 0000000..441a09f --- /dev/null +++ b/app/Http/Controllers/MetaSearch/Trivago/v1/TrivagoController.php @@ -0,0 +1,872 @@ +username = 'trivago'; + $this->password = 'P8MUQtNP6TkKMz3E'; + + $this->channelManagerId = 11;//Trivago + + $this->request = $request; + $this->currencyService = $currencyService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->propertyChannelCouponService = $propertyChannelCouponService; + + $payload = $this->request->getContent(); + parse_str(urldecode($payload), $payloadDecode); + $this->param = $payloadDecode; + + $this->authorization = $request->header('authorization'); + + $this->tax = 10; + $this->cityTax = 2; + + + $this->mealCodeMapping = [ + 10 => 'AI', + 11 => 'BB', + 13 => 'RO', + 14 => 'FB', + 15 => 'HB', + ]; + + $this->paymentTypeMapping = [ + 'CRD' => 'prepaid', + 'HTL' => 'postpaid' + ]; + + $this->CPA['TR'] = ["1" => 9.60, "2" => 10.80, "3" => 11.00, "4" => 11.20, "5" => 11.40, "6" => 11.60, "7" => 11.80, "8" => 12.00]; + $this->CPA['DE'] = ["1" => 10.80, "2" => 11.00, "3" => 11.20, "4" => 11.40, "5" => 11.60, "6" => 11.80, "7" => 12.00, "8" => 12.20]; + $this->CPA['UK'] = ["1" => 10.80, "2" => 11.00, "3" => 11.20, "4" => 11.40, "5" => 11.60, "6" => 11.80, "7" => 12.00, "8" => 12.20]; + $this->CPA['US'] = ["1" => 10.80, "2" => 11.00, "3" => 11.20, "4" => 11.40, "5" => 11.60, "6" => 11.80, "7" => 12.00, "8" => 12.20]; + + $this->countryCodeCampaign = [ + 'DE' => [ + 'code' => 'DE', + 'campaignCode' => 7 + ], + 'TR' => [ + 'code' => 'TR', + 'campaignCode' => 8 + ], + 'US' => [ + 'code' => 'US', + 'campaignCode' => 7 + ], + 'UK' => [ + 'code' => 'UK', + 'campaignCode' => 7 + ] + ]; + + } + + + public function checkAuthentication($authorization) + { + + $response = ['status' => false, 'message' => '']; + + $basicAuth = 'Basic ' . base64_encode($this->username . ':' . $this->password); + + if ($authorization != $basicAuth) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + + return response()->json($response); + + } + + public function requestParamConverter($requestParam = []) + { + + $searchRequestJson = []; + + $searchRequestJson['date']['checkIn'] = Carbon::parse($requestParam['start_date'])->toDateString(); + $searchRequestJson['date']['checkOut'] = Carbon::parse($requestParam['end_date'])->toDateString(); + + $rooms = []; + for ($i = 0; $i < $requestParam['num_rooms']; $i++) { + + $roomCounter = ($i + 1); + + if (isset($requestParam['room_adults_' . $roomCounter])) { + $rooms[$i]['adults'] = $requestParam['room_adults_' . $roomCounter]; + } + + + $rooms[$i]['children'] = 0; + $rooms[$i]['age'] = null; + if (isset($requestParam['room_childs_' . $roomCounter])) { + + $childAges = json_decode($requestParam['room_childs_' . $roomCounter]); + + $rooms[$i]['children'] = count($childAges); + $rooms[$i]['age'] = $childAges; + } + + } + + $searchRequestJson['rooms'] = $rooms; + $searchRequestJson['property'] = json_decode($requestParam['hotels']); + + return $searchRequestJson; + + } + + public function roomOccupancy($requestParam) + { + + $roomOccupancy = []; + foreach ($requestParam as $room) { + $adults = str_repeat('A', $room['adults']); + + $children = []; + if ($room['children'] > 0 && !empty($room['age'])) { + foreach ($room['age'] as $child) { + $children[] = 'C' . $child; + } + } + + $roomOccupancy[] = $adults . implode('', $children); + + } + + return $roomOccupancy; + } + + public function deepLinkGenerator($token, $requestParam = []) + { + + $deepLink = 'https://be.extranetwork.com/' . $token . '/en/search/'; + + + $deepLink .= Carbon::parse($requestParam['date']['checkIn'])->format('Ymd') . '/'; + $deepLink .= Carbon::parse($requestParam['date']['checkOut'])->format('Ymd') . '/'; + + + $roomOccupancy = $this->roomOccupancy($requestParam['rooms']); + + $deepLink .= implode(',', $roomOccupancy);; + + $deepLink .= '?utm_medium=trivago&utm_source=trivago&locale=' . $requestParam['locale']; + + return $deepLink; + + } + + public function couponCodeCheck($propertyId, $checkIn, $checkOut) + { + //$propertyChannelCoupon + $propertyChannelCouponsCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_id', 'condition' => '=', 'value' => 1], + ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::now()->toDateString()], + ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ], + ]; + + $propertyChannelCoupons = $this->propertyChannelCouponService->select($propertyChannelCouponsCriteria); + + if ($propertyChannelCoupons['status'] == 'success') { + $propertyChannelCoupons = $propertyChannelCoupons['data']; + + foreach ($propertyChannelCoupons as $propertyChannelCouponKey => $propertyChannelCouponData) { + + //Reservation Date Check + if (!is_null($propertyChannelCouponData['reservation_start_date']) && !is_null($propertyChannelCouponData['reservation_end_date'])) { + $reservationDateCheck = Carbon::parse($propertyChannelCouponData['reservation_start_date'])->lessThanOrEqualTo(Carbon::parse($checkIn)) + && Carbon::parse($propertyChannelCouponData['reservation_end_date'])->greaterThanOrEqualTo(Carbon::parse($checkOut)); + if (!$reservationDateCheck) { + unset($propertyChannelCoupons[$propertyChannelCouponKey]); + } + + } + } + + } else { + $propertyChannelCoupons = []; + } + + $propertyChannelCoupon = !empty($propertyChannelCoupons) ? reset($propertyChannelCoupons) : null; + + return $propertyChannelCoupon; + + } + + public function availability(Request $request) + { + + $response = ['status' => true, 'message' => '']; + + $checkAuthentication = $this->checkAuthentication($this->authorization); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $searchController = App::make("App\Http\Controllers\BookingEngine\V1\SearchController"); + + $requestTime = microtime(true); + + + $hotelList['root'] = $this->param; + unset($hotelList['root']['hotels']); + $hotelList['root']['hotel_ids'] = json_decode($this->param['hotels']); + $hotelList['root']['hotels'] = []; + + $hotelList['root']['api_version'] = (integer)$hotelList['root']['api_version']; + $hotelList['root']['num_rooms'] = (integer)$hotelList['root']['num_rooms']; + + for ($i = 0; $i < $hotelList['root']['num_rooms']; $i++) { + $roomCounter = ($i + 1); + if (isset($hotelList['root']['room_adults_' . $roomCounter])) { + $hotelList['root']['room_adults_' . $roomCounter] = (integer)$hotelList['root']['room_adults_' . $roomCounter]; + } + } + + $requestCurrency = $this->param['currency']; + + try { + + + //Hotels Check + $channelManagerPropertyList = []; + $channelManagerPropertyMappingCriteria['criteria'] = [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ]; + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + $channelManagerPropertyList = pickItemFromArray('property_id', $channelManagerPropertyMapping['data']); + } + + $availableHotelIds = array_intersect($hotelList['root']['hotel_ids'], $channelManagerPropertyList); + + if (empty($availableHotelIds)) { + throw new ApiErrorException('Hotel not found'); + } + + $this->param['hotels'] = json_encode($availableHotelIds); + //Hotels Check + + + $searchRequestJson = $this->requestParamConverter($this->param); + + $searchRequestJson['ipAddress'] = $request->getClientIp(); + $searchRequestJson['isMobile'] = null; + $searchRequestJson['noneCacheSearch'] = true; + $searchRequestJson['ipAddress'] = $request->getClientIp(); + $searchRequestJson['justPriceValues'] = true; + + + $cacheKeyParam = $this->param; + unset($cacheKeyParam['currency']); + unset($cacheKeyParam['rate_model']); + unset($cacheKeyParam['lang']); + $cacheKey = 'TRV:' . md5(implode(',', $cacheKeyParam)); + + //Cache::forget($cacheKey); + + if (Cache::has($cacheKey)) { + $search = Cache::get($cacheKey); + } else { + $requestCreate = Request::create(null, null, [], [], [], [], json_encode($searchRequestJson)); + $requestCreate->headers->set('channelId', '1'); + //$requestCreate->headers->set('bookingEnginePropertyId', null); + $requestCreate->headers->set('channelToken', 'ece3ef02-42e7-92b7-f379-08226ed7a0f3');//TODO: from DB + $requestCreate->headers->set('bookingEngineToken', null); + $search = $searchController->search($requestCreate); + + Cache::put($cacheKey, $search, 60 * 60);//1 Day 24 * 60 * 60 + } + + $search = json_decode(json_encode($search), 1); + + if ($search['original']['status'] != 200) { + throw new ApiErrorException('Hotel not found'); + } + + $properties = $search['original']['data']['properties']; + + if (empty($properties)) { + throw new ApiErrorException('Hotel not found'); + } + + $hotelCounter = 0; + $numberOfStay = Carbon::parse($searchRequestJson['date']['checkIn'])->diffInDays(Carbon::parse($searchRequestJson['date']['checkOut'])); + + foreach ($properties as $propertyId => $property) { + + if (!isset($property['currency'])) { + continue; + } + + $currencyExchangeRate = 1; + if ($requestCurrency != $property['currency']) { + $currencyExchangeRate = $this->currencyService->lastExchangeRate($property['currency'], $requestCurrency); + if ($currencyExchangeRate['status'] == 'success') { + $currencyExchangeRate = $currencyExchangeRate['data']; + } + } + + + $languageLocale = explode('_', $this->param['lang'], 2); + $searchRequestJson['locale'] = $languageLocale[1]; + + $token = $property['booking_engine_token']; + $deepLinkGenerator = $this->deepLinkGenerator($token, $searchRequestJson); + + $property = reset($property['availabilities']); + $propertyRooms = $property['rooms']; + + //Standard Room Room Only FreeCancellation - Pay at Hotel + + $requestedRoomPriceGroup = []; + foreach ($propertyRooms as $propertyRoomId => $propertyRoom) { + + foreach ($propertyRoom['rates'] as $propertyRoomRateId => $propertyRoomRate) { + + foreach ($propertyRoomRate['requestedRoomPrice'] as $occupancyCode => $requestedRoomPrices) { + + foreach ($requestedRoomPrices['prices'] as $requestedRoomPriceKey => $requestedRoomPrice) { + + $roomRateKeyName = $occupancyCode . ' ' . $requestedRoomPrice['room']['name'] . ' ' . $requestedRoomPrice['rate']['boardName'] . ' ' . $requestedRoomPrice['name']; + + $roomRateKeycode = $requestedRoomPrice['rate']['accommodationCode'] . '|' . (isset($requestedRoomPrice['cancellationPolicy']['id']) ? $requestedRoomPrice['cancellationPolicy']['id'] : 0) . '|' . $requestedRoomPrice['bookingPaymentType']['code']; + + //$requestedRoomPriceGroup[$roomRateKeycode][$occupancyCode][] = $roomRateKeyName . '-' . $requestedRoomPrice['rateKeyCode']; + $requestedRoomPrice['dailyAverageDiscount'] = $requestedRoomPrices['dailyAverageDiscount']; + $requestedRoomPriceGroup[$roomRateKeycode][$occupancyCode][] = $requestedRoomPrice; + + } + + + } + + } + } + + $roomOccupancy = $this->roomOccupancy($searchRequestJson['rooms']); + + + $roomRateBoardGroup = []; + foreach ($requestedRoomPriceGroup as $groupKey => $groupValue) { + + $counter = 0; + + foreach ($groupValue as $groupValueOccupancyCode => $groupValueOccupancy) { + foreach ($groupValueOccupancy as $groupValueOccupancyCodeX => $groupValueOccupancyVal) { + + foreach ($roomOccupancy as $occupancyOrder => $occupancy) { + + $occupancyCheckKey = $occupancy . '-' . $occupancyOrder; + + if ($groupValueOccupancyCode == $occupancy && !isset($roomRateBoardGroup[$groupKey][$counter][$occupancyCheckKey])) { + $roomRateBoardGroup[$groupKey][$counter][$occupancyCheckKey] = $groupValueOccupancyVal; + if (count($roomRateBoardGroup[$groupKey][$counter]) == count($roomOccupancy)) { + $counter++; + } + } + + } + + } + } + } + + + $roomRateTypes = []; + foreach ($roomRateBoardGroup as $boardGroups) { + + foreach ($boardGroups as $boardGroups) { + + + $roomRateTypesGroup = []; + foreach ($boardGroups as $requestedRoomPrice) { + + $roomRateKeyName = $requestedRoomPrice['room']['name'] . ' ' . $requestedRoomPrice['rate']['boardName'] . ' ' . $requestedRoomPrice['name']; + + $total = $requestedRoomPrice['total']; + $totalBeforeDiscount = $requestedRoomPrice['total']; + $totalBeforeDiscountDailyAverage = ($totalBeforeDiscount / $numberOfStay); + + $discounts = []; + if ($requestedRoomPrice['dailyAverageDiscount'] > 0) { + + $totalBeforeDiscount = ($total / (100 - (double)$requestedRoomPrice['dailyAverageDiscount'])) * 100; + $totalBeforeDiscountDailyAverage = ($totalBeforeDiscount / $numberOfStay); + $totalDiscount = $totalBeforeDiscount - $total; + $dailyDiscountAverage = ($totalDiscount / $numberOfStay); + + + $netRate = $dailyDiscountAverage / (1 + ($this->tax / 100) + ($this->cityTax / 100)); + $vat = $netRate * ($this->tax / 100); + $localTax = $netRate * ($this->cityTax / 100); + + + $finalRateDiscount = moneyDoubleFormatDecimal($localTax * $currencyExchangeRate) + moneyDoubleFormatDecimal($netRate * $currencyExchangeRate) + moneyDoubleFormatDecimal($vat * $currencyExchangeRate); + + $discounts[] = [ + 'booking_fee' => 0, + 'final_rate' => moneyDoubleFormatDecimal($finalRateDiscount), //moneyDoubleFormatDecimal($dailyDiscountAverage * $currencyExchangeRate), + 'hotel_fee' => 0, + 'local_tax' => moneyDoubleFormatDecimal($localTax * $currencyExchangeRate), + 'marketing_text' => $requestedRoomPrice['dailyAverageDiscount'] . '% Special Discount', + 'net_rate' => moneyDoubleFormatDecimal($netRate * $currencyExchangeRate), + 'resort_fee' => 0, + 'service_charge' => 0, + 'vat' => moneyDoubleFormatDecimal($vat * $currencyExchangeRate), + ]; + + } + + $netRate = $totalBeforeDiscountDailyAverage / (1 + ($this->tax / 100) + ($this->cityTax / 100)); + $vat = $netRate * ($this->tax / 100); + $localTax = $netRate * ($this->cityTax / 100); + + + $finalRate = moneyDoubleFormatDecimal($localTax * $currencyExchangeRate) + moneyDoubleFormatDecimal($netRate * $currencyExchangeRate) + moneyDoubleFormatDecimal($vat * $currencyExchangeRate); + + $roomRateTypesGroup[][$roomRateKeyName] = [ + 'booking_fee' => 0, + 'breakfast_included' => in_array($requestedRoomPrice['rate']['accommodationCode'], [10, 11, 14, 15]) ? true : false, + 'currency' => $requestCurrency, + 'discounts' => $discounts, + 'final_rate' => moneyDoubleFormatDecimal($finalRate),//moneyDoubleFormatDecimal($totalBeforeDiscountDailyAverage * $currencyExchangeRate), + 'free_cancellation' => $requestedRoomPrice['cancellationPolicy']['isFreeCancellation'] ? true : false, + //'free_cancellation_deadline' => '2018-04-25T16:00:00+00:00', + 'hotel_fee' => 0, + 'local_tax' => moneyDoubleFormatDecimal($localTax * $currencyExchangeRate), + 'meal_code' => isset($this->mealCodeMapping[$requestedRoomPrice['rate']['accommodationCode']]) ? $this->mealCodeMapping[$requestedRoomPrice['rate']['accommodationCode']] : 'RO', + 'net_rate' => moneyDoubleFormatDecimal($netRate * $currencyExchangeRate), + 'payment_type' => isset($this->paymentTypeMapping[$requestedRoomPrice['bookingPaymentType']['code']]) ? $this->paymentTypeMapping[$requestedRoomPrice['bookingPaymentType']['code']] : 'postpaid', + 'resort_fee' => 0, + 'room_code' => $requestedRoomPrice['room']['name'], + 'service_charge' => 0, + 'url' => $deepLinkGenerator, + //'mobileURL' => null', + 'vat' => moneyDoubleFormatDecimal($vat * $currencyExchangeRate), + 'rate_type' => 'DEFAULT', + //'rateKeyCode' => $requestedRoomPrice['rateKeyCode'], + //'dailyAverageDiscount' => $requestedRoomPrice['dailyAverageDiscount'], + ]; + + + } + + $roomRateTypes[] = $roomRateTypesGroup; + + + } + } + + if (!empty($roomRateTypes)) { + + //if (in_array($propertyId, [1275,1325])) { + + $propertyCouponCodeCheck = $this->couponCodeCheck($propertyId, $searchRequestJson['date']['checkIn'], $searchRequestJson['date']['checkOut']); + + if (!empty($propertyCouponCodeCheck)) { + $roomRateTypesWithCoupon = []; + foreach ($roomRateTypes as $roomRateTypeKey => $roomRateType) { + foreach ($roomRateType as $roomRateTypeDetailKey => $roomRateTypeDetails) { + + //dd($roomRateTypeKey, $roomRateTypeDetailKey, $propertyCouponCodeCheck, $roomRateTypeDetail, key($roomRateTypeDetail)); + + $roomRateTypeDetail = reset($roomRateTypeDetails); + + $couponPercentageValue = (100 - $propertyCouponCodeCheck['value']) / 100; + + $finalRateDiscountBeforeReward = collect($roomRateTypeDetail['discounts'])->sum('final_rate'); + $localTaxDiscountBeforeReward = collect($roomRateTypeDetail['discounts'])->sum('local_tax'); + $netRateDiscountBeforeReward = collect($roomRateTypeDetail['discounts'])->sum('net_rate'); + $vatDiscountBeforeReward = collect($roomRateTypeDetail['discounts'])->sum('vat'); + + $finalRateDiscountReward = ($roomRateTypeDetail['final_rate'] - $finalRateDiscountBeforeReward) * $propertyCouponCodeCheck['value'] / 100; + $localTaxDiscountReward = ($roomRateTypeDetail['local_tax'] - $localTaxDiscountBeforeReward) * $propertyCouponCodeCheck['value'] / 100; + $netRateDiscountReward = ($roomRateTypeDetail['net_rate'] - $netRateDiscountBeforeReward) * $propertyCouponCodeCheck['value'] / 100; + $vatDiscountReward = ($roomRateTypeDetail['vat'] - $vatDiscountBeforeReward) * $propertyCouponCodeCheck['value'] / 100; + + $discountReward = [ + 'booking_fee' => 0, + 'final_rate' => moneyDoubleFormatDecimal($finalRateDiscountBeforeReward + $finalRateDiscountReward), + 'hotel_fee' => 0, + 'local_tax' => moneyDoubleFormatDecimal($localTaxDiscountBeforeReward + $localTaxDiscountReward), + 'marketing_text' => 'Special REWARD Discount', + 'net_rate' => moneyDoubleFormatDecimal($netRateDiscountBeforeReward + $netRateDiscountReward), + 'resort_fee' => 0, + 'service_charge' => 0, + 'vat' => moneyDoubleFormatDecimal($vatDiscountBeforeReward + $vatDiscountReward), + ]; + + $discountReward['marketing_text'] = number_format($discountReward['final_rate'] / $roomRateTypeDetail['final_rate'] * 100) . '% Special Popup Discount'; + + unset($roomRateTypeDetail['discounts']); + + $roomRateTypeDetail['discounts'][] = $discountReward; + + //$roomRateTypeDetail['rate_type'] = 'REWARD'; + $roomRateTypeDetail['rate_type'] = 'DEFAULT'; + + + $roomRateTypesWithCoupon[$roomRateTypeKey][$roomRateTypeDetailKey][key($roomRateTypeDetails)] = $roomRateTypeDetail; + + } + + unset($roomRateTypes[$roomRateTypeKey]); + + } + + $roomRateTypes = array_merge($roomRateTypes, $roomRateTypesWithCoupon); + } + + //} + + + $hotelList['root']['hotels'][$hotelCounter] = [ + 'hotel_id' => $propertyId, + 'room_types' => $roomRateTypes + ]; + + $hotelCounter++; + + } + + + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + $hotelList['root']['hotels'] = []; + } + + $responseTime = microtime(true) - $requestTime; + + + //Log::debug(json_encode($this->param) . ' - Response Time: ' . number_format($responseTime, 2) . ' - Status: ' . count($hotelList['root']['hotels'])); + + + return response()->json($hotelList); + + } + + public function hotel(Request $request) + { + + $response = ['status' => true, 'message' => '']; + + + $hotelList = []; + + $hotelList['api_version'] = 4; + $hotelList['lang'] = 'en_GB'; + $hotelList['hotels'] = []; + + try { + + + $channelManagerPropertyMappingCriteria['criteria'] = [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ]; + $channelManagerPropertyMappingCriteria['with'] = [ + 'property.propertyType', 'property.propertyContact', 'property.propertyWeb', 'property.country' + ]; + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $propertyMapping) { + + if ($propertyMapping['status'] != 1) { + continue; + } + + $metaAddress = json_decode($propertyMapping['property']['property_contact']['meta'], 1); + + + $hotelList['hotels'][] = [ + 'partner_reference' => $propertyMapping['property']['id'], + 'name' => $propertyMapping['property']['name'], + 'street' => fillOnUndefined($metaAddress, 'street'), + 'city' => fillOnUndefined($metaAddress, 'city'), + 'postal_code' => fillOnUndefined($metaAddress, 'zip_code'), + //'state' => null, + 'country' => $propertyMapping['property']['country']['name'], + 'latitude' => $propertyMapping['property']['property_contact']['latitude'], + 'longitude' => $propertyMapping['property']['property_contact']['longitude'], + 'desc' => $propertyMapping['property']['name'], + 'amenities' => null,//['Beauty Center', 'Business Center', 'Café/ Bistro', 'Sauna'], + 'url' => $propertyMapping['property']['property_web']['webProtocolUrl'], + 'email' => $propertyMapping['property']['property_contact']['email'], + 'phone' => $propertyMapping['property']['property_contact']['phone'], + 'fax' => $propertyMapping['property']['property_contact']['fax'] + ]; + + //dd(json_decode($propertyMapping['property']['property_contact']['meta'],1)); + + $addressExplode = explode('/', $propertyMapping['property']['property_contact']['address'], 2); + $street = isset($addressExplode[0]) ? $addressExplode[0] : $propertyMapping['property']['property_contact']['address']; + $cityExplode = isset($addressExplode[1]) ? explode(',', $addressExplode[1]) : (isset($addressExplode[0]) ? explode(',', $addressExplode[0]) : null); + $city = isset($cityExplode[0]) ? $cityExplode[0] : null; + + $streetExplode = explode(', ', $street); + unset($streetExplode[count($streetExplode) - 1]); + $street = implode(', ', $streetExplode); + + $metaUpdate = [ + 'street' => $street, + 'city' => $city, + 'zip_code' => $propertyMapping['property']['property_contact']['zip_code'], + ]; + + if (empty($propertyMapping['property']['property_contact']['meta'])) { + PropertyContact::where('id', $propertyMapping['property']['property_contact']['id'])->update(['meta' => json_encode($metaUpdate)]); + } + + + //dd($propertyMapping['property']['property_contact']['id'],$metaUpdate); + + } + + + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + $hotelList['root']['hotels'] = []; + } + + + return response()->json($hotelList); + + } + + public function campaign(Request $request) + { + + + $hotelList = []; + + $hotelExcludeList = []; + $hotelExcludeList['TR'] = [721, 729]; + + try { + + + $channelManagerPropertyMappingCriteria['criteria'] = [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ]; + $channelManagerPropertyMappingCriteria['with'] = [ + 'property.propertyType', 'property.propertyContact', 'property.propertyWeb', 'property.country' + ]; + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $hotelList[] = [ + 'partner_reference' => 'partner_reference', + 'locale' => 'locale', + 'campaign' => 'campaign' + ]; + + foreach ($channelManagerPropertyMapping['data'] as $propertyMapping) { + + foreach ($this->CPA as $countryCode => $countryValue) { + foreach ($countryValue as $campaignId => $campaignValue) { + + if (isset($this->countryCodeCampaign[$countryCode]) && $this->countryCodeCampaign[$countryCode]['campaignCode'] == $campaignId) { + + if (isset($hotelExcludeList[$countryCode])) { + if (in_array($propertyMapping['property_id'], $hotelExcludeList[$countryCode])) { + continue; + } + } + + $hotelList[] = [ + 'partner_reference' => $propertyMapping['property']['id'], + 'locale' => $countryCode, + 'campaign' => $campaignId + ]; + } + + } + } + + + } + + + } + + + } catch (ApiErrorException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + } + + $out = ""; + foreach ($hotelList as $hotel) { + $out .= implode(",", $hotel) . PHP_EOL; + + } + + header("Content-Type: application/csv"); + header("Content-Disposition: attachment; filename=Extranetwork-Campaign.csv"); + header('Pragma: no-cache'); + header("Expires: 0"); + + echo $out; + die(); + + //return response()->json($hotelList); + + } + + public function cpa(Request $request) + { + + + $cpaList[] = [ + 'locale' => 'locale', + 'campaign' => 'campaign', + 'cpa_value' => 'cpa_value' + ]; + + + try { + + foreach ($this->CPA as $countryCode => $countryValue) { + foreach ($countryValue as $campaignId => $campaignValue) { + + $cpaList[] = [ + 'locale' => $countryCode, + 'campaign' => $campaignId, + 'cpa_value' => $campaignValue + ]; + + } + + } + + } catch (ApiErrorException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + } + + + $out = ""; + foreach ($cpaList as $cpa) { + $out .= implode(",", $cpa) . PHP_EOL; + + } + + header("Content-Type: application/csv"); + header("Content-Disposition: attachment; filename=Extranetwork-CPA.csv"); + header('Pragma: no-cache'); + header("Expires: 0"); + + echo $out; + die(); + + //return response()->json($hotelList); + + } + + +} diff --git a/app/Http/Controllers/MetaSearch/Trivago/v1/_TrivagoController.php b/app/Http/Controllers/MetaSearch/Trivago/v1/_TrivagoController.php new file mode 100644 index 0000000..af70e8a --- /dev/null +++ b/app/Http/Controllers/MetaSearch/Trivago/v1/_TrivagoController.php @@ -0,0 +1,884 @@ +username = 'trivago'; + $this->password = 'P8MUQtNP6TkKMz3E'; + + $this->channelManagerId = 11;//Trivago + + $this->request = $request; + $this->currencyService = $currencyService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->propertyChannelCouponService = $propertyChannelCouponService; + + $payload = $this->request->getContent(); + parse_str(urldecode($payload), $payloadDecode); + $this->param = $payloadDecode; + + $this->authorization = $request->header('authorization'); + + $this->tax = 10; + $this->cityTax = 2; + + + $this->mealCodeMapping = [ + 10 => 'AI', + 11 => 'BB', + 13 => 'RO', + 14 => 'FB', + 15 => 'HB', + ]; + + $this->paymentTypeMapping = [ + 'CRD' => 'prepaid', + 'HTL' => 'postpaid' + ]; + + $this->CPA['TR'] = ["1" => 9.60, "2" => 10.80, "3" => 11.00, "4" => 11.20, "5" => 11.40, "6" => 11.60, "7" => 11.80, "8" => 12.00]; + $this->CPA['DE'] = ["1" => 10.80, "2" => 11.00, "3" => 11.20, "4" => 11.40, "5" => 11.60, "6" => 11.80, "7" => 12.00, "8" => 12.20]; + $this->CPA['UK'] = ["1" => 10.80, "2" => 11.00, "3" => 11.20, "4" => 11.40, "5" => 11.60, "6" => 11.80, "7" => 12.00, "8" => 12.20]; + $this->CPA['US'] = ["1" => 10.80, "2" => 11.00, "3" => 11.20, "4" => 11.40, "5" => 11.60, "6" => 11.80, "7" => 12.00, "8" => 12.20]; + + $this->countryCodeCampaign = [ + 'DE' => [ + 'code' => 'DE', + 'campaignCode' => 7 + ], + 'TR' => [ + 'code' => 'TR', + 'campaignCode' => 8 + ], + 'US' => [ + 'code' => 'US', + 'campaignCode' => 7 + ], + 'UK' => [ + 'code' => 'UK', + 'campaignCode' => 7 + ] + ]; + + } + + + public function checkAuthentication($authorization) + { + + $response = ['status' => false, 'message' => '']; + + $basicAuth = 'Basic ' . base64_encode($this->username . ':' . $this->password); + + if ($authorization != $basicAuth) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + + return response()->json($response); + + } + + public function requestParamConverter($requestParam = []) + { + + $searchRequestJson = []; + + $searchRequestJson['date']['checkIn'] = Carbon::parse($requestParam['start_date'])->toDateString(); + $searchRequestJson['date']['checkOut'] = Carbon::parse($requestParam['end_date'])->toDateString(); + + $rooms = []; + for ($i = 0; $i < $requestParam['num_rooms']; $i++) { + + $roomCounter = ($i + 1); + + if (isset($requestParam['room_adults_' . $roomCounter])) { + $rooms[$i]['adults'] = $requestParam['room_adults_' . $roomCounter]; + } + + + $rooms[$i]['children'] = 0; + $rooms[$i]['age'] = null; + if (isset($requestParam['room_childs_' . $roomCounter])) { + + $childAges = json_decode($requestParam['room_childs_' . $roomCounter]); + + $rooms[$i]['children'] = count($childAges); + $rooms[$i]['age'] = $childAges; + } + + } + + $searchRequestJson['rooms'] = $rooms; + $searchRequestJson['property'] = json_decode($requestParam['hotels']); + + return $searchRequestJson; + + } + + public function roomOccupancy($requestParam) + { + + $roomOccupancy = []; + foreach ($requestParam as $room) { + $adults = str_repeat('A', $room['adults']); + + $children = []; + if ($room['children'] > 0 && !empty($room['age'])) { + foreach ($room['age'] as $child) { + $children[] = 'C' . $child; + } + } + + $roomOccupancy[] = $adults . implode('', $children); + + } + + return $roomOccupancy; + } + + public function deepLinkGenerator($token, $requestParam = []) + { + + $deepLink = 'https://be.extranetwork.com/' . $token . '/en/search/'; + + + $deepLink .= Carbon::parse($requestParam['date']['checkIn'])->format('Ymd') . '/'; + $deepLink .= Carbon::parse($requestParam['date']['checkOut'])->format('Ymd') . '/'; + + + $roomOccupancy = $this->roomOccupancy($requestParam['rooms']); + + $deepLink .= implode(',', $roomOccupancy);; + + $deepLink .= '?utm_medium=trivago&utm_source=trivago&locale=' . $requestParam['locale']; + + return $deepLink; + + } + + public function couponCodeCheck($propertyId, $checkIn, $checkOut) + { + //$propertyChannelCoupon + $propertyChannelCouponsCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_id', 'condition' => '=', 'value' => 1], + ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::now()->toDateString()], + ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ], + ]; + + $propertyChannelCoupons = $this->propertyChannelCouponService->select($propertyChannelCouponsCriteria); + + if ($propertyChannelCoupons['status'] == 'success') { + $propertyChannelCoupons = $propertyChannelCoupons['data']; + + foreach ($propertyChannelCoupons as $propertyChannelCouponKey => $propertyChannelCouponData) { + + //Reservation Date Check + if (!is_null($propertyChannelCouponData['reservation_start_date']) && !is_null($propertyChannelCouponData['reservation_end_date'])) { + $reservationDateCheck = Carbon::parse($propertyChannelCouponData['reservation_start_date'])->lessThanOrEqualTo(Carbon::parse($checkIn)) + && Carbon::parse($propertyChannelCouponData['reservation_end_date'])->greaterThanOrEqualTo(Carbon::parse($checkOut)); + if (!$reservationDateCheck) { + unset($propertyChannelCoupons[$propertyChannelCouponKey]); + } + + } + } + + } else { + $propertyChannelCoupons = []; + } + + $propertyChannelCoupon = !empty($propertyChannelCoupons) ? reset($propertyChannelCoupons) : null; + + return $propertyChannelCoupon; + + } + + public function availability(Request $request) + { + + $response = ['status' => true, 'message' => '']; + + $checkAuthentication = $this->checkAuthentication($this->authorization); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $searchController = App::make("App\Http\Controllers\BookingEngine\V1\SearchController"); + + $requestTime = microtime(true); + + + $hotelList['root'] = $this->param; + unset($hotelList['root']['hotels']); + $hotelList['root']['hotel_ids'] = json_decode($this->param['hotels']); + $hotelList['root']['hotels'] = []; + + $hotelList['root']['api_version'] = (integer)$hotelList['root']['api_version']; + $hotelList['root']['num_rooms'] = (integer)$hotelList['root']['num_rooms']; + + for ($i = 0; $i < $hotelList['root']['num_rooms']; $i++) { + $roomCounter = ($i + 1); + if (isset($hotelList['root']['room_adults_' . $roomCounter])) { + $hotelList['root']['room_adults_' . $roomCounter] = (integer)$hotelList['root']['room_adults_' . $roomCounter]; + } + } + + $requestCurrency = $this->param['currency']; + + try { + + + //Hotels Check + $channelManagerPropertyList = []; + $channelManagerPropertyMappingCriteria['criteria'] = [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ]; + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + $channelManagerPropertyList = pickItemFromArray('property_id', $channelManagerPropertyMapping['data']); + } + + $availableHotelIds = array_intersect($hotelList['root']['hotel_ids'], $channelManagerPropertyList); + + if (empty($availableHotelIds)) { + throw new ApiErrorException('Hotel not found'); + } + + $this->param['hotels'] = json_encode($availableHotelIds); + //Hotels Check + + + $searchRequestJson = $this->requestParamConverter($this->param); + + $searchRequestJson['ipAddress'] = $request->getClientIp(); + $searchRequestJson['isMobile'] = null; + $searchRequestJson['noneCacheSearch'] = true; + $searchRequestJson['ipAddress'] = $request->getClientIp(); + $searchRequestJson['justPriceValues'] = true; + + + $cacheKeyParam = $this->param; + unset($cacheKeyParam['currency']); + unset($cacheKeyParam['rate_model']); + unset($cacheKeyParam['lang']); + $cacheKey = 'TRV:' . md5(implode(',', $cacheKeyParam)); + + //Cache::forget($cacheKey); + + if (Cache::has($cacheKey)) { + $search = Cache::get($cacheKey); + } else { + $requestCreate = Request::create(null, null, [], [], [], [], json_encode($searchRequestJson)); + $requestCreate->headers->set('channelId', '1'); + //$requestCreate->headers->set('bookingEnginePropertyId', null); + $requestCreate->headers->set('channelToken', 'ece3ef02-42e7-92b7-f379-08226ed7a0f3');//TODO: from DB + $requestCreate->headers->set('bookingEngineToken', null); + $search = $searchController->search($requestCreate); + + Cache::put($cacheKey, $search, 60 * 60);//1 Day 24 * 60 * 60 + } + + $search = json_decode(json_encode($search), 1); + + if ($search['original']['status'] != 200) { + throw new ApiErrorException('Hotel not found'); + } + + $properties = $search['original']['data']['properties']; + + if (empty($properties)) { + throw new ApiErrorException('Hotel not found'); + } + + $hotelCounter = 0; + $numberOfStay = Carbon::parse($searchRequestJson['date']['checkIn'])->diffInDays(Carbon::parse($searchRequestJson['date']['checkOut'])); + + foreach ($properties as $propertyId => $property) { + + if (!isset($property['currency'])) { + continue; + } + + $currencyExchangeRate = 1; + if ($requestCurrency != $property['currency']) { + $currencyExchangeRate = $this->currencyService->lastExchangeRate($property['currency'], $requestCurrency); + if ($currencyExchangeRate['status'] == 'success') { + $currencyExchangeRate = $currencyExchangeRate['data']; + } + } + + + $languageLocale = explode('_', $this->param['lang'], 2); + $searchRequestJson['locale'] = $languageLocale[1]; + + $token = $property['booking_engine_token']; + $deepLinkGenerator = $this->deepLinkGenerator($token, $searchRequestJson); + + $property = reset($property['availabilities']); + $propertyRooms = $property['rooms']; + + //Standard Room Room Only FreeCancellation - Pay at Hotel + + $requestedRoomPriceGroup = []; + foreach ($propertyRooms as $propertyRoomId => $propertyRoom) { + + foreach ($propertyRoom['rates'] as $propertyRoomRateId => $propertyRoomRate) { + + foreach ($propertyRoomRate['requestedRoomPrice'] as $occupancyCode => $requestedRoomPrices) { + + foreach ($requestedRoomPrices['prices'] as $requestedRoomPriceKey => $requestedRoomPrice) { + + $roomRateKeyName = $occupancyCode . ' ' . $requestedRoomPrice['room']['name'] . ' ' . $requestedRoomPrice['rate']['boardName'] . ' ' . $requestedRoomPrice['name']; + + $roomRateKeycode = $requestedRoomPrice['rate']['accommodationCode'] . '|' . (isset($requestedRoomPrice['cancellationPolicy']['id']) ? $requestedRoomPrice['cancellationPolicy']['id'] : 0) . '|' . $requestedRoomPrice['bookingPaymentType']['code']; + + //$requestedRoomPriceGroup[$roomRateKeycode][$occupancyCode][] = $roomRateKeyName . '-' . $requestedRoomPrice['rateKeyCode']; + $requestedRoomPrice['dailyAverageDiscount'] = $requestedRoomPrices['dailyAverageDiscount']; + $requestedRoomPriceGroup[$roomRateKeycode][$occupancyCode][] = $requestedRoomPrice; + + } + + + } + + } + } + + $roomOccupancy = $this->roomOccupancy($searchRequestJson['rooms']); + + + $roomRateBoardGroup = []; + foreach ($requestedRoomPriceGroup as $groupKey => $groupValue) { + + $counter = 0; + + foreach ($groupValue as $groupValueOccupancyCode => $groupValueOccupancy) { + foreach ($groupValueOccupancy as $groupValueOccupancyCodeX => $groupValueOccupancyVal) { + + foreach ($roomOccupancy as $occupancyOrder => $occupancy) { + + $occupancyCheckKey = $occupancy . '-' . $occupancyOrder; + + if ($groupValueOccupancyCode == $occupancy && !isset($roomRateBoardGroup[$groupKey][$counter][$occupancyCheckKey])) { + $roomRateBoardGroup[$groupKey][$counter][$occupancyCheckKey] = $groupValueOccupancyVal; + if (count($roomRateBoardGroup[$groupKey][$counter]) == count($roomOccupancy)) { + $counter++; + } + } + + } + + } + } + } + + + $roomRateTypes = []; + foreach ($roomRateBoardGroup as $boardGroups) { + + foreach ($boardGroups as $boardGroups) { + + + $roomRateTypesGroup = []; + foreach ($boardGroups as $requestedRoomPrice) { + + $roomRateKeyName = $requestedRoomPrice['room']['name'] . ' ' . $requestedRoomPrice['rate']['boardName'] . ' ' . $requestedRoomPrice['name']; + + $total = $requestedRoomPrice['total']; + $totalBeforeDiscount = $requestedRoomPrice['total']; + $totalBeforeDiscountDailyAverage = ($totalBeforeDiscount / $numberOfStay); + + $discounts = []; + if ($requestedRoomPrice['dailyAverageDiscount'] > 0) { + + $totalBeforeDiscount = ($total / (100 - (double)$requestedRoomPrice['dailyAverageDiscount'])) * 100; + $totalBeforeDiscountDailyAverage = ($totalBeforeDiscount / $numberOfStay); + $totalDiscount = $totalBeforeDiscount - $total; + $dailyDiscountAverage = ($totalDiscount / $numberOfStay); + + + $netRate = $dailyDiscountAverage / (1 + ($this->tax / 100) + ($this->cityTax / 100)); + $vat = $netRate * ($this->tax / 100); + $localTax = $netRate * ($this->cityTax / 100); + + + $finalRateDiscount = moneyDoubleFormatDecimal($localTax * $currencyExchangeRate) + moneyDoubleFormatDecimal($netRate * $currencyExchangeRate) + moneyDoubleFormatDecimal($vat * $currencyExchangeRate); + + $discounts[] = [ + 'booking_fee' => 0, + 'final_rate' => moneyDoubleFormatDecimal($finalRateDiscount), //moneyDoubleFormatDecimal($dailyDiscountAverage * $currencyExchangeRate), + 'hotel_fee' => 0, + 'local_tax' => moneyDoubleFormatDecimal($localTax * $currencyExchangeRate), + 'marketing_text' => $requestedRoomPrice['dailyAverageDiscount'] . '% Special Discount', + 'net_rate' => moneyDoubleFormatDecimal($netRate * $currencyExchangeRate), + 'resort_fee' => 0, + 'service_charge' => 0, + 'vat' => moneyDoubleFormatDecimal($vat * $currencyExchangeRate), + ]; + + } + + $netRate = $totalBeforeDiscountDailyAverage / (1 + ($this->tax / 100) + ($this->cityTax / 100)); + $vat = $netRate * ($this->tax / 100); + $localTax = $netRate * ($this->cityTax / 100); + + + $finalRate = moneyDoubleFormatDecimal($localTax * $currencyExchangeRate) + moneyDoubleFormatDecimal($netRate * $currencyExchangeRate) + moneyDoubleFormatDecimal($vat * $currencyExchangeRate); + + $roomRateTypesGroup[][$roomRateKeyName] = [ + 'booking_fee' => 0, + 'breakfast_included' => in_array($requestedRoomPrice['rate']['accommodationCode'], [10, 11, 14, 15]) ? true : false, + 'currency' => $requestCurrency, + 'discounts' => $discounts, + 'final_rate' => moneyDoubleFormatDecimal($finalRate),//moneyDoubleFormatDecimal($totalBeforeDiscountDailyAverage * $currencyExchangeRate), + 'free_cancellation' => $requestedRoomPrice['cancellationPolicy']['isFreeCancellation'] ? true : false, + //'free_cancellation_deadline' => '2018-04-25T16:00:00+00:00', + 'hotel_fee' => 0, + 'local_tax' => moneyDoubleFormatDecimal($localTax * $currencyExchangeRate), + 'meal_code' => isset($this->mealCodeMapping[$requestedRoomPrice['rate']['accommodationCode']]) ? $this->mealCodeMapping[$requestedRoomPrice['rate']['accommodationCode']] : 'RO', + 'net_rate' => moneyDoubleFormatDecimal($netRate * $currencyExchangeRate), + 'payment_type' => isset($this->paymentTypeMapping[$requestedRoomPrice['bookingPaymentType']['code']]) ? $this->paymentTypeMapping[$requestedRoomPrice['bookingPaymentType']['code']] : 'postpaid', + 'resort_fee' => 0, + 'room_code' => $requestedRoomPrice['room']['name'], + 'service_charge' => 0, + 'url' => $deepLinkGenerator, + //'mobileURL' => null', + 'vat' => moneyDoubleFormatDecimal($vat * $currencyExchangeRate), + 'rate_type' => 'DEFAULT', + //'rateKeyCode' => $requestedRoomPrice['rateKeyCode'], + //'dailyAverageDiscount' => $requestedRoomPrice['dailyAverageDiscount'], + ]; + + + } + + $roomRateTypes[] = $roomRateTypesGroup; + + + } + } + + if (!empty($roomRateTypes)) { + + //if (in_array($propertyId, [1275,1325])) { + + $propertyCouponCodeCheck = $this->couponCodeCheck($propertyId, $searchRequestJson['date']['checkIn'], $searchRequestJson['date']['checkOut']); + + if (!empty($propertyCouponCodeCheck)) { + $roomRateTypesWithCoupon = []; + foreach ($roomRateTypes as $roomRateTypeKey => $roomRateType) { + foreach ($roomRateType as $roomRateTypeDetailKey => $roomRateTypeDetails) { + + //dd($roomRateTypeKey, $roomRateTypeDetailKey, $propertyCouponCodeCheck, $roomRateTypeDetail, key($roomRateTypeDetail)); + + $roomRateTypeDetail = reset($roomRateTypeDetails); + + $couponPercentageValue = (100 - $propertyCouponCodeCheck['value']) / 100; + + $finalRateDiscountBeforeReward = collect($roomRateTypeDetail['discounts'])->sum('final_rate'); + $localTaxDiscountBeforeReward = collect($roomRateTypeDetail['discounts'])->sum('local_tax'); + $netRateDiscountBeforeReward = collect($roomRateTypeDetail['discounts'])->sum('net_rate'); + $vatDiscountBeforeReward = collect($roomRateTypeDetail['discounts'])->sum('vat'); + + $finalRateDiscountReward = ($roomRateTypeDetail['final_rate'] - $finalRateDiscountBeforeReward) * $propertyCouponCodeCheck['value'] / 100; + $localTaxDiscountReward = ($roomRateTypeDetail['local_tax'] - $localTaxDiscountBeforeReward) * $propertyCouponCodeCheck['value'] / 100; + $netRateDiscountReward = ($roomRateTypeDetail['net_rate'] - $netRateDiscountBeforeReward) * $propertyCouponCodeCheck['value'] / 100; + $vatDiscountReward = ($roomRateTypeDetail['vat'] - $vatDiscountBeforeReward) * $propertyCouponCodeCheck['value'] / 100; + + $discountReward = [ + 'booking_fee' => 0, + 'final_rate' => moneyDoubleFormatDecimal($finalRateDiscountBeforeReward + $finalRateDiscountReward), + 'hotel_fee' => 0, + 'local_tax' => moneyDoubleFormatDecimal($localTaxDiscountBeforeReward + $localTaxDiscountReward), + 'marketing_text' => 'Special REWARD Discount', + 'net_rate' => moneyDoubleFormatDecimal($netRateDiscountBeforeReward + $netRateDiscountReward), + 'resort_fee' => 0, + 'service_charge' => 0, + 'vat' => moneyDoubleFormatDecimal($vatDiscountBeforeReward + $vatDiscountReward), + ]; + + + /*foreach ($roomRateTypeDetail['discounts'] as $discountKey => $discount) { + + $discount['vat'] = moneyDoubleFormatDecimal($discount['vat'] * $couponPercentageValue); + $discount['net_rate'] = moneyDoubleFormatDecimal($discount['net_rate'] * $couponPercentageValue); + $discount['local_tax'] = moneyDoubleFormatDecimal($discount['local_tax'] * $couponPercentageValue); + $discount['final_rate'] = moneyDoubleFormatDecimal($discount['net_rate'] + $discount['vat'] + $discount['local_tax']); + + $roomRateTypeDetail['discounts'][$discountKey] = $discount; + + } + + + $roomRateTypeDetail['vat'] = moneyDoubleFormatDecimal($roomRateTypeDetail['vat'] * $couponPercentageValue); + $roomRateTypeDetail['net_rate'] = moneyDoubleFormatDecimal($roomRateTypeDetail['net_rate'] * $couponPercentageValue); + $roomRateTypeDetail['local_tax'] = moneyDoubleFormatDecimal($roomRateTypeDetail['local_tax'] * $couponPercentageValue); + $roomRateTypeDetail['final_rate'] = moneyDoubleFormatDecimal($roomRateTypeDetail['net_rate'] + $roomRateTypeDetail['vat'] + $roomRateTypeDetail['local_tax']);*/ + + unset($roomRateTypeDetail['discounts']); + + $roomRateTypeDetail['discounts'][] = $discountReward; + + $roomRateTypeDetail['rate_type'] = 'REWARD'; + + $roomRateTypesWithCoupon[$roomRateTypeKey][$roomRateTypeDetailKey][key($roomRateTypeDetails)] = $roomRateTypeDetail; + + } + + } + + $roomRateTypes = array_merge($roomRateTypes, $roomRateTypesWithCoupon); + } + + //} + + + $hotelList['root']['hotels'][$hotelCounter] = [ + 'hotel_id' => $propertyId, + 'room_types' => $roomRateTypes + ]; + + $hotelCounter++; + + } + + + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + $hotelList['root']['hotels'] = []; + } + + $responseTime = microtime(true) - $requestTime; + + + //Log::debug(json_encode($this->param) . ' - Response Time: ' . number_format($responseTime, 2) . ' - Status: ' . count($hotelList['root']['hotels'])); + + + return response()->json($hotelList); + + } + + public function hotel(Request $request) + { + + $response = ['status' => true, 'message' => '']; + + + $hotelList = []; + + $hotelList['api_version'] = 4; + $hotelList['lang'] = 'en_GB'; + $hotelList['hotels'] = []; + + try { + + + $channelManagerPropertyMappingCriteria['criteria'] = [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ]; + $channelManagerPropertyMappingCriteria['with'] = [ + 'property.propertyType', 'property.propertyContact', 'property.propertyWeb', 'property.country' + ]; + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $propertyMapping) { + + if ($propertyMapping['status'] != 1) { + continue; + } + + $metaAddress = json_decode($propertyMapping['property']['property_contact']['meta'], 1); + + + $hotelList['hotels'][] = [ + 'partner_reference' => $propertyMapping['property']['id'], + 'name' => $propertyMapping['property']['name'], + 'street' => fillOnUndefined($metaAddress, 'street'), + 'city' => fillOnUndefined($metaAddress, 'city'), + 'postal_code' => fillOnUndefined($metaAddress, 'zip_code'), + //'state' => null, + 'country' => $propertyMapping['property']['country']['name'], + 'latitude' => $propertyMapping['property']['property_contact']['latitude'], + 'longitude' => $propertyMapping['property']['property_contact']['longitude'], + 'desc' => $propertyMapping['property']['name'], + 'amenities' => null,//['Beauty Center', 'Business Center', 'Café/ Bistro', 'Sauna'], + 'url' => $propertyMapping['property']['property_web']['webProtocolUrl'], + 'email' => $propertyMapping['property']['property_contact']['email'], + 'phone' => $propertyMapping['property']['property_contact']['phone'], + 'fax' => $propertyMapping['property']['property_contact']['fax'] + ]; + + //dd(json_decode($propertyMapping['property']['property_contact']['meta'],1)); + + $addressExplode = explode('/', $propertyMapping['property']['property_contact']['address'], 2); + $street = isset($addressExplode[0]) ? $addressExplode[0] : $propertyMapping['property']['property_contact']['address']; + $cityExplode = isset($addressExplode[1]) ? explode(',', $addressExplode[1]) : (isset($addressExplode[0]) ? explode(',', $addressExplode[0]) : null); + $city = isset($cityExplode[0]) ? $cityExplode[0] : null; + + $streetExplode = explode(', ', $street); + unset($streetExplode[count($streetExplode) - 1]); + $street = implode(', ', $streetExplode); + + $metaUpdate = [ + 'street' => $street, + 'city' => $city, + 'zip_code' => $propertyMapping['property']['property_contact']['zip_code'], + ]; + + if (empty($propertyMapping['property']['property_contact']['meta'])) { + PropertyContact::where('id', $propertyMapping['property']['property_contact']['id'])->update(['meta' => json_encode($metaUpdate)]); + } + + + //dd($propertyMapping['property']['property_contact']['id'],$metaUpdate); + + } + + + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!$response['status']) { + $hotelList['root']['hotels'] = []; + } + + + return response()->json($hotelList); + + } + + public function campaign(Request $request) + { + + + $hotelList = []; + + $hotelExcludeList = []; + $hotelExcludeList['TR'] = [721, 729]; + + try { + + + $channelManagerPropertyMappingCriteria['criteria'] = [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ]; + $channelManagerPropertyMappingCriteria['with'] = [ + 'property.propertyType', 'property.propertyContact', 'property.propertyWeb', 'property.country' + ]; + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + $hotelList[] = [ + 'partner_reference' => 'partner_reference', + 'locale' => 'locale', + 'campaign' => 'campaign' + ]; + + foreach ($channelManagerPropertyMapping['data'] as $propertyMapping) { + + foreach ($this->CPA as $countryCode => $countryValue) { + foreach ($countryValue as $campaignId => $campaignValue) { + + if (isset($this->countryCodeCampaign[$countryCode]) && $this->countryCodeCampaign[$countryCode]['campaignCode'] == $campaignId) { + + if (isset($hotelExcludeList[$countryCode])) { + if (in_array($propertyMapping['property_id'], $hotelExcludeList[$countryCode])) { + continue; + } + } + + $hotelList[] = [ + 'partner_reference' => $propertyMapping['property']['id'], + 'locale' => $countryCode, + 'campaign' => $campaignId + ]; + } + + } + } + + + } + + + } + + + } catch (ApiErrorException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + } + + $out = ""; + foreach ($hotelList as $hotel) { + $out .= implode(",", $hotel) . PHP_EOL; + + } + + header("Content-Type: application/csv"); + header("Content-Disposition: attachment; filename=Extranetwork-Campaign.csv"); + header('Pragma: no-cache'); + header("Expires: 0"); + + echo $out; + die(); + + //return response()->json($hotelList); + + } + + public function cpa(Request $request) + { + + + $cpaList[] = [ + 'locale' => 'locale', + 'campaign' => 'campaign', + 'cpa_value' => 'cpa_value' + ]; + + + try { + + foreach ($this->CPA as $countryCode => $countryValue) { + foreach ($countryValue as $campaignId => $campaignValue) { + + $cpaList[] = [ + 'locale' => $countryCode, + 'campaign' => $campaignId, + 'cpa_value' => $campaignValue + ]; + + } + + } + + } catch (ApiErrorException | Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + } + + + $out = ""; + foreach ($cpaList as $cpa) { + $out .= implode(",", $cpa) . PHP_EOL; + + } + + header("Content-Type: application/csv"); + header("Content-Disposition: attachment; filename=Extranetwork-CPA.csv"); + header('Pragma: no-cache'); + header("Expires: 0"); + + echo $out; + die(); + + //return response()->json($hotelList); + + } + + +} diff --git a/app/Http/Controllers/MetaSearch/Yandex/v1/YandexController.php b/app/Http/Controllers/MetaSearch/Yandex/v1/YandexController.php new file mode 100644 index 0000000..2d845b7 --- /dev/null +++ b/app/Http/Controllers/MetaSearch/Yandex/v1/YandexController.php @@ -0,0 +1,541 @@ +username = 'yandex'; + $this->password = 'P8MUQtNP6TkKMz3E'; + + $this->channelManagerId = 13;//Yandex + + $this->request = $request; + $this->currencyService = $currencyService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->propertyChannelCouponService = $propertyChannelCouponService; + + $payload = $this->request->getContent(); + parse_str(urldecode($payload), $payloadDecode); + $this->param = $payloadDecode; + + $this->authorization = $request->header('authorization'); + + $this->tax = 10; + + + $this->mealCodeMapping = [ + 10 => 'AI', + 11 => 'BB', + 13 => 'RO', + 14 => 'FB', + 15 => 'HB', + ]; + + $this->paymentTypeMapping = [ + 'CRD' => 'prepaid', + 'HTL' => 'postpaid' + ]; + + + } + + + public function checkAuthentication($authorization) + { + + $response = ['status' => false, 'message' => '']; + + $basicAuth = 'Basic ' . base64_encode($this->username . ':' . $this->password); + + if ($authorization != $basicAuth) { + $response['message'] = 'Your username or password is incorrect.'; + } else { + $response['status'] = true; + } + + return $response; + + } + + + public function responseError($errorMessage) + { + + $response = [ + 'status' => false, + 'message' => $errorMessage, + ]; + + //channelManagerLogService + if (!is_null($this->channelManagerLogId)) { + $updateDataLog = [ + 'response' => json_encode($response), + 'status' => 0 + ]; + + if (!is_null($this->channelManagerRequestTime)) { + $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime; + } + + $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog); + } + //channelManagerLogService + + + return response()->json($response); + + } + + public function requestParamConverter($requestParam = []) + { + + $searchRequestJson = []; + + $searchRequestJson['date']['checkIn'] = Carbon::parse($requestParam['Checkin'])->toDateString(); + $searchRequestJson['date']['checkOut'] = Carbon::parse($requestParam['Checkin'])->addDays($requestParam['Nights'])->toDateString(); + + + $rooms = []; + $rooms[0]['adults'] = (integer)$requestParam['Context']['OccupancyDetails']['NumAdults']; + $rooms[0]['children'] = (integer)($requestParam['Context']['Occupancy'] - $requestParam['Context']['OccupancyDetails']['NumAdults']); + + $rooms[0]['age'] = []; + if (isset($requestParam['Context']['OccupancyDetails']['Children']['Child'])) { + $requestParam['Context']['OccupancyDetails']['Children']['Child'] = singleElementXMLArray($requestParam['Context']['OccupancyDetails']['Children']['Child']); + } else { + $requestParam['Context']['OccupancyDetails']['Children']['Child'] = []; + } + + foreach ($requestParam['Context']['OccupancyDetails']['Children']['Child'] as $child) { + $rooms[0]['age'][] = $child['@attributes']['age']; + } + + $searchRequestJson['rooms'] = $rooms; + + if (isset($requestParam['PropertyList']['Property'])) { + $requestParam['PropertyList']['Property'] = singleElementXMLArray($requestParam['PropertyList']['Property']); + } else { + $requestParam['PropertyList']['Property'] = []; + } + + + $searchRequestJson['property'] = $requestParam['PropertyList']['Property']; + + return $searchRequestJson; + + } + + public function roomOccupancy($requestParam) + { + + $roomOccupancy = []; + foreach ($requestParam as $room) { + $adults = str_repeat('A', $room['adults']); + + $children = []; + if ($room['children'] > 0 && !empty($room['age'])) { + foreach ($room['age'] as $child) { + $children[] = 'C' . $child; + } + } + + $roomOccupancy[] = $adults . implode('', $children); + + } + + return $roomOccupancy; + } + + public function deepLinkGenerator($token, $requestParam = []) + { + + $deepLink = 'https://be.extranetwork.com/' . $token . '/' . fillOnUndefined($requestParam, 'locale', 'en') . '/search/'; + + + $deepLink .= Carbon::parse($requestParam['date']['checkIn'])->format('Ymd') . '/'; + $deepLink .= Carbon::parse($requestParam['date']['checkOut'])->format('Ymd') . '/'; + + + $roomOccupancy = $this->roomOccupancy($requestParam['rooms']); + + $deepLink .= implode(',', $roomOccupancy);; + + $deepLink .= '?utm_source=yandex'; + + return $deepLink; + + } + + public function couponCodeCheck($propertyId, $checkIn, $checkOut) + { + //$propertyChannelCoupon + $propertyChannelCouponsCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_id', 'condition' => '=', 'value' => 1], + ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::now()->toDateString()], + ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ], + ]; + + $propertyChannelCoupons = $this->propertyChannelCouponService->select($propertyChannelCouponsCriteria); + + if ($propertyChannelCoupons['status'] == 'success') { + $propertyChannelCoupons = $propertyChannelCoupons['data']; + + foreach ($propertyChannelCoupons as $propertyChannelCouponKey => $propertyChannelCouponData) { + + //Reservation Date Check + if (!is_null($propertyChannelCouponData['reservation_start_date']) && !is_null($propertyChannelCouponData['reservation_end_date'])) { + $reservationDateCheck = Carbon::parse($propertyChannelCouponData['reservation_start_date'])->lessThanOrEqualTo(Carbon::parse($checkIn)) + && Carbon::parse($propertyChannelCouponData['reservation_end_date'])->greaterThanOrEqualTo(Carbon::parse($checkOut)); + if (!$reservationDateCheck) { + unset($propertyChannelCoupons[$propertyChannelCouponKey]); + } + + } + } + + } else { + $propertyChannelCoupons = []; + } + + $propertyChannelCoupon = !empty($propertyChannelCoupons) ? reset($propertyChannelCoupons) : null; + + return $propertyChannelCoupon; + + } + + + public function hotel(Request $request) + { + + $response = ['status' => true, 'message' => '']; + + + $listings = new \SimpleXMLElement(''); + + $listings->addChild('language', 'tr'); + + + try { + + + $channelManagerPropertyMappingCriteria['criteria'] = [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ]; + $channelManagerPropertyMappingCriteria['with'] = [ + 'property.propertyType', 'property.propertyContact', 'property.propertyWeb', 'property.country' + ]; + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $propertyMapping) { + + if ($propertyMapping['status'] != 1) { + continue; + } + + $metaAddress = json_decode($propertyMapping['property']['property_contact']['meta'], 1); + + $listing = $listings->addChild('listing'); + + $listing->addChild('id', $propertyMapping['property']['id']); + $listing->addChild('name', htmlspecialchars($propertyMapping['property']['name'], ENT_XML1, 'UTF-8')); + + $address = $listing->addChild('address'); + $address->addAttribute('format', 'simple'); + + $component = $address->addChild('component', htmlspecialchars(fillOnUndefined($metaAddress, 'street'), ENT_XML1, 'UTF-8')); + $component->addAttribute('name', 'addr1'); + + //$component = $address->addChild('component'); + //$component->addAttribute('name', 'addr2'); + + $component = $address->addChild('component', fillOnUndefined($metaAddress, 'city')); + $component->addAttribute('name', 'city'); + + //$component = $address->addChild('component', $propertyMapping['property']['country']['name']); + //$component->addAttribute('name','region'); + + $component = $address->addChild('component', fillOnUndefined($metaAddress, 'zip_code')); + $component->addAttribute('name', 'postal_code'); + + $listing->addChild('country', $propertyMapping['property']['country']['country_code']); + $listing->addChild('latitude', $propertyMapping['property']['property_contact']['latitude']); + $listing->addChild('longitude', $propertyMapping['property']['property_contact']['longitude']); + $listing->addChild('category', 'hotel'); + + } + + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + header('Content-Type: application/xml; charset=UTF-8'); + echo $listings->asXML(); + exit; + + + } + + public function availability(Request $request) + { + + $response = ['status' => true, 'message' => '']; + + $checkAuthentication = $this->checkAuthentication($this->authorization); + if (!$checkAuthentication['status']) { + return $this->responseError($checkAuthentication['message']); + } + + $searchController = App::make("App\Http\Controllers\BookingEngine\V1\SearchController"); + + $requestTime = microtime(true); + + + $param = $this->request->getContent(); + $paramXML = simplexml_load_string($param); + $requestParam = json_decode(json_encode($paramXML), 1); + + $hotelIdList = collect($requestParam['PropertyList']['Property'])->values(); + $hotelIdList = $hotelIdList ? $hotelIdList->toArray() : []; + + $transaction = new \SimpleXMLElement(''); + + try { + + + //Hotels Check + $channelManagerPropertyList = []; + $channelManagerPropertyMappingCriteria['criteria'] = [ + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ]; + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + $channelManagerPropertyList = pickItemFromArray('property_id', $channelManagerPropertyMapping['data']); + } + + $availableHotelIds = array_intersect($hotelIdList, $channelManagerPropertyList); + + if (empty($availableHotelIds)) { + throw new ApiErrorException('Hotel not found'); + } + //Hotels Check + + $searchRequestJson = $this->requestParamConverter($requestParam); + + + $searchRequestJson['isMobile'] = null; + $searchRequestJson['noneCacheSearch'] = true; + $searchRequestJson['ipAddress'] = $request->getClientIp(); + $searchRequestJson['justPriceValues'] = true; + $searchRequestJson['locale'] = fillOnUndefined($requestParam, 'Locale') ?? 'tr'; + + $roomOccupancy = $this->roomOccupancy($searchRequestJson['rooms']); + $roomOccupancy = implode(',', $roomOccupancy); + + + $requestCurrency = fillOnUndefined($requestParam, 'Currency') ?? 'TRY'; + + + $cacheKeyParam = $requestCurrency . ':' . $searchRequestJson['date']['checkIn'] . ':' . $searchRequestJson['date']['checkOut'] . ':' . implode(',', $searchRequestJson['property']) . ':' . $roomOccupancy; + $cacheKey = 'YND:' . md5($cacheKeyParam); + + //Cache::forget($cacheKey); + + if (Cache::has($cacheKey)) { + $search = Cache::get($cacheKey); + } else { + $requestCreate = Request::create(null, null, [], [], [], [], json_encode($searchRequestJson)); + $requestCreate->headers->set('channelId', '1'); + //$requestCreate->headers->set('bookingEnginePropertyId', null); + $requestCreate->headers->set('channelToken', 'ece3ef02-42e7-92b7-f379-08226ed7a0f3');//TODO: from DB + $requestCreate->headers->set('bookingEngineToken', null); + $search = $searchController->search($requestCreate); + + Cache::put($cacheKey, $search, 60 * 60);//1 Day 24 * 60 * 60 + } + + $search = json_decode(json_encode($search), 1); + + if ($search['original']['status'] != 200) { + throw new ApiErrorException('Hotel not found'); + } + + $properties = $search['original']['data']['properties']; + + if (empty($properties)) { + throw new ApiErrorException('Hotel not found'); + } + + $hotelCounter = 0; + $numberOfStay = Carbon::parse($searchRequestJson['date']['checkIn'])->diffInDays(Carbon::parse($searchRequestJson['date']['checkOut'])); + + foreach ($properties as $propertyId => $property) { + + $propertyName = $property['name']; + + if (!isset($property['currency'])) { + continue; + } + + $currencyExchangeRate = 1; + if ($requestCurrency != $property['currency']) { + $currencyExchangeRate = $this->currencyService->lastExchangeRate($property['currency'], $requestCurrency); + if ($currencyExchangeRate['status'] == 'success') { + $currencyExchangeRate = $currencyExchangeRate['data']; + } + } + + $token = $property['booking_engine_token']; + $deepLinkGenerator = $this->deepLinkGenerator($token, $searchRequestJson); + + + $property = reset($property['availabilities']); + $propertyRooms = $property['rooms']; + + //Standard Room Room Only FreeCancellation - Pay at Hotel + + + $requestedRoomPriceGroup = []; + foreach ($propertyRooms as $propertyRoomId => $propertyRoom) { + + foreach ($propertyRoom['rates'] as $propertyRoomRateId => $propertyRoomRate) { + + foreach ($propertyRoomRate['requestedRoomPrice'] as $occupancyCode => $requestedRoomPrices) { + + foreach ($requestedRoomPrices['prices'] as $requestedRoomPriceKey => $requestedRoomPrice) { + + $roomRateKeyName = $occupancyCode . ' ' . $requestedRoomPrice['room']['name'] . ' ' . $requestedRoomPrice['rate']['boardName'] . ' ' . $requestedRoomPrice['name']; + $roomRateKeycode = $requestedRoomPrice['rate']['accommodationCode'] . '|' . (isset($requestedRoomPrice['cancellationPolicy']['id']) ? $requestedRoomPrice['cancellationPolicy']['id'] : 0) . '|' . $requestedRoomPrice['bookingPaymentType']['code']; + + //$requestedRoomPriceGroup[$roomRateKeycode][$occupancyCode][] = $roomRateKeyName . '-' . $requestedRoomPrice['rateKeyCode']; + $requestedRoomPrice['dailyAverageDiscount'] = $requestedRoomPrices['dailyAverageDiscount']; + $requestedRoomPriceGroup[] = $requestedRoomPrice; + + } + + } + + } + } + + if (!empty($requestedRoomPriceGroup)) { + $requestedRoomPriceGroup = collect($requestedRoomPriceGroup)->sortBy('total')->toArray(); + } + + $requestedRoomPrice = reset($requestedRoomPriceGroup); + + + $propertyCouponCodeCheck = $this->couponCodeCheck($propertyId, $searchRequestJson['date']['checkIn'], $searchRequestJson['date']['checkOut']); + if ($propertyCouponCodeCheck) { + + $requestedRoomPrice['totalWithoutPopup'] = $requestedRoomPrice['total']; + + $couponPercentageValue = (100 - $propertyCouponCodeCheck['value']) / 100; + $requestedRoomPrice['total'] = moneyDoubleFormatDecimal($requestedRoomPrice['total'] * $couponPercentageValue); + + } + + + $transactionResult = $transaction->addChild('Result'); + + $transactionResult->addChild('Property', $propertyId); + $transactionResult->addChild('Checkin', $searchRequestJson['date']['checkIn']); + $transactionResult->addChild('Nights', $numberOfStay); + + + $taxValue = $requestedRoomPrice['total'] * ($this->tax / 100); + $baseTotalValue = $requestedRoomPrice['total'] - $taxValue; + $netTotalValue = ($taxValue + $baseTotalValue); + + $baseRate = $transactionResult->addChild('Baserate', moneyDoubleFormatDecimal($baseTotalValue * $currencyExchangeRate)); + $baseRate->addAttribute('currency', $requestCurrency); + + $tax = $transactionResult->addChild('Tax', moneyDoubleFormatDecimal($taxValue * $currencyExchangeRate)); + $tax->addAttribute('currency', $requestCurrency); + + $otherFees = $transactionResult->addChild('OtherFees', 0); + $otherFees->addAttribute('currency', $requestCurrency); + + $transactionResult->addChild('Custom1', htmlspecialchars($propertyName, ENT_XML1, 'UTF-8')); + + $allowablePointsOfSale = $transactionResult->addChild('AllowablePointsOfSale'); + $pointOfSale = $allowablePointsOfSale->addChild('PointOfSale'); + $pointOfSale->addAttribute('id', 'default'); + + $pointOfSale->addChild('URL', htmlspecialchars($deepLinkGenerator, ENT_XML1, 'UTF-8')); + + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + header('Content-Type: application/xml; charset=UTF-8'); + echo $transaction->asXML(); + exit; + + + } + + +} diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php new file mode 100644 index 0000000..9463ac3 --- /dev/null +++ b/app/Http/Controllers/PaymentController.php @@ -0,0 +1,257 @@ +propertyPaymentService = $propertyPaymentService; + $this->mailer = $mailer; + } + + public function initializePayment(Request $request) + { + + /* + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($this->request->getContent(), 1); + + + dd($params); + + + $responseData = [ + 'bookingCode' => $bookingCode, + 'total' => $totalRoomsPrice, + 'currency' => $currencyCode + ]; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + */ + + + } + + public function paymentRedirect(Request $request, $paymentCode) + { + + $response = ['status' => false, 'message' => '']; + + try { + + $paymentTransaction = $this->propertyPaymentService->getPaymentTransactionDetail($paymentCode); + + if (!$paymentTransaction['status']) { + throw new ApiErrorException($paymentTransaction['message']); + } + + $paymentTransaction = $paymentTransaction['data']; + + if ($paymentTransaction['status'] == 1) { + return redirect()->to($paymentTransaction['paramsArray']['responseUrl']); + } + + //Set Redirect Status + $this->propertyPaymentService->updatePaymentTransaction($paymentTransaction['id'], ['status' => 3]); + + if ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'POS') { + + + //TODO: Burada PAN replace edilecek + //$this->propertyPaymentService->updatePaymentTransaction($paymentTransaction['id'], ['status' => 3]); + + $formData = $paymentTransaction['extraParamsArray']; + return view('threeDSecureForm', compact('formData')); + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'STR') { + + if (isset($paymentTransaction['extraParamsArray']['redirect']['url'])) { + return redirect()->to($paymentTransaction['extraParamsArray']['redirect']['url']); + } + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'ENW') { + + if (isset($paymentTransaction['extraParamsArray']['redirect']['url'])) { + return redirect()->to($paymentTransaction['extraParamsArray']['redirect']['url']); + } + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'SPY') { + + $formData = $paymentTransaction['extraParamsArray']; + return view('threeDSecureForm', compact('formData')); + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'MOK') { + + if (isset($paymentTransaction['extraParamsArray'])) { + return redirect()->to($paymentTransaction['extraParamsArray']); + } + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'BOG') { + + $redirectUrl = collect($paymentTransaction['extraParamsArray']['links'])->where('method', 'REDIRECT')->first(); + + if (isset($redirectUrl['href'])) { + return redirect()->to($redirectUrl['href']); + } + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'TBC') { + + $redirectUrl = collect($paymentTransaction['extraParamsArray']['links'])->where('method', 'REDIRECT')->first(); + + if (isset($redirectUrl['uri'])) { + return redirect()->to($redirectUrl['uri']); + } + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'QNB') { + + $formData = $paymentTransaction['extraParamsArray']; + return view('threeDSecureForm', compact('formData')); + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'WEE') { + + if (isset($paymentTransaction['extraParamsArray']['threeDSecureUrl'])) { + return redirect()->to($paymentTransaction['extraParamsArray']['threeDSecureUrl']); + } + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'HLK') { + + $formData = $paymentTransaction['extraParamsArray']; + return view('threeDSecureForm', compact('formData')); + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'ESN') { + + if (isset($paymentTransaction['extraParamsArray']['URL_3DS'])) { + return redirect()->to($paymentTransaction['extraParamsArray']['URL_3DS']); + } + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'KVY') { + + $formData = $paymentTransaction['extra_params']; + echo $formData; die(); + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'RTL') { + + $extraParams = json_decode($paymentTransaction['extra_params'],1); + echo $extraParams['form3d_html']; die(); + + } elseif ($paymentTransaction['payment_type_mapping']['payment_type']['pos_code'] == 'PYR') { + + $redirectUrl = $paymentTransaction['extraParamsArray']['payload']; + + if (isset($redirectUrl['paymentUrl'])) { + return redirect()->to($redirectUrl['paymentUrl']); + } + + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + + //TODO: Burada bir yere yönlendirilmeli + dd($response); + + + } + + public function paymentCheck(Request $request, $paymentCode) + { + + $responseData = ['status' => false]; + + try { + + $checkPayment = $this->propertyPaymentService->checkPayment($paymentCode, $request->all()); + + $responseData['paymentCode'] = $checkPayment['data']['paymentCode']; + + if ($checkPayment['status']) { + $responseData['status'] = true; + $responseData['bankOrderId'] = $checkPayment['data']['bankOrderId']; + } else { + $responseData['message'] = $checkPayment['message']; + } + + if (isset($checkPayment['data']['paymentTransactionDetail']['property'])) { + $responseData['paymentTransactionDetail']['amount'] = $checkPayment['data']['paymentTransactionDetail']['amount']; + $responseData['paymentTransactionDetail']['currency'] = $checkPayment['data']['paymentTransactionDetail']['currency']; + $responseData['paymentTransactionDetail']['message'] = $checkPayment['data']['paymentTransactionDetail']['message']; + $responseData['property'] = $checkPayment['data']['paymentTransactionDetail']['property']; + } + + //$logMessage + $mailParams = [ + 'title' => 'PaymentCheck', + 'logMessage' => '
' . print_r($responseData, true) . '
' + ]; + + $this->mailer->onQueue( + 'logMail', + new LogMail($mailParams) + ); + //$logMessage + + } catch (ApiErrorException $e) { + $responseData['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $responseData['message'] = $e->getMessage(); + } + + return redirect()->to($checkPayment['data']['responseUrl']); + + + } + + +} diff --git a/app/Http/Controllers/TestController.php b/app/Http/Controllers/TestController.php new file mode 100644 index 0000000..779bbb3 --- /dev/null +++ b/app/Http/Controllers/TestController.php @@ -0,0 +1,491 @@ +exampleValidator = $exampleValidator; + $this->propertyFactAttributeRepository = $propertyFactAttributeRepository; + $this->propertyUnitRepository = $propertyUnitRepository; + + $this->propertyPaymentService = $propertyPaymentService; + $this->newBookingMailService = $newBookingMailService; + $this->mailer = $mailer; + + $this->notificationService = $notificationService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + + $this->manualPaymentMailService = $manualPaymentMailService; + } + + + public function testResponse(Request $request) + { + + $inputAll = $request->input(); + + dd($inputAll); + + Log::debug(json_encode($inputAll)); + + $response = [ + 'success' => true + ]; + + return response()->json($response); + + dd($inputAll); + + + $publishableKey = 'pk_test_51HuYHvEa9cmPdLq3cIxkIJ2y6EtTL1mSuPKKbtSJuMemCaLW49h8lPhVINB0ju8MvYDwM45cxk1oFpz8EoMpVpum00Rw3qKohb'; + $secretKey = 'sk_test_51HuYHvEa9cmPdLq3ANG7ZYaGB9zMuhQZlwH19axJRauZsMnnpnuGBN1h8iAfr9kNVWe4FWcEcvZiMjn3hhBELHHx00hiBgjO41'; + + try { + + //Secret key + $stripe = new \Stripe\StripeClient($secretKey); + + + $charge = $stripe->charges->create([ + 'currency' => 'EUR', + 'amount' => 120,//10.20 + 'source' => $inputAll['source'] + ]); + + dd($inputAll, $charge); + + if ($charge['status'] == 'succeeded') { + + } + + } catch (\Exception $e) { + dd($e->getMessage()); + } + + dd($inputAll); + + + $account = [ + 'bank' => 'akbank', + 'model' => '3d_pay', + 'client_id' => '100200000', + 'store_key' => '123456', + 'env' => 'test', // test veya production. test ise; API Test Url, production ise; API Production URL kullanılır. + ]; + + + $pos = new \App\Core\Payment\Pos\Pos($account); + + $orderParams = Cache::get($request->input('oid')); + + + $pos->prepare($orderParams); + $payment = $pos->payment(); + + dd($payment, $payment->response, $payment->response->status, $payment->response->code); + + $response = $payment->response; + + dd($response, $request->input()); + + + } + + public function test(Request $request) + { + + $inputAll = $request->input(); + + + try { + + + /*$path = resource_path('data/data.json'); + $json = file_get_contents($path); + $summaryReportData = json_decode($json, true); + + $this->mailer->onQueue('dailyReportMail', new DailyReportMail($summaryReportData)); + + die();*/ + + //$mailParams = ['booking_id' => 72027]; + //$this->newBookingMailService->process($mailParams); + + die(); + + $initializePaymentParam = [ + 'propertyId' => 1, + 'orderId' => getCodeGenerate(), + 'installment' => 0, + 'amount' => (double)1.25, + 'currency' => 'AZN', + 'responseUrl' => 'http://api.extranetwork.local/testResponse', + 'preferredPaymentTypeId' => 606, + 'ipAddress' => $request->getClientIp(), + /*'creditCard' => [ + 'name' => 'Test Card', + 'number' => '4000007546012078', + 'month' => '04', + 'year' => '2028', + 'cvv' => '236', + ]*/ + ]; + + //dd($initializePaymentParam); + + //4938460158754205 2024/11 715 123456 + //4119790155203496 2024/04 579 + + $initializePayment = $this->propertyPaymentService->initializePayment($initializePaymentParam); + + dd($initializePayment); + + die(); + + $param = [ + 'property_id' => 1805, + 'subject' => '🛎️ ' . __('notification-new_booking', [], 'en'), + 'message' => __('notification-new_booking_desc', ['hotel_name' => 'Seres Hotel', 'amount' => 198.45, 'currency' => 'EUR'], 'en'), + ]; + + Notification::route('OneSignal', null)->notify(new PushNotificationPropertyUser($param)); + + die(); + + $mailParams = ['booking_id' => 71740]; + $this->newBookingMailService->process($mailParams); + + + $propertyProductOfferMail = [ + 'offerKey' => '6c0d6ee7-c378-4859-9603-7967ede0e120' + ]; + + $this->mailer->onQueue('propertyProductOfferMail', new PropertyProductOfferMail($propertyProductOfferMail)); + + + // + + + //$newBookingNotificationParam = ['booking_id' => 63012]; + //$this->notificationService->sendNewBookingNotification($newBookingNotificationParam); + //PUSH NOTIFICATION + + die(); + + + /*$report = json_decode('{"name":"Cemile Ays\u0131n ARIKAN","email":"cemile@extranetwork.com","daily":{"data":{"1258":{"id":1258,"name":"Rayelin Hotel Istanbul Old City","count":3,"total":1504.2399999999998,"commission":225.63599999999997},"580":{"id":580,"name":"\u00d6zkaymak Falez Hotel","count":1,"total":1054.7,"commission":158.205},"1007":{"id":1007,"name":"T\u00fcrkay Hotel","count":1,"total":226.59912,"commission":27.191894400000002}},"title":"Daily Report","type":"daily","period":"20.01.2025","summary":{"count":5,"total":2785.5391199999995,"commission":411.03289440000003}},"monthly":{"data":{"1258":{"id":1258,"name":"Rayelin Hotel Istanbul Old City","count":13,"total":8061.37,"commission":1209.2054999999998},"580":{"id":580,"name":"\u00d6zkaymak Falez Hotel","count":6,"total":3558.55,"commission":533.7825},"1007":{"id":1007,"name":"T\u00fcrkay Hotel","count":3,"total":1301.88312,"commission":156.2259744},"1205":{"id":1205,"name":"Duck Otel","count":7,"total":386.2046399999999,"commission":57.93069599999999}},"title":"Monthly Report","type":"monthly","period":"01.2025","summary":{"count":29,"total":13308.00776,"commission":1957.1446703999998}},"annually":{"data":{"1258":{"id":1258,"name":"Rayelin Hotel Istanbul Old City","count":13,"total":8061.37,"commission":1209.2054999999998},"580":{"id":580,"name":"\u00d6zkaymak Falez Hotel","count":6,"total":3558.55,"commission":533.7825},"1007":{"id":1007,"name":"T\u00fcrkay Hotel","count":3,"total":1301.88312,"commission":156.2259744},"1205":{"id":1205,"name":"Duck Otel","count":7,"total":386.2046399999999,"commission":57.93069599999999}},"title":"Annually Report","type":"annually","period":"2025","summary":{"count":29,"total":13308.00776,"commission":1957.1446703999998}},"activeProperty":[{"id":1258,"name":"Rayelin Hotel Istanbul Old City","commission":15,"year":"2024","month":"2024-12","contract_user_id":22},{"id":1205,"name":"Duck Otel","commission":15,"year":"2024","month":"2024-08","contract_user_id":22},{"id":1007,"name":"T\u00fcrkay Hotel","commission":12,"year":"2024","month":"2024-02","contract_user_id":22},{"id":580,"name":"\u00d6zkaymak Falez Hotel","commission":15,"year":"2022","month":"2022-11","contract_user_id":22}]}',1); + $this->mailer->onQueue('dailyReportSalesMail', new DailyReportMailSales($report)); + + die();*/ + + + } catch (Exception $e) { + dd($e->getMessage()); + Log::debug($e->getMessage()); + } + + + die(); + + //$summaryReportData = json_decode('{"daily":{"data":{"EUR":{"count":9,"total":2080.9900000000002,"commission":240.5208},"GEL":{"count":4,"total":958.6600000000001,"commission":24.5828},"TRY":{"count":1,"total":27000,"commission":4050}},"title":"Daily Report","period":"11.12.2024","summary":{"count":14,"total":3140.391492,"commission":359.35053736000003}},"monthly":{"data":{"EUR":{"count":98,"total":32560.43,"commission":3690.2600999999995},"GEL":{"count":81,"total":39810.82000000001,"commission":1132.786},"TRY":{"count":24,"total":151606.96,"commission":21772.044}},"title":"Monthly Report","period":"12.2024","summary":{"count":203,"total":50083.697692,"commission":4665.479554399999},"summaryCheckoutData":{"EUR":{"count":202,"total":59142.93000000001,"commission":5871.0608},"GEL":{"count":101,"total":41661.039999999986,"commission":1358.6717999999996},"TRY":{"count":31,"total":257321.56,"commission":38041.734000000004},"USD":{"count":2,"total":120,"commission":14.4}},"summaryCheckout":{"count":336,"total":80288.454236,"commission":7380.090077360001}},"annually":{"data":{"EUR":{"count":3711,"total":1805925.850000003,"commission":197665.67849999937},"GEL":{"count":1841,"total":1037083.2200000021,"commission":30233.390399999997},"TRY":{"count":335,"total":2973705.5399999996,"commission":387296.1086999998},"USD":{"count":37,"total":5649.07,"commission":677.8884000000002},"GBP":{"count":20,"total":18908.2,"commission":2268.9840000000004}},"title":"Annually Report","period":"2024","summary":{"count":5944,"total":2264081.583865003,"commission":221800.07140706936}},"activeProperty":{"count":145,"groupByMonth":{"2024-12":[{"id":1310,"name":"Hope Sapanca Tiny House","commission":15,"year":"2024","month":"2024-12"},{"id":1216,"name":"Hub Inn Pera ","commission":15,"year":"2024","month":"2024-12"},{"id":1224,"name":"Aria Claros Beach & Spa Resort","commission":15,"year":"2024","month":"2024-12"},{"id":1258,"name":"Rayelin Hotel Istanbul Old City","commission":15,"year":"2024","month":"2024-12"},{"id":1218,"name":"StayHub","commission":15,"year":"2024","month":"2024-12"},{"id":1242,"name":"Tusan Beach Resort","commission":15,"year":"2024","month":"2024-12"},{"id":1304,"name":"Litros Hotel & Spa","commission":15,"year":"2024","month":"2024-12"},{"id":1308,"name":"Rayelin Hotel Old Town","commission":15,"year":"2024","month":"2024-12"}],"2024-11":[{"id":1281,"name":"Hotel Atina Budva","commission":15,"year":"2024","month":"2024-11"},{"id":1277,"name":"Villa La Barba","commission":15,"year":"2024","month":"2024-11"},{"id":1275,"name":"S\u00f6\u011f\u00fct Hotel Old City","commission":15,"year":"2024","month":"2024-11"},{"id":1273,"name":"The Riada Hotel","commission":15,"year":"2024","month":"2024-11"},{"id":1269,"name":"Hotel Del Lago Luxury by Sara\u00e7o\u011flu","commission":15,"year":"2024","month":"2024-11"},{"id":1293,"name":"Blue Ottoman Hotel","commission":15,"year":"2024","month":"2024-11"},{"id":1303,"name":"Zelve Hotel","commission":15,"year":"2024","month":"2024-11"},{"id":1276,"name":"Grand S Hotel","commission":15,"year":"2024","month":"2024-11"}],"2024-10":[{"id":1244,"name":"B Hotel Bishkek","commission":15,"year":"2024","month":"2024-10"},{"id":1247,"name":"Keten Suites Taksim","commission":15,"year":"2024","month":"2024-10"},{"id":1246,"name":"Ankara Santral Otel","commission":15,"year":"2024","month":"2024-10"},{"id":1253,"name":"Kazda\u011flar\u0131 Allia Thermal Health & Spa","commission":12,"year":"2024","month":"2024-10"},{"id":1249,"name":"Cartoon Hotel","commission":15,"year":"2024","month":"2024-10"},{"id":1267,"name":"Ottoman Hotel Sakarya","commission":15,"year":"2024","month":"2024-10"},{"id":1260,"name":"Monark Hotel Cappadocia","commission":12,"year":"2024","month":"2024-10"}],"2024-09":[{"id":1215,"name":"Hub Suite \u0130stanbul","commission":15,"year":"2024","month":"2024-09"},{"id":1229,"name":"Qlus\u0131ve Hotel","commission":15,"year":"2024","month":"2024-09"},{"id":1231,"name":"Credo Hotel Kotor","commission":15,"year":"2024","month":"2024-09"}],"2024-08":[{"id":1208,"name":"Vicolo Otel","commission":15,"year":"2024","month":"2024-08"},{"id":1205,"name":"Duck Otel","commission":15,"year":"2024","month":"2024-08"},{"id":1203,"name":"Expo Park Hotel","commission":15,"year":"2024","month":"2024-08"},{"id":1200,"name":"Sierra Cave Cappadocia Hotel","commission":15,"year":"2024","month":"2024-08"},{"id":1196,"name":"Lubberona Cave Cappadocia","commission":15,"year":"2024","month":"2024-08"},{"id":1184,"name":"Elephant In The Room Hotel","commission":15,"year":"2024","month":"2024-08"}],"2024-07":[{"id":1166,"name":"Alice Hotel Antalya","commission":15,"year":"2024","month":"2024-07"},{"id":1180,"name":"Keremk\u00f6y Ya\u015fam Platosu","commission":10,"year":"2024","month":"2024-07"},{"id":1170,"name":"Hotel Berke Ranch & Nature","commission":15,"year":"2024","month":"2024-07"}],"2024-06":[{"id":1150,"name":"Konyaalt\u0131 Furkan Family","commission":15,"year":"2024","month":"2024-06"}],"2024-05":[{"id":1125,"name":"Petra Hotel Trabzon","commission":15,"year":"2024","month":"2024-05"}],"2024-04":[{"id":1103,"name":"New Wave 2 Apart-Hotel","commission":12,"year":"2024","month":"2024-04"},{"id":1102,"name":"Cappadocia Villa Comfort Hotel","commission":15,"year":"2024","month":"2024-04"},{"id":1098,"name":"Batumi Palm Hotel","commission":12,"year":"2024","month":"2024-04"},{"id":1091,"name":"Nehir Suit Hotel","commission":15,"year":"2024","month":"2024-04"},{"id":1090,"name":"Mera Park Otel","commission":15,"year":"2024","month":"2024-04"},{"id":1082,"name":"Very Peri Cappadocia ","commission":12,"year":"2024","month":"2024-04"},{"id":1081,"name":"La Vie Cappadocia Cave Hotel","commission":12,"year":"2024","month":"2024-04"}],"2024-03":[{"id":1048,"name":"Side Crown Serenity","commission":15,"year":"2024","month":"2024-03"},{"id":1047,"name":"Side Crown Palace","commission":15,"year":"2024","month":"2024-03"},{"id":1076,"name":"Alia Cave Hotel","commission":9,"year":"2024","month":"2024-03"},{"id":1073,"name":"Mithra Cave Hotel","commission":9,"year":"2024","month":"2024-03"},{"id":1075,"name":"Misty Cave Hotel","commission":9,"year":"2024","month":"2024-03"}],"2024-02":[{"id":1020,"name":"Gamirasu Junior","commission":15,"year":"2024","month":"2024-02"},{"id":1007,"name":"T\u00fcrkay Hotel","commission":12,"year":"2024","month":"2024-02"},{"id":1008,"name":"Tamara Business Antalya","commission":12,"year":"2024","month":"2024-02"},{"id":1010,"name":"Tamara Business Hotel Van","commission":12,"year":"2024","month":"2024-02"},{"id":1011,"name":"Utopia Cave Cappadocia","commission":15,"year":"2024","month":"2024-02"},{"id":1025,"name":"Hotel Monarch Batumi","commission":12,"year":"2024","month":"2024-02"},{"id":1031,"name":"La Perla Cave Cappadocia","commission":15,"year":"2024","month":"2024-02"},{"id":1024,"name":"Hotel Mandalin","commission":15,"year":"2024","month":"2024-02"},{"id":1033,"name":"Saliche Cave Suits","commission":15,"year":"2024","month":"2024-02"},{"id":1034,"name":"Cappadocia Hobbit House","commission":15,"year":"2024","month":"2024-02"},{"id":1035,"name":"Hotel Salvador","commission":12,"year":"2024","month":"2024-02"}],"2024-01":[{"id":1006,"name":"Can Adalya Palace Hotel","commission":12,"year":"2024","month":"2024-01"},{"id":989,"name":"Tantan Cappadocia House","commission":15,"year":"2024","month":"2024-01"},{"id":984,"name":"Cappanar Cave","commission":15,"year":"2024","month":"2024-01"},{"id":983,"name":"Yunak Evleri","commission":9,"year":"2024","month":"2024-01"},{"id":982,"name":"Enjoy Stone House","commission":12,"year":"2024","month":"2024-01"},{"id":981,"name":"Sabiha Sultan Hotel","commission":12,"year":"2024","month":"2024-01"}],"2023-12":[{"id":955,"name":"My Adress Ala\u00e7at\u0131 Otel","commission":15,"year":"2023","month":"2023-12"},{"id":966,"name":"Arya Apart Kundu","commission":15,"year":"2023","month":"2023-12"},{"id":961,"name":"Lorem Hotel","commission":15,"year":"2023","month":"2023-12"},{"id":956,"name":"Alt\u0131noluk Lambada Otel","commission":15,"year":"2023","month":"2023-12"}],"2023-11":[{"id":934,"name":"Transatlantik Hotel & SPA","commission":15,"year":"2023","month":"2023-11"},{"id":944,"name":"Transatlantik Beach Hotel","commission":15,"year":"2023","month":"2023-11"},{"id":928,"name":"Daima Biz Hotel","commission":12,"year":"2023","month":"2023-11"}],"2023-10":[{"id":911,"name":"All Seasons Suites","commission":10,"year":"2023","month":"2023-10"},{"id":909,"name":"All Seasons Hotel","commission":10,"year":"2023","month":"2023-10"},{"id":907,"name":"Grand Hotel G\u00fclsoy","commission":15,"year":"2023","month":"2023-10"},{"id":901,"name":"Emerald Hotel","commission":12,"year":"2023","month":"2023-10"},{"id":912,"name":"Grand Ons Hotel","commission":15,"year":"2023","month":"2023-10"}],"2023-09":[{"id":872,"name":"La Rezidans Hotel","commission":15,"year":"2023","month":"2023-09"}],"2023-08":[{"id":848,"name":"Green Glass Hotel","commission":12,"year":"2023","month":"2023-08"}],"2023-07":[{"id":808,"name":"Nok \u0130stanbul Suites","commission":15,"year":"2023","month":"2023-07"}],"2023-06":[{"id":772,"name":"Cronton Design Hotel","commission":15,"year":"2023","month":"2023-06"},{"id":790,"name":"New Wave Hotel ","commission":12,"year":"2023","month":"2023-06"}],"2023-05":[{"id":738,"name":"VONRESORT Abant","commission":15,"year":"2023","month":"2023-05"},{"id":729,"name":"\u00d6zkaymak Select Resort Hotel","commission":15,"year":"2023","month":"2023-05"},{"id":728,"name":"Lara Din\u00e7 Hotel","commission":15,"year":"2023","month":"2023-05"},{"id":743,"name":"Hotel Luna Antalya","commission":12,"year":"2023","month":"2023-05"},{"id":755,"name":"Hotel Chao","commission":12,"year":"2023","month":"2023-05"},{"id":759,"name":"The Nest Hotel","commission":15,"year":"2023","month":"2023-05"}],"2023-04":[{"id":692,"name":"Orka Royal Hotel & Spa","commission":12,"year":"2023","month":"2023-04"},{"id":696,"name":"Mielo Lara Hotel","commission":15,"year":"2023","month":"2023-04"},{"id":693,"name":"Orient Express & Spa by Orka Hotels","commission":12,"year":"2023","month":"2023-04"},{"id":694,"name":"Pianoforte by Febor Hotels&Spa","commission":12,"year":"2023","month":"2023-04"},{"id":695,"name":"Orka Taksim Suites & Hotel","commission":12,"year":"2023","month":"2023-04"},{"id":726,"name":"\u00d6zkaymak Otem Hotel","commission":15,"year":"2023","month":"2023-04"},{"id":698,"name":"Febor Park Hotel","commission":12,"year":"2023","month":"2023-04"},{"id":699,"name":"Lara Kapris Otel","commission":15,"year":"2023","month":"2023-04"},{"id":712,"name":"G Hotels Skopje","commission":15,"year":"2023","month":"2023-04"},{"id":721,"name":"\u00d6zkaymak \u0130ncekum Hotel","commission":15,"year":"2023","month":"2023-04"},{"id":724,"name":"\u00d6zkaymak Marina Hotel","commission":15,"year":"2023","month":"2023-04"}],"2023-03":[{"id":684,"name":"Alfa Cave Hotel","commission":15,"year":"2023","month":"2023-03"},{"id":681,"name":"The Calypso Cave","commission":15,"year":"2023","month":"2023-03"}],"2023-02":[{"id":665,"name":"Endless Flats","commission":12,"year":"2023","month":"2023-02"},{"id":671,"name":"Bilgehan Hotel","commission":9,"year":"2023","month":"2023-02"},{"id":668,"name":"Sim Hotel","commission":12,"year":"2023","month":"2023-02"}],"2023-01":[{"id":629,"name":"Endless Comfort Hotel","commission":10,"year":"2023","month":"2023-01"},{"id":628,"name":"Endless Suites Taksim","commission":10,"year":"2023","month":"2023-01"},{"id":644,"name":"VONRESORT Golden Coast","commission":15,"year":"2023","month":"2023-01"},{"id":643,"name":"VONRESORT Elite","commission":15,"year":"2023","month":"2023-01"},{"id":645,"name":"VONRESORT Golden Beach","commission":15,"year":"2023","month":"2023-01"}],"2022-12":[{"id":623,"name":"WOW Airport Hotel","commission":15,"year":"2022","month":"2022-12"},{"id":605,"name":"Oksijen Zone Hotel & Spa","commission":15,"year":"2022","month":"2022-12"},{"id":591,"name":"WOW \u0130stanbul Hotel","commission":15,"year":"2022","month":"2022-12"}],"2022-11":[{"id":580,"name":"\u00d6zkaymak Falez Hotel","commission":15,"year":"2022","month":"2022-11"},{"id":529,"name":"Green Nature Resort & Spa Otel","commission":12,"year":"2022","month":"2022-11"}],"2022-10":[{"id":506,"name":"Green Nature Diamond Hotel","commission":12,"year":"2022","month":"2022-10"},{"id":503,"name":"Elegance Hotels International","commission":12,"year":"2022","month":"2022-10"}],"2022-08":[{"id":450,"name":"Bilem Hotel Beach & Spa","commission":15,"year":"2022","month":"2022-08"}],"2022-07":[{"id":430,"name":"Invite Hotel Corner Trabzon","commission":15,"year":"2022","month":"2022-07"}],"2022-05":[{"id":400,"name":"Elanaz Hotel","commission":12,"year":"2022","month":"2022-05"}],"2022-04":[{"id":384,"name":"Albinas Hotel Old City","commission":12,"year":"2022","month":"2022-04"}],"2022-03":[{"id":366,"name":"Sealife Royal Suites","commission":10,"year":"2022","month":"2022-03"},{"id":368,"name":"Sultan Hostel & Guesthouse","commission":12,"year":"2022","month":"2022-03"}],"2022-02":[{"id":362,"name":"Bar\u0131n Hotel","commission":12,"year":"2022","month":"2022-02"}],"2022-01":[{"id":346,"name":"G\u00fcner Business Hotel","commission":12,"year":"2022","month":"2022-01"}],"2021-12":[{"id":317,"name":"Lalahan Hotel","commission":12,"year":"2021","month":"2021-12"},{"id":343,"name":"CitrusLuna Suite Hotel","commission":15,"year":"2021","month":"2021-12"},{"id":341,"name":"Harbiye Residence","commission":12,"year":"2021","month":"2021-12"},{"id":330,"name":"Radar Hotel","commission":12,"year":"2021","month":"2021-12"},{"id":326,"name":"Yavuz Otel","commission":12,"year":"2021","month":"2021-12"},{"id":319,"name":"\u0130lkay Hotel","commission":12,"year":"2021","month":"2021-12"},{"id":318,"name":"Hotellino","commission":12,"year":"2021","month":"2021-12"},{"id":316,"name":"Sirkeci Mansion Hotel","commission":9,"year":"2021","month":"2021-12"},{"id":314,"name":"Hotel \u015eahinler","commission":12,"year":"2021","month":"2021-12"},{"id":313,"name":"Levni Plus Hotel","commission":12,"year":"2021","month":"2021-12"}],"2021-10":[{"id":278,"name":"Gorrion Hotel Istanbul","commission":8,"year":"2021","month":"2021-10"}],"2021-08":[{"id":252,"name":"Tut Hotel","commission":12,"year":"2021","month":"2021-08"}],"2021-07":[{"id":249,"name":"Grand Ant Hotel","commission":12,"year":"2021","month":"2021-07"}],"2021-04":[{"id":236,"name":"Sealife Kemer Resort Hotel","commission":10,"year":"2021","month":"2021-04"}],"2020-11":[{"id":165,"name":"Sealife Lounge Hotel","commission":10,"year":"2020","month":"2020-11"},{"id":164,"name":"SeaLife Family Resort Hotel","commission":10,"year":"2020","month":"2020-11"},{"id":163,"name":"Porto Bello Hotel Resort & Spa","commission":10,"year":"2020","month":"2020-11"},{"id":166,"name":"Sealife Buket Beach & Resort","commission":10,"year":"2020","month":"2020-11"},{"id":168,"name":"Endless Art Hotel Special Category","commission":10,"year":"2020","month":"2020-11"}],"2020-08":[{"id":71,"name":"Grand Yavuz Hotel","commission":15,"year":"2020","month":"2020-08"}]},"lastMonth":{"2024-12":[{"id":1310,"name":"Hope Sapanca Tiny House","commission":15,"year":"2024","month":"2024-12"},{"id":1216,"name":"Hub Inn Pera ","commission":15,"year":"2024","month":"2024-12"},{"id":1224,"name":"Aria Claros Beach & Spa Resort","commission":15,"year":"2024","month":"2024-12"},{"id":1258,"name":"Rayelin Hotel Istanbul Old City","commission":15,"year":"2024","month":"2024-12"},{"id":1218,"name":"StayHub","commission":15,"year":"2024","month":"2024-12"},{"id":1242,"name":"Tusan Beach Resort","commission":15,"year":"2024","month":"2024-12"},{"id":1304,"name":"Litros Hotel & Spa","commission":15,"year":"2024","month":"2024-12"},{"id":1308,"name":"Rayelin Hotel Old Town","commission":15,"year":"2024","month":"2024-12"}],"2024-11":[{"id":1281,"name":"Hotel Atina Budva","commission":15,"year":"2024","month":"2024-11"},{"id":1277,"name":"Villa La Barba","commission":15,"year":"2024","month":"2024-11"},{"id":1275,"name":"S\u00f6\u011f\u00fct Hotel Old City","commission":15,"year":"2024","month":"2024-11"},{"id":1273,"name":"The Riada Hotel","commission":15,"year":"2024","month":"2024-11"},{"id":1269,"name":"Hotel Del Lago Luxury by Sara\u00e7o\u011flu","commission":15,"year":"2024","month":"2024-11"},{"id":1293,"name":"Blue Ottoman Hotel","commission":15,"year":"2024","month":"2024-11"},{"id":1303,"name":"Zelve Hotel","commission":15,"year":"2024","month":"2024-11"},{"id":1276,"name":"Grand S Hotel","commission":15,"year":"2024","month":"2024-11"}],"2024-10":[{"id":1244,"name":"B Hotel Bishkek","commission":15,"year":"2024","month":"2024-10"},{"id":1247,"name":"Keten Suites Taksim","commission":15,"year":"2024","month":"2024-10"},{"id":1246,"name":"Ankara Santral Otel","commission":15,"year":"2024","month":"2024-10"},{"id":1253,"name":"Kazda\u011flar\u0131 Allia Thermal Health & Spa","commission":12,"year":"2024","month":"2024-10"},{"id":1249,"name":"Cartoon Hotel","commission":15,"year":"2024","month":"2024-10"},{"id":1267,"name":"Ottoman Hotel Sakarya","commission":15,"year":"2024","month":"2024-10"},{"id":1260,"name":"Monark Hotel Cappadocia","commission":12,"year":"2024","month":"2024-10"}]}}}',1); + //$this->mailer->onQueue('dailyReportMail', new DailyReportMail($summaryReportData)); + + //dd($summaryReportData); + + + //Log::debug($inputAll); + + + /*$newBookingNotificationParam = ['property_id' => 1]; + $this->notificationService->sendLogNotification($newBookingNotificationParam);*/ + + + /*$bookingPropertyAddonUpdateMail = [ + 'bookingCode' => 'BKG240814-OYS9A8DG' + ]; + $this->mailer->onQueue('bookingInvoiceUpdateMail', new BookingInvoiceUpdateMail($bookingPropertyAddonUpdateMail));*/ + + + /*$paymentDetailParam = [ + 'orderCode' => 'LNK230913-ALAXEATT', + 'language_code' => isset($params['language_code']) ? $params['language_code'] : 'en' + ]; + $this->manualPaymentMailService->process($paymentDetailParam);*/ + + + //$mailParams = ['booking_id' => 44264]; + //$this->newBookingMailService->process($mailParams); + //$this->mailer->onQueue('modifiedBookingMail', new ModifiedBookingMail($mailParams)); + //$this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($mailParams)); + //dd('ds'); + + + //F5k8GvBkLQ22414131 + //Lucpc736tlh2414127 + //w9wAo95tftA2414111 - Success + + try { + + $initializePaymentParam = [ + 'propertyId' => 1, + 'orderId' => getCodeGenerate(), + 'installment' => 0, + 'amount' => (double)868.3199999999999, + 'currency' => 'TRY', + 'responseUrl' => 'http://api.extranetwork.local/testResponse', + 'preferredPaymentTypeId' => 325, + 'ipAddress' => $request->getClientIp(), + 'creditCard' => [ + 'name' => 'Burhan Yumak', + 'number' => '4938460158754205', + 'month' => '11', + 'year' => '2024', + 'cvv' => '715', + ] + ]; + + //4938460158754205 2024/11 715 123456 + //4119790155203496 2024/04 579 + + $initializePayment = $this->propertyPaymentService->initializePayment($initializePaymentParam); + + dd($initializePayment); + + + die(); + + //BookingPaymentDataCode + /*$mailParams = [ + 'user_id' => 1, + 'booking_id' => 978, + 'unlock_code' => 'KEOSXS', + //'locale' => 'en' + ]; + return new BookingPaymentDataCodeMail($mailParams); + die(); + $this->mailer->onQueue('bookingPaymentDataCode', new BookingPaymentDataCodeMail($mailParams));*/ + + + $paymentInfo = [ + "creditCard" => [ + "cardHolder" => "John Doe", + "cardNumber" => "5127541122223332", + "cardExpireMonth" => "12", + "cardExpireYear" => "2022", + "cardCvv" => "000", + "installment" => 0 + ] + ]; + + $cardNumber = $paymentInfo['creditCard']['cardNumber']; + $cardLengthSize = ceil(strlen($cardNumber) / 4); + + $cardNumberParse = []; + for ($i = 0; $i < $cardLengthSize; $i++) { + $cardNumberParse['cc'][] = Crypt::encrypt(mb_substr($cardNumber, $i * 4, 4)); + } + + $cardNumberParse['cm'] = Crypt::encrypt($paymentInfo['creditCard']['cardExpireMonth']); + $cardNumberParse['cy'] = Crypt::encrypt(mb_substr($paymentInfo['creditCard']['cardExpireYear'], -2, 2)); + $cardNumberParse['cv'] = Crypt::encrypt($paymentInfo['creditCard']['cardCvv']); + + dd($cardNumberParse, Crypt::decrypt($cardNumberParse['cm'])); + + dd(ceil(strlen($paymentInfo['creditCard']['cardNumber']) / 4)); + + $param = "3441"; + $x = Crypt::encrypt($param); + + dd($x, strlen($x), Crypt::decrypt($x)); + + die(); + $mailParams = ['booking_id' => 10117]; + $this->newBookingMailService->process($mailParams); + + die(); + + + $mailParams = []; + + $today = '2022-02-25'; + + + $report['daily']['data'] = []; + $report['daily']['title'] = 'Daily Report'; + $report['daily']['period'] = Carbon::parse($today)->format('d.m.Y'); + $daily = vwBookingSummary::where('time', '>', Carbon::parse($today)->toDateString()) + ->where('time', '<', Carbon::parse($today)->addDay()->toDateString()) + ->get()->toArray(); + + if ($daily) { + $dataCollect = collect($daily); + $dataCollectGroup = $dataCollect->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroup as $currencyCode => $currencyGroup) { + $report['daily']['data'][$currencyCode]['count'] = count($currencyGroup); + $report['daily']['data'][$currencyCode]['total'] = array_sum(pickItemFromArray('total', $currencyGroup)); + } + } + + + $report['monthly']['data'] = []; + $report['monthly']['title'] = 'Monthly Report'; + $report['monthly']['period'] = Carbon::parse($today)->firstOfMonth()->format('m.Y'); + $monthly = vwBookingSummary::where('time', '>', Carbon::parse($today)->firstOfMonth()->toDateString()) + ->where('time', '<', Carbon::parse($today)->addMonth()->firstOfMonth()->toDateString()) + ->get()->toArray(); + + if ($monthly) { + $dataCollect = collect($monthly); + $dataCollectGroup = $dataCollect->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroup as $currencyCode => $currencyGroup) { + $report['monthly']['data'][$currencyCode]['count'] = count($currencyGroup); + $report['monthly']['data'][$currencyCode]['total'] = array_sum(pickItemFromArray('total', $currencyGroup)); + } + } + + + $report['annually']['data'] = []; + $report['annually']['title'] = 'Annually Report'; + $report['annually']['period'] = Carbon::parse($today)->firstOfYear()->format('Y'); + $annually = vwBookingSummary::where('time', '>', Carbon::parse($today)->firstOfYear()->toDateString()) + ->where('time', '<', Carbon::parse($today)->addYear()->firstOfYear()->toDateString()) + ->get()->toArray(); + + if ($annually) { + $dataCollect = collect($annually); + $dataCollectGroup = $dataCollect->groupBy('currency_code')->toArray(); + foreach ($dataCollectGroup as $currencyCode => $currencyGroup) { + $report['annually']['data'][$currencyCode]['count'] = count($currencyGroup); + $report['annually']['data'][$currencyCode]['total'] = array_sum(pickItemFromArray('total', $currencyGroup)); + } + } + + + $this->mailer->onQueue('dailyReportMail', new DailyReportMail($report)); + + + die(); + + //Secret key + $secretKey = 'sk_test_51HuYHvEa9cmPdLq3ANG7ZYaGB9zMuhQZlwH19axJRauZsMnnpnuGBN1h8iAfr9kNVWe4FWcEcvZiMjn3hhBELHHx00hiBgjO41'; + $stripe = new \Stripe\StripeClient($secretKey); + + //$balance = $stripe->balance->retrieve(); + $balanceTransactions = $stripe->balanceTransactions->all(['limit' => 10]); + + + dd($balanceTransactions->toArray()); + + $token = $stripe->tokens->create([ + 'card' => [ + 'number' => $params['creditCard']['number'], + 'exp_month' => $params['creditCard']['month'], + 'exp_year' => $params['creditCard']['year'], + 'cvc' => $params['creditCard']['cvv'] + ], + ]); + + + die(); + + + //Bunu bookin sonrası ve booking iptali kısmına koyucaz + + //Connected Room Case + $roomAvailabilityUpdateForConnectedRoomParams = [ + 'property_id' => 1, + 'channel_id' => 1, + 'availability_type_id' => [1], + 'startDate' => '2022-01-18', + 'endDate' => '2022-01-25', + ]; + + $this->propertyRoomAvailabilityService->roomAvailabilityUpdateForConnectedRooms($roomAvailabilityUpdateForConnectedRoomParams); + //Connected Room Case + + dd('ok'); + + $mailParams = ['booking_id' => 1134]; + $this->newBookingMailService->process($mailParams); + + throw new Exception('api-unknown_error'); + + + } catch (Exception $e) { + dd($e->getMessage()); + Log::debug($e->getMessage()); + } + + die(); + + } +} diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php new file mode 100644 index 0000000..6364665 --- /dev/null +++ b/app/Http/Controllers/UserController.php @@ -0,0 +1,845 @@ +request = $request; + $this->userService = $userService; + $this->tempPropertyService = $tempPropertyService; + $this->propertyService = $propertyService; + $this->userPropertyMappingService = $userPropertyMappingService; + $this->jwtService = $jwtService; + $this->applicationCacheService = $applicationCacheService; + $this->mailer = $mailer; + $this->apiAccessTokenService = $apiAccessTokenService; + $this->languageService = $languageService; + $this->productService = $productService; + $this->mondayService = $mondayService; + } + + public function getUserList() + { + + + /*$userListCriteria['criteria'] = []; + $userListCriteria['criteria'][] = ['field' => 'idate', 'condition' => '=', 'value' => 0]; + //$userListCriteria['firstRow'] = true; + $userListCriteria['skip'] = 0; //take *2 + $userListCriteria['take'] = 3; + $userListCriteria['orderBy'][] = ['field' => 'id', 'value' => 'DESC'];*/ + + if (is_null($this->request->params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + + $userListCriteria = $this->request->params; + $userList = $this->userService->select($userListCriteria, fillOnUndefined($userListCriteria, 'select', ['*'])); + + if ($userList['status'] == 'success') { + return apiResponse(1, null, $userList['data'], 200); + } else { + return apiResponse(0, $userList['message'], null, 400); + } + } + + public function userCreate(Request $request) + { + + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + + $params = $this->request->params; + $userId = $request->credentials->user_id; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + $userCreateParams = $params; + $userCreateParams['user_id'] = $userId; + $return = []; + $userCreate = $this->userService->create($userCreateParams); + + if ($userCreate['status'] != 'success') { + throw new ApiErrorException($userCreate['message']); + } + $return['user'] = $userCreate['data']; + + $mailParams = [ + 'name_surname' => $return['user']['name'] . ' ' . $return['user']['surname'], + 'email' => $return['user']['email'], + 'hash_key' => $return['user']['hash_key'], + 'activation_link' => Config::get('app.client_server') . '/activate-user?email=' . $return['user']['email'] . '&key=' . $return['user']['hash_key'], + + ]; + + $this->mailer->onQueue( + 'userCreateMail', + new UserCreateMail($mailParams) + ); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function checkUserKey(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + $params = $this->request->params; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + $checkUserParams = $params; + $return = []; + $checkUserKey = $this->userService->checkUserKey($checkUserParams); + + if ($checkUserKey['status'] != 'success') { + throw new ApiErrorException($checkUserKey['message']); + } + $return['user'] = $checkUserKey['data']; + unset($return['user']['id']); + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function newPassword(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + $params = $this->request->params; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + $checkUserParams = $params; + $return = []; + $checkUserKey = $this->userService->checkUserKey($checkUserParams); + + if ($checkUserKey['status'] != 'success') { + throw new Exception($checkUserKey['message']); + } + + $checkUserKey = $checkUserKey['data']; + $newPasswordParams = [ + 'email' => fillOnUndefined($params, 'email'), + 'hash_key' => fillOnUndefined($params, 'key'), + 'password' => fillOnUndefined($params, 'password'), + 'password_confirmation' => fillOnUndefined($params, 'password_confirmation'), + 'user_id' => $checkUserKey['id'], + + ]; + + $newPassword = $this->userService->newPassword($newPasswordParams); + if ($newPassword['status'] != 'success') { + throw new Exception($newPassword['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function changePassword(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + $params = $this->request->params; + $userId = $request->credentials->user_id; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + $return = []; + + $changePasswordParams = [ + 'user_id' => $userId, + 'old_password' => fillOnUndefined($params, 'old_password'), + 'password' => fillOnUndefined($params, 'password'), + 'password_confirmation' => fillOnUndefined($params, 'password_confirmation'), + ]; + + $changePassword = $this->userService->changePassword($changePasswordParams); + if ($changePassword['status'] != 'success') { + throw new Exception($changePassword['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function forgotPassword(Request $request) + { + + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + $params = $this->request->params; + + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + + + $language = "en"; + if ($request->headers->get('language')) { + $language = $request->headers->get('language'); + } + + $userUpdateParams = [ + 'email' => fillOnUndefined($params, 'email'), + ]; + $return = []; + + $userUpdate = $this->userService->forgotPassword($userUpdateParams); + if ($userUpdate['status'] != 'success') { + throw new Exception($userUpdate['message']); + } + $userUpdate = $userUpdate['data']; + $mailParams = [ + 'name_surname' => $userUpdate['name'] . ' ' . $userUpdate['surname'], + 'email' => $userUpdate['email'], + 'hash_key' => $userUpdate['hash_key'], + 'activation_link' => Config::get('app.client_server') . '/reset-password?email=' . $userUpdate['email'] . '&key=' . $userUpdate['hash_key'], + 'language' => $language, + + ]; + + $this->mailer->onQueue( + 'UserForgotPassword', + new UserForgotPassword($mailParams) + ); + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function resetPassword(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + $params = $this->request->params; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + $userUpdateParams = [ + 'email' => fillOnUndefined($params, 'email'), + 'hash_key' => fillOnUndefined($params, 'key'), + 'password' => fillOnUndefined($params, 'password'), + 'password_confirmation' => fillOnUndefined($params, 'password_confirmation'), + ]; + $return = []; + + $userUpdate = $this->userService->resetPassword($userUpdateParams); + if ($userUpdate['status'] != 'success') { + throw new Exception($userUpdate['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => 'Your password updated successfuly', 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function userUpdate(Request $request) + { + + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + $params = $this->request->params; + + $userId = $request->credentials->user_id; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + $userUpdateParams = [ + 'user_update_data' => fillOnUndefined($params, 'user_update_data'), + 'update_user_id' => fillOnUndefined($params, 'update_user_id'), + 'user_id' => $userId + + + ]; + + $return = []; + $userCreate = $this->userService->update($userUpdateParams); + if ($userCreate['status'] != 'success') { + throw new Exception($userCreate['message']); + } + $return['user'] = $userCreate['data']; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function addUserProperty(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + + $params = $this->request->params; + + $userId = $request->credentials->user_id; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + + $userPropertyParams = [ + 'add_user_id' => fillOnUndefined($params, 'add_user_id'), + 'add_property_id' => fillOnUndefined($params, 'add_property_id'), + 'user_id' => $userId + ]; + $return = []; + + $addUserProperty = $this->userPropertyMappingService->addUserProperty($userPropertyParams); + + if ($addUserProperty['status'] != 'success') { + throw new Exception($addUserProperty['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function removeUserProperty(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + + $params = $this->request->params; + + $userId = $request->credentials->user_id; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + + $userPropertyParams = [ + 'remove_user_id' => fillOnUndefined($params, 'remove_user_id'), + 'remove_property_id' => fillOnUndefined($params, 'remove_property_id'), + 'user_id' => $userId + ]; + $return = []; + + $addUserProperty = $this->userPropertyMappingService->removeUserProperty($userPropertyParams); + if ($addUserProperty['status'] != 'success') { + throw new Exception($addUserProperty['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function userRegister() + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + $params = $this->request->params; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + $userCreateParams = $params; + $return = []; + $userCreate = $this->userService->create($userCreateParams); + if ($userCreate['status'] != 'success') { + throw new Exception($userCreate['message']); + } + $return['user'] = $userCreate['data']; + + $jwtData = $this->jwtService->jwtCreate(['user_id' => $return['user']['id']]); + + if ($jwtData['status'] != 'success') { + throw new Exception($userCreate['message']); + } + + $return['token'] = $jwtData['data']; + + if ($params['temp_hotel_id']) { + $propertySearchCriteria = [ + 'criteria' => [ + ["field" => "id", "condition" => "=", "value" => $params['temp_hotel_id']] + ], + 'firstRow' => true + ]; + $tempPropertyData = $this->tempPropertyService->select($propertySearchCriteria, ['id', 'name', 'giata_id', 'destination_id']); + if ($tempPropertyData['status'] == 'success') { + $tempProperty = $tempPropertyData['data']; + $propertyInsertData = [ + 'name' => $tempProperty['name'], + 'destination_id' => $tempProperty['destination_id'], + 'giata_id' => $tempProperty['destination_id'], + 'currency_type' => 'TRY', + 'created_at' => time(), + 'updated_at' => time(), + ]; + $propertyCreate = $this->propertyService->create($propertyInsertData); + if ($propertyCreate['status'] == 'success') { + $return['property'] = $propertyCreate['data']; + $userPropertyMappingData = [ + 'user_id' => $userCreate['data']['id'], + 'property_id' => $propertyCreate['data']['id'], + ]; + $userPropertyMappingCreate = $this->userPropertyMappingService->create($userPropertyMappingData); + if ($userPropertyMappingCreate['status'] == 'success') { + // $return['user_property_mapping'] = $userPropertyMappingCreate['data'] ; + } + } + } + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function setProperty() + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + + $request = $this->request->params; + $checkUserPropertyCriteria = [ + 'criteria' => [ + ['field' => 'user_id', 'condition' => '=', 'value' => $this->request->credentials->user_id], + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($request, 'property_id')], + ], + 'with' => ['property'], + 'firstRow' => true, + ]; + $checkUserProperty = $this->userPropertyMappingService->select($checkUserPropertyCriteria); + + if ($checkUserProperty['status'] != 'success') { + throw new Exception(lang('An unknown error occurred')); + } + + if (!$checkUserProperty['data']) { + throw new ApiErrorException(lang('Mapping data not found')); + } + $cacheParams = [ + + 'token' => $this->request->header('authToken'), + 'property_id' => $checkUserProperty['data']['property_id'] + + ]; + $applicationCache = $this->applicationCacheService->applicationCacheCreate($cacheParams); + $return['hotel_user_mapping'] = $checkUserProperty['data']; + $return['application_cache'] = $applicationCache['data']; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function userRegisterWithProperty() + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + DB::beginTransaction(); + + $params = $this->request->params; + if (is_null($params)) { + return apiResponse(0, 'Parameter Error.', null, 400); + } + $language = "en"; + if ($this->request->headers->get('language')) { + $language = $this->request->headers->get('language'); + } + $userCreateParams = $params; + $return = []; + $userCreateParams['status'] = 1; + $userCreateParams['user_type'] = 1; + + $userCreate = $this->userService->create($userCreateParams); + if ($userCreate['status'] != 'success') { + throw new ApiErrorException($userCreate['message']); + } + $return['user'] = $userCreate['data']; + + $propertyInsertData = [ + 'name' => $userCreateParams['property_name'], + 'status' => 1, + 'created_by' => $return['user']['id'], + 'updated_by' => $return['user']['id'], + 'created_at' => time(), + 'updated_at' => time(), + ]; + $propertyCreate = $this->propertyService->create($propertyInsertData); + if ($propertyCreate['status'] != 'success') { + throw new ApiErrorException($propertyCreate['message']); + } + $return['property_list'] = [ + [ + 'id' => $propertyCreate['data']['id'], + 'name' => $propertyCreate['data']['name'], + 'default_photo' => "/assets/img/placeholder.png", + ] + ]; + $userPropertyMappingData = [ + 'user_id' => $userCreate['data']['id'], + 'status' => 1, + 'property_id' => $propertyCreate['data']['id'], + 'created_by' => $return['user']['id'], + 'updated_by' => $return['user']['id'], + 'created_at' => time(), + 'updated_at' => time(), + ]; + $userPropertyMappingCreate = $this->userPropertyMappingService->create($userPropertyMappingData); + if ($userPropertyMappingCreate['status'] != 'success') { + throw new ApiErrorException($userPropertyMappingCreate['message']); + } + + $propertyProducts = $this->productService->setDefaultPropertyProducts($userPropertyMappingData); + if ($propertyProducts['status'] != 'success') { + throw new ApiErrorException($propertyProducts['message']); + } + + $jwtToken = $this->jwtService->jwtCreate(['user_id' => $return['user']['id']]); + if ($jwtToken['status'] != 'success') { + throw new ApiErrorException(lang('An unknown error occurred.')); + } + $jwtToken = $jwtToken['data']; + $saveToken = [ + "token" => md5(fillOnUndefined($jwtToken, "token")), + "expire_date" => fillOnUndefined($jwtToken, "exp"), + "user_id" => fillOnUndefined($return['user'], "id"), + "invalidate" => fillOnUndefined($jwtToken, "invalidate", 0), + ]; + + $saveTokenTo = $this->apiAccessTokenService->create($saveToken); + if ($saveTokenTo['status'] != 'success') { + throw new ApiErrorException(lang('General error')); + } + + + $return['token'] = $jwtToken['token']; + $return['expire_time'] = $saveTokenTo['data']['expire_time']; + $return['locale'] = null; + + + $mailParams = [ + 'name_surname' => $return['user']['name'] . ' ' . $return['user']['surname'], + 'email' => $return['user']['email'], + "password" => $return['user']['userPassword'], + 'language' => $language, + ]; + + $this->mailer->onQueue( + 'userCreateMail', + new UserCreateMail($mailParams) + ); + + $return['user'] = [ + 'name' => $userCreate['data']['name'], + 'surname' => $userCreate['data']['surname'], + + ]; + + + $notificationParam = $userCreateParams; + Notification::route('mail', ['sales@extranetwork.com' => 'Extranetwork Sales'])->notify(new NewUserNotification($notificationParam)); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + DB::commit(); + + //Monday.com contact items + //$createBoardItems = $this->mondayService->createBoardItems($userCreateParams); + + //Kommo integration + $kommoPropertyParam = [ + 'name_surname' => $notificationParam['name'] . ' ' . $notificationParam['surname'], + 'phone_number' => $notificationParam['phone'], + 'email' => $notificationParam['email'], + 'property' => $notificationParam['property_name'], + 'web' => fillOnUndefined($notificationParam, 'web', 'www.extranetwork.com') + ]; + + $this->propertyService->kommoCreateLead($kommoPropertyParam); + + } catch (ApiErrorException $e) { + DB::rollBack(); + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function getProfile(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $requestParams = [ + 'user_id' => $request->credentials->user_id, + 'status' => 1 + ]; + + $profileFields = ['id', 'email', 'name', 'surname', 'gender', 'language', 'phone']; + + $profile = $this->userService->getProfile($requestParams, $profileFields); + + if ($profile['status'] != 'success') { + throw new Exception($profile['message']); + } + $return['profile'] = $profile['data']; + + $languageCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + + $languages = $this->languageService->getAllLanguages($languageCriteria, ['id', 'code', 'name', 'status', 'language_key']); + + if ($languages['status'] != 'success') { + throw new Exception($languages['message']); + } + $return['languages'] = $languages['data']; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function updateProfile(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + + $requestParams = [ + 'name' => fillOnUndefined($params, 'name'), + 'surname' => fillOnUndefined($params, 'surname'), + 'gender' => fillOnUndefined($params, 'gender'), + 'language' => fillOnUndefined($params, 'language'), + 'phone' => fillOnUndefined($params, 'phone'), + 'user_id' => $request->credentials->user_id, + ]; + + $profile = $this->userService->profileUpdate($requestParams); + + if ($profile['status'] != 'success') { + + throw new ApiErrorException($profile['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $profile['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } +} diff --git a/app/Http/Controllers/V1/AIController.php b/app/Http/Controllers/V1/AIController.php new file mode 100644 index 0000000..4b10142 --- /dev/null +++ b/app/Http/Controllers/V1/AIController.php @@ -0,0 +1,84 @@ + false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + + $client = new \GuzzleHttp\Client([ + 'max' => 5, + 'strict' => false, + 'referer' => false, + 'protocols' => ['https'], + 'timeout' => 30, + 'headers' => [ + 'Authorization' => 'Bearer ' . config('app.openAISecretKey'), + 'Content-Type' => 'application/json', + 'Cache-Control' => 'no-cache', + 'Connection' => 'keep-alive', + 'Accept-Encoding' => 'gzip' + ] + ] + ); + + + $result = $client->post('https://api.openai.com/v1/chat/completions', [ + 'body' => json_encode($params['query']) + ]); + + $result = $result->getBody()->getContents(); + $result = json_decode($result, 1); + + $choiceMessage = reset($result['choices']); + + if (isset($choiceMessage['message']['content'])) { + $choiceMessage = json_decode($choiceMessage['message']['content'], 1); + $data = $choiceMessage; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $data]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + +} + diff --git a/app/Http/Controllers/V1/CompetitorPriceAnalysisController.php b/app/Http/Controllers/V1/CompetitorPriceAnalysisController.php new file mode 100644 index 0000000..565a0f1 --- /dev/null +++ b/app/Http/Controllers/V1/CompetitorPriceAnalysisController.php @@ -0,0 +1,1235 @@ +tripAdvisorCookie = 'TADCID=X6AlsdAQPb0JGrWnABQCFdpBzzOuRA-9xvCxaMyI12kGsGqfJg45fcvAP84z1sQ0l6BdP8fUu-L4qe5Zh7uINoyb1usbZKS8xpY; ak_bmsc=1D49AF2E3BEE0E197E48F537CB620AA3~000000000000000000000000000000~YAAQTLOvw+g4QVN8AQAARPc0rQ3fR0x3SZUbtbLJX5hS76gF80zdg9dmKzmyonlGUQ5dyEnGt6odg1kTQUj74Xg3FeuvUX3RZZ0Oqg/HiEAMSuEbC9QgS6fugmOZLJDsJ0Vyt29BuAOYMPf/OX7lt7pCPCgfb6iQClwqh1L5ZfsjPwLrd8B9KjIKMY9hjOdpjTacei9gZoM7eR0Q3Xe2r5BoIoV4CDpM8kde/BD6rKcZbRjfNAc0KTUpHUawTS3unZ9y145u8H/99xxZCbjqrUW25g+x+hpBdI7IBr6FTXw5sljsl5V7iwfNob0NGTJF4y/37RA5KBqGatwkeMp/qdMUPBK76TyDx0q6hIwBiVxTm3y0Hq999VUmrl7Kg2IEvrACcDQNM4gKo+zjxnse+g=='; + //$this->tripAdvisorCookie = 'TNI1625!AC8GutJQ28ZxO8jorf9FRDmeZ1UQdBgLItfITy8i77flGfQpI+UIoZd+cEfErXSR8VMnvDTKEW1MdkNx2GdRoJdWitJp5OC+ZjGozmfw/z8AfgvQ/IPIym57VSEZN06vcVmQulCtCPaM/q+OdmNYoEc7A3kcekslH9yl2sBLchIS'; + $this->tripAdvisorCookie = '4c4b806c8afc9ae6f0d9f62e0951c53c17d4d7964446a664f74de11087849ec3'; + + $this->restClient = new Client([ + 'max' => 5, + 'strict' => false, + 'referer' => false, + 'protocols' => ['https'], + 'track_redirects' => false, + 'allow_redirects' => false, + 'timeout' => 5 + ] + ); + + $this->params = Input::all(); + $this->currencyService = $currencyService; + $this->competitorPriceAnalysisService = $competitorPriceAnalysisService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyChannelCouponService = $propertyChannelCouponService; + $this->propertyPromotionService = $propertyPromotionService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->maxCompetitorPropertyAdd = 5; + } + + public function property(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $this->restClientFroTripadvisr = new Client([ + 'max' => 5, + 'verify' => false, + 'http_errors' => false + ] + ); + + $query[] = [ + 'query' => '84b17ed122fbdbd4', + 'variables' => [ + 'request' => [ + 'query' => $this->params['keyword'], + 'limit' => 10, + 'scope' => 'WORLDWIDE', + 'locale' => 'tr-TR', + 'scopeGeoId' => 1, + 'searchCenter' => null, + 'types' => ['LOCATION'], + 'locationTypes' => [ + 'ACCOMMODATION' + ], + 'userId' => null, + 'context' => [ + 'listResultType' => 'HOTEL' + ], + 'articleCategories' => [ + 'default', + 'love_your_local', + 'insurance_lander' + ], + 'enabledFeatures' => [ + 'typeahead-q' + ] + ] + ], + "extensions" => [ + "preRegisteredQueryId" => "5ddff5cef01e3cfd" + ] + ]; + + $result = $this->restClientFroTripadvisr->post('https://www.tripadvisor.com/data/graphql/ids', [ + 'headers' => [ + 'accept-encoding' => 'gzip, deflate', + 'content-type' => 'application/json', + 'origin' => 'https://www.tripadvisor.com', + 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36', + 'x-requested-by' => $this->tripAdvisorCookie, + 'Cookie' => 'TAUnique=%1%enc%3AB8gsSVzcYv%2Fy%2FQcUCC8NkwTAxw1PuWKMN7hDUF0uFMBsFY3v8WCw%2B6S4L8prdRoSNox8JbUSTxk%3D; TATrkConsent=eyJvdXQiOiIiLCJpbiI6IkFMTCJ9; TASSK=enc%3AAGWI%2Fjlo8Bi8BIsq9syl2j66bc48e82FdKzTyCz99EMkiPVMHbfZ6ngS%2F%2FZ%2FLHG6rhp8Sul%2BflsMdGktQml1d%2Fv7%2BwaYcOfHfgNDsKFC%2FJK4vpvZ5AbBpyN8zPlNHh5kAQ%3D%3D; _lc2_fpi=eb26cb8958b8--01kg7mzmk5gj81kjyacda52m8t; _lc2_fpi_meta=%7B%22w%22%3A1769783415397%7D; _gcl_au=1.1.656612842.1769783416; _ga=GA1.1.959970588.1769783416; PMC=V2*MS.44*MD.20260130*LD.20260130; CM=%1%mds%2C1769783421921%2C1769869821%7C; _ga_QX0Q50ZC9P=GS2.1.s1769783415$o1$g1$t1769784141$j57$l0$h0; TASID=2CE514F9A73746734B5866FC46F2C79C; TADCID=JXGuyvNjCnVXf8NBABQChrLCR-aOT_K5vlfY_RXuc4v8CctrMv3z7OyJhtqOIyA2jOwugGFgO4oUe5amkFV2bF3B1pi0-C11DE0; TASession=V2ID.2CE514F9A73746734B5866FC46F2C79C*SQ.1*LS.Hotel_Review*HS.recommended*ES.popularity*DS.5*SAS.popularity*FPS.oldFirst*FA.1*DF.0*TRA.true; PAC=AJfQGZeJqolH8Hzh3aZ4aFh_W2_NwTyw1iLgJX-tGmLLLPZwnNUwZE10W5UpsGahNGHg2Ks3ir6Z_6VMT-2nePlhd0jEjv2wl51LQbZqgAxsvoqWpjsPObiKX0VGg8bLCCUuPQCm1PWMu6iQhRW8tlJasIUq89-o5gswGEJueIGjz6bk-Ugzm1MP8oxQBgQcCaCNPMtS5PvVLfz2craf2Xs%3D; SRT=TART_SYNC; TART=%1%enc%3Aego6iAgRUncSBJJBqhWUc6MS72Gdmrlbyt6LsE1hcOb28uizb4wsXf5kB4qidaAdxJICaUS3Vk4%3D; OptanonConsent=isGpcEnabled=0&datestamp=Mon+Mar+02+2026+15%3A44%3A46+GMT%2B0300+(GMT%2B03%3A00)&version=202601.2.0&browserGpcFlag=0&isIABGlobal=false&hosts=&consentId=65f573db-6363-4747-9874-4ef7843acd7e&interactionCount=1&isAnonUser=1&landingPath=https%3A%2F%2Fwww.tripadvisor.de%2FHotel_Review-g293974-d295165-Reviews-Grand_Yavuz_Hotel-Istanbul.html&groups=C0001%3A1%2CC0002%3A1%2CC0003%3A1%2CC0004%3A1&prevHadToken=0; _li_dcdm_c=.tripadvisor.de; pbjs_sharedId=04024d25-0c58-462b-a8df-f66362c4dfa8; pbjs_sharedId_cst=zix7LPQsHA%3D%3D; _lr_retry_request=true; _lr_env_src_ats=false; _gcl_aw=GCL.1772455489.null; pbjs_unifiedID=%7B%22TDID_LOOKUP%22%3A%22FALSE%22%2C%22TDID_CREATED_AT%22%3A%222026-03-02T12%3A44%3A48%22%7D; pbjs_unifiedID_cst=zix7LPQsHA%3D%3D; pbjs_li_nonid=%7B%7D; pbjs_li_nonid_cst=zix7LPQsHA%3D%3D; __gads=ID=1cd3edce1a02a3a9:T=1769783416:RT=1772455490:S=ALNI_MZw9YE9xOBfuT3GptcwBZz1kk9y8A; __gpi=UID=000012ed05a9c113:T=1769783416:RT=1772455490:S=ALNI_MZjriFWmZQ4byU9h5qi7PxTySAUPA; __eoi=ID=5e090e36e225d6f3:T=1769783416:RT=1772455490:S=AA-AfjaO0smbZgTBvQkXOeDLsC_b; _lr_sampling_rate=100; datadome=jUF6V7MInCDT_e60vxsoUawgoBJ~kcHXw8wWQdbdAkpYR1DUqrYEcBuA9knMvbo2Q67tacHuwfJkQIk9aIeMFP5dfkY4ZQhZyeNyekXYunHAxxhaJ19q4AqV0_oha73O; __vt=y16Z4GVTSrBGH8EoABQCT24E-H_BQo6gx1APGQJPtzvZqhsLjxQLo2PGhRdDojXJUj-NQn9d3uXlRiKs37Gm9slng8tYN5Ykvbhr99p9t0D5jo3uHrNEzrIfiJ_F87BCZBZZH0h1u-4d1B0A0JmN31zUaQ', + ], + 'body' => json_encode($query), + 'decode_content' => true + ] + ); + + $result = $result->getBody()->getContents(); + $result = json_decode($result, 1); + + $dataResults = $result[0]['data']['Typeahead_autocomplete']['results']; + + $dataHotels = []; + foreach ($dataResults as $dataResult) { + + $hotelKeyRegex = '/Hotel_Review-(.*)-Reviews/m'; + preg_match_all($hotelKeyRegex, $dataResult['details']['url'], $hotelKeyMatch, PREG_SET_ORDER, 0); + $hotelKey = $hotelKeyMatch[0][1]; + + $dataHotels[] = [ + 'name' => $dataResult['details']['localizedName'], + 'location' => $dataResult['details']['localizedAdditionalNames']['longOnlyHierarchy'], + 'key' => $hotelKey + ]; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $dataHotels]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function getPropertyCompetitorPrice($params = []) + { + + $response = ['status' => false, 'message' => '']; + + try { + + + $currency = 'USD'; + $currencyRequest = fillOnUndefined($params, 'currency', 'USD'); + $competitorPropertyKey = $params['competitor_property_key']; + $startDate = $params['date'];//$params['startDate']; + $hotelIdExplode = explode('-d', $competitorPropertyKey, 2); + $hotelId = $hotelIdExplode[1]; + + + $dailyPrices = []; + $dailyPricesCache = []; + + $timeStartOverall = Carbon::now(); + + $date = Carbon::parse($startDate)->toDateString(); + + //$cacheKey = 'competitorPropertyHash-' . md5($competitorPropertyKey . '-' . $day . '-' . $currencyRequest); + $cacheKey = 'competitorPropertyHash-' . md5($competitorPropertyKey . '-' . $currencyRequest); + + //Cache::forget($cacheKey); + + if (Cache::has($cacheKey)) { + $dailyPricesCache = Cache::get($cacheKey); + } + + if (isset($dailyPricesCache[$competitorPropertyKey]) && isset($dailyPricesCache[$competitorPropertyKey][$date])) { + + $dailyPrices = $dailyPricesCache[$competitorPropertyKey][$date]; + + } else { + + $lastExchangeRate = $this->currencyService->lastExchangeRate($currency, $currencyRequest); + $currencyList = $this->currencyService->getCurrencyList(); + if ($currencyList['status'] != 'success') { + throw new ApiErrorException($currencyList['message']); + } + + $currencyList = pickItemFromArray('code', $currencyList['data']); + if (!in_array($currencyRequest, $currencyList)) { + throw new ApiErrorException('Invalid currency code'); + } + + + $dailyPrices['date'] = $date; + $dailyPrices['competitorPropertyKey'] = $competitorPropertyKey; + $dailyPrices['amount'] = null; + $dailyPrices['currency'] = null; + $dailyPrices['provider'] = null; + + $timeStart = Carbon::now(); + try { + + $query = [ + "detailId" => (integer)$hotelId, + "checkIn" => $date, + "checkOut" => Carbon::parse($date)->addDay()->toDateString(), + "rooms" => [ + [ + "adults" => 2, + "childrenAges" => [] + ] + ] + ]; + + $client = new \GuzzleHttp\Client([ + 'max' => 5, + 'strict' => false, + 'referer' => false, + 'protocols' => ['https'], + 'timeout' => 5, + 'headers' => [ + 'X-RapidAPI-Host' => 'travel-advisor.p.rapidapi.com', + 'X-RapidAPI-Key' => 'baad80dc1cmsha3959d133378f89p1c7844jsnd2bbb8c1b529', + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + 'Cache-Control' => 'no-cache', + 'Connection' => 'keep-alive', + 'Accept-Encoding' => 'gzip', + //'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' + ] + ] + ); + + $result = $client->post('https://travel-advisor.p.rapidapi.com/hotels/v2/get-offers?currency=USD', [ + 'body' => json_encode($query) + ]); + + $result = $result->getBody()->getContents(); + $result = json_decode($result, 1); + + //$result = json_decode('{"data":{"AppPresentation_queryHotelCommerceV2":{"__typename":"AppPresentation_HotelCommerceResponseV2","impressions":[{"__typename":"AppPresentation_ImpressionLog","data":"noClientLog"}],"sections":[{"__typename":"AppPresentation_HotelCommerceOfferList","trackingKey":"{\"ik\":\"031acedb-8ad1-4e60-83b7-f420f74270d2_0\",\"lid\":295165,\"hlk\":\"d933245d-57cc-41bb-abe7-65c56de1dbd4\",\"sn\":\"HotelCommerceDeals\",\"haik\":\"2b2dd3584c074bf1aa0fd27e0515ee34\",\"login\":false,\"plus\":false,\"hbfk\":\"a952489d18264e5dadc24707d6e912e7\"}","trackingTitle":"HotelOfferListSection_HOTEL_COMMERCE_OFFERS","stableDiffingType":"HotelOfferListSection_HOTEL_COMMERCE_OFFERS","isComplete":false,"clusterId":"HOTEL_COMMERCE_OFFERS","offersV2":[{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=BookingCom&src=32477061&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=1&matchID=1&oos=0&cnt=13&silo=10500&bucket=903023&nrank=1&crank=1&clt=M&ttype=MobileCR&tm=286615964&managed=false&capped=false&gosox=8odt39lfF_8t2Lfd3qE3zpQ0bxS-DfXQEVCGCu56GjFwnVeP40ut1Q002MbPq6-GJ6vB_j_RINgMP7TOHy-KvSCdi6d4Z8La-qnaZPCa-GEiuUaN_QJF7VccooTGz6JmhU8LApaUfO047k4rSkKAbSnSXPyL7p4B7_t3niacNpO2A0QC9DiwQuNiQC8EQLE_tDRUphklFjaKlLCkmWspYTT4-PvHv3rN5yphvdPwOS3NkNHYdexTPEJU7pHbjPOzsXI_bXJoAOHM2nApL3ce2g&priceShown=70&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=700&rq=P&rate=62.7700&fees=7.5400&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_637ab6e45797e5e16d5d775b08bcb864&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=19aaa73e178a8f07cd8f023359c51db95&ik=a952489d18264e5dadc24707d6e912e7&aok=ca2e78acd4e24a049ddd6ec13becaa79&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus0_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":"img2/branding/hotels/Booking_Com_v2_384x164_Blue.png","providerName":"Booking.com","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=HotelsCom2&src=34516980&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=2&matchID=1&oos=0&cnt=13&silo=6103&bucket=901739&nrank=2&crank=2&clt=M&ttype=MobileCR&tm=286615964&managed=false&capped=false&gosox=3hpVWWn59hLMfGrDq-0PCWG0EYNU_kg7POsPF6ThFAsDLVIH9gwfrcn3KKhOJ77Hs9AhHIzsICyJ0mI3s8HKXpv7jGezH5fXhsJqGmqdraP3tSka3ZDOkJoU7EIJ70o5dfkzgxFaWMjETOgqmxodjD1lXD1fYejoKeMUXnsa3xIWLHmXb6IRJ4xhf-0yHS8BVJTNTk4sr1oaCXYVIZq4lELuOzeOFUU6d4EYfZ4AxRhyztoG3Eb2AOJCs0ZKWt6hRj9B443Hfn2gtB2jjrSYxtg2QAMYK-T84MC4H3H0X3M&priceShown=70&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=700&rq=P&rate=62.8200&fees=7.5400&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_24b0d2a6ed870a6180e189a001b79dd5&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=12cc54312b2eb45f0ce07e66a5c1c33f6&ik=a952489d18264e5dadc24707d6e912e7&aok=c2783e8f360947db94f58e9d83893781&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus1_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"Hotels.com","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$63","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=Destinia&src=38799788&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=3&matchID=1&oos=0&cnt=13&silo=17847&bucket=941214&nrank=13&crank=13&clt=M&ttype=MobileCR&tm=286615964&managed=false&capped=false&gosox=swfzbqjFSX7gVvosEdYcyYIGyfWsITpUvJQf-vEJqDHm9NwKePRSjyIPgYDOS3Zu3lVB4dzt7cLJszgYOeRqxckOHll06lzUQrVe3cN6hK12F3yN-JD5PgYUe25vWygGqyUJllLEK0WqQnKNdJYH_ZPUgiXvFP7XXyp0h7lT9ou0NFSmGSUWNoqUsKSZaylhNPj4-8e_es3nKmG90_A5Lc2Q0dh17FM8QlTukduM87Oxcj9tcmgA4czacCkvdx7a&priceShown=63&pm=AIWE&hac=AVAILABLE&mbl=BEAT&mbldelta=0&rq=P&rate=56.4000&fees=7.5700&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_463dd3f7e09ddac3184dd5e74ad235d4&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=1b6dc44a7278cc8c32567e3a520bb93b6&ik=a952489d18264e5dadc24707d6e912e7&aok=97efe91fd2da40e78e184ad7b9c8b7e9&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$63","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus2_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"Destinia.com","strikeThroughPrice":{"__typename":"AppPresentation_LocalizedString","string":"$74","debugValueKey":null},"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=Expedia&src=32697091&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=4&matchID=1&oos=0&cnt=13&silo=4310&bucket=910482&nrank=3&crank=3&clt=M&ttype=MobileCR&tm=286615964&managed=false&capped=false&gosox=A9XDoEHOyR8iEoOswVX8y-cW5J59D7A7N0XHbUPeLv66diINev9wjgAObbDdrO6yX5d0Q1ez6ijY2HLxwIX5W9XpaS5zSbmuZK_8Gu7vLuTAD9SGrijsQAT4286JgdpRReQPdO0gi8p8HjzLpSV7beb1PKJh4qVLBTlLxU6LEb1krsYnSBNbM8o5e0dlEj_EjuMEKK-PIa-Wjo5kGgO767Q0VKYZJRY2ipSwpJlrKWE0-Pj7x796zecqYb3T8DktzZDR2HXsUzxCVO6R24zzs7FyP21yaADhzNpwKS93Hto&priceShown=70&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=700&rq=P&rate=62.8200&fees=7.5400&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_75d1f38b85875ca0bf26dcc0945b80d7&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=13cf902d80cadc2e02215c3661782bd91&ik=a952489d18264e5dadc24707d6e912e7&aok=cc924d45a89847e890676c53a4749cce&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus3_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"Expedia.com","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":null,"commerceLink":null,"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"otelz.com","strikeThroughPrice":null,"status":"IN_PROGRESS","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=TravelocityEWS&src=54221039&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=6&matchID=1&oos=0&cnt=13&silo=11456&bucket=860112&nrank=5&crank=5&clt=M&ttype=MobileCR&tm=286615964&managed=true&capped=false&gosox=MahhiGPAvYM26fgM1Fs4y0LVZilal8soQAd2MInZ40In_xelT2u3Ixjgz2DgbhROuqk_NPFo6jTPJtKMwVpV-CMX2PFFS0t0R3BGfZbK-b9Y1Jq0QYdwPNWnPIX2GiSyMqshHc50jde-NsAl4bH3ts_GhD0pIqyi4lT55BblhQcYWrVvWWB70uU03PUBEl4af9tWrpjsJHFaPzLyh2ldrULuOzeOFUU6d4EYfZ4AxRhyztoG3Eb2AOJCs0ZKWt6hRj9B443Hfn2gtB2jjrSYxtg2QAMYK-T84MC4H3H0X3M&priceShown=70&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=700&rq=P&rate=62.8200&fees=7.5400&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=16948884fd24b04e1043a4fb842aa97be&ik=a952489d18264e5dadc24707d6e912e7&aok=82abbe08c2104f3e80985e9b18d71158&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus5_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"Travelocity","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=Agoda&src=48793666&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=7&matchID=1&oos=0&cnt=13&silo=5122&bucket=895087&nrank=6&crank=6&clt=M&ttype=MobileCR&tm=286615964&managed=false&capped=false&gosox=AVPDU6RbcFVwDmbzu1YYBtZP2QUi_h128CKV_kPJuS07F_gMAdHJfUdZc8AvnTrys1pvQMRoQTuIgA976JCaSbt03vCzSlET_r8sQ_dNDrbRg9T-G1NQPHxYQ_TbM2jw-UtdbO2VUIE_TgNh3iknx_9MM4l1otBv9IETF1TlsG1h_2lMxYV4LzkEOncYyHUB0LI9QnHjhVUlnK50nbopfWyJVyDQFH6wPS4euHOGtm6Hz1FMFNk3jN9MF76vIS3C&priceShown=70&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=700&rq=P&rate=62.8200&fees=7.5300&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_6977b14032c41291e33b975570bcb286&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=1ce3884332c82f010606b95510eb54183&ik=a952489d18264e5dadc24707d6e912e7&aok=60e8134067604ba487e187f96f906c5a&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus6_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"Agoda.com","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=CtripTA&src=79214115&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=8&matchID=1&oos=0&cnt=13&silo=13669&bucket=899272&nrank=7&crank=7&clt=M&ttype=MobileCR&tm=286615964&managed=false&capped=false&gosox=lsSswv72QfbQgHuBbiLSFYE96DuL8ibgqcCcE_kHNN_Km9dkuLDm3L_m7RS8TJ3HivMyOCtiFJw9AfjfwuKpbEVsJk90QOwx9LCM4T2yPAJh7pkq6NTEKg0WDZ-hQs8lqb4uKT8BvbntHGMsxczemosM8iUgx9LuUm4EROAIHrXC9Gj0-zlbhNnfi4QogBrlrJpHfyCEDtt94CM_z-5cGGHhfsgOsRiGcJYkNnQrWhC3R27nhgsncVtnNAe50s6W&priceShown=70&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=700&rq=P&rate=62.8100&fees=7.5400&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_596afd5c88483c206cc4318df651709e&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=18f2d4afbf696d011dbe4059cc42ee964&ik=a952489d18264e5dadc24707d6e912e7&aok=12a5b5ad33374d5dbf7c1ee6c3d83798&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus7_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"Trip.com","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$74","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=Mirai&src=255621478&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=9&matchID=1&oos=0&cnt=13&silo=26106&bucket=979000&nrank=8&crank=8&clt=M&ttype=MobileCR&tm=286615964&managed=true&capped=false&gosox=sb95c4IBPXymK172SEJwTOq17gLqU_oE5TJLTjytnMaxAedRBBXUYoekoR-d9PMVNvrpLlBCaSTqtFK0EoJ2boOmn7uhCcmC2KGSJKrEybT4gR0orTS2T-AArDVYrR6dYi4oq6NjBVtyuYK5lmWSSKlSNP1ISAz8UxD3Fybyaf0M96yboyeG0iMId70TR_Fd28BM-yClSZkKZcUCt5Qj4mfpsweKUIlmCaDUaTygVHxh4X7IDrEYhnCWJDZ0K1oQt0du54YLJ3FbZzQHudLOlg&priceShown=74&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=1100&rq=P&rate=67.0700&fees=6.7100&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_1b7639ec01898e034557cc27443ed58d&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=17098edcf450ce800b98a43c64c737828&ik=a952489d18264e5dadc24707d6e912e7&aok=dddc0118d4ec4025b3bd3a8c56cd61f0&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$74","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus8_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"Official website","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$67","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=StayForLong&src=143811959&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=10&matchID=1&oos=0&cnt=13&silo=40511&bucket=944068&nrank=9&crank=9&clt=M&ttype=MobileCR&tm=286615964&managed=false&capped=false&gosox=iU3wCxpEHfKz1_yraLhvgLECmoIxDl97J66m2TgHHDHBaXRNIPUumERknooRzmLhEf1P3JKVjztLN-P06N6ZoJs7YuP0twV4ttX9T7P2xCVP6M-JfITK9FzYFiRtslp_bNXI4HjtY-enzYzJfOrNNoE1QjUcqgXJchXu2mYd5xM21Y_InGFgm4ssJnJCe_hHYf9pTMWFeC85BDp3GMh1AdCyPUJx44VVJZyudJ26KX1siVcg0BR-sD0uHrhzhrZuh89RTBTZN4zfTBe-ryEtwg&priceShown=67&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=400&rq=P&rate=66.0100&fees=0.9900&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_1a2c43030be33dc2825829366987f63a&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=18f8f91cf2b981d48d9a18cd57013d982&ik=a952489d18264e5dadc24707d6e912e7&aok=ced593da26634a0d99b705e4a8d92827&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$67","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus9_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"StayForLong","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=Vio&src=256849928&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=11&matchID=1&oos=0&cnt=13&silo=47493&bucket=997319&nrank=10&crank=10&clt=M&ttype=MobileCR&tm=286615964&managed=false&capped=false&gosox=qi8Pk1Rxoc1dwj6TlSgFnDHmTstrv7hj9b7zE8y_bU1UhxfB0tE_98RArd2AGTDUfqleSf3zWjw73eZ_KKqYWhD2WpKqmjNXuuKaHjmd90ShD8E-GDEqX0YFTdRe7DHJnw-aMYVFolfl6BPtHd2VDX3OrZqMMJuXF4BjH4EnXAghnAXWcstQ_ATM_czA23uMxrvsCe2kVfhGH30bQ8AVpnzO-4h0JzsHHOAlD7-2zqL5HNWwqmBPMFSYFlkr7Hpm9lTNXFfW_xzFOmhEb7yupJBLokGIyXFrxUiGNMxHAZU&priceShown=70&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=700&rq=P&rate=62.7800&fees=7.5300&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_1db480d711ee619f011e9cb65cc805d7&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=184590a69616cbf405d1409a6ec8a78b8&ik=a952489d18264e5dadc24707d6e912e7&aok=fa05ea300e294464b826633eae70aa87&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus10_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"Vio.com","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=BookingeDreamsWL&src=60220887&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=12&matchID=1&oos=0&cnt=13&silo=35404&bucket=914257&nrank=11&crank=11&clt=M&ttype=MobileCR&tm=286615964&managed=true&capped=false&gosox=wAYrrGjW7Bydo3kUpJXG9x-_rTT4xfM27ciwH-8l-DFHQoXiw53-HkoXH2woreUgPEQGNzWoU0UWdeapcHF6TC2ZZTDzaMEmDVNA1Ju9Ksf3PkgqLsaIz3LZAuwdMtNPvqhfyhXVqIaPY00bJD0LJE-0Cn622zDgb7Kfys-XrUCA00kLdR6Ct3D621MI2FwsqpyEGSk8F_hZm79XabgIE9lfMxamOainLytCpOUDBa13BFhBr8Q51Y0eTw8h_GtuOIcjIhb4Huqemyr-_eDENA&priceShown=70&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=700&rq=P&rate=62.7700&fees=7.5400&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=1780cec9318b617c63e6e6c6e2f041d05&ik=a952489d18264e5dadc24707d6e912e7&aok=e4d17c75ad2a42529ce22b377ebafcea&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus11_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"eDreams","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]},{"__typename":"AppPresentation_HotelCommerceOfferDeal","displayPrice":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"commerceLink":{"__typename":"AppPresentation_ExternalLink","externalUrl":"/Commerce?p=OrbitzEWS&src=78874101&geo=295165&from=HotelDateSearch_HotelCommerceDeals&slot=13&matchID=1&oos=0&cnt=13&silo=20728&bucket=862895&nrank=11&crank=12&clt=M&ttype=MobileCR&tm=286615964&managed=true&capped=false&gosox=duEJfdKRjSaUExDPyWiVTxfzxv14vo3_X57IHuv-PENZzSU0u_qCcSugyEI21SUpyTdJYppCoBfhowdBz1tytuSVcY2ctKopxjdDSR4zUA8bk_9BDHix8tffG7gOF9URKgwSVrp1OniloXJJ3ME0EJzucmES6J8quJoqj2ReWepGgoURtfMoOFl-stDivIvoQmMxR6jRfSXd2WGXNXtHBnzO-4h0JzsHHOAlD7-2zqL5HNWwqmBPMFSYFlkr7Hpm9lTNXFfW_xzFOmhEb7yupJBLokGIyXFrxUiGNMxHAZU&priceShown=70&pm=AIWE&hac=AVAILABLE&mbl=LOSE&mbldelta=700&rq=P&rate=62.8200&fees=7.5400&cur=USD&adults=2&child_rm_ages=&inDay=18&outDay=19&rdex=RDEX_445cc1f599f38d72d456f6729697e683&rooms=1&inMonth=3&inYear=2024&outMonth=3&outYear=2024&auid=a087cf4c-7eeb-4052-9492-74e1ec4dc0d4&def_d=false&bld=L_1,D_47,G_2,W_1,U_0,C_240318,T_15&bh=true&cs=10084d859975c54482a6a57bb7df14f32&ik=a952489d18264e5dadc24707d6e912e7&aok=c3ebd3b4b4534b04b485a5d5ebfa84d3&tp=APS-HotelCommerceDeals&pageLocId=295165","text":{"__typename":"AppPresentation_LocalizedString","string":"$70","debugValueKey":null},"accessibilityString":null,"trackingContext":"server_nonPlus12_hotelCommerceLink"},"details":[{"__typename":"AppPresentation_LocalizedString","string":"Free breakfast included","debugValueKey":null}],"providerLogoUrl":null,"providerName":"Orbitz.com","strikeThroughPrice":null,"status":"AVAILABLE","roomUrgencyMessage":null,"labels":[]}]},{"__typename":"AppPresentation_LogicalBreak","stableDiffingType":"LogicalBreakSection","spacing":"spacing-03","clusterId":null,"divider":null,"background":null},{"__typename":"AppPresentation_OmnibusDisclosure","link":null,"contentText":{"__typename":"AppPresentation_HtmlString","htmlString":"Prices are provided by our partners, and reflect average nightly room rates, including taxes and fees that are fixed, known to our partners, and due at time of booking. Other miscellaneous taxes and hotel fees which are not fixed or due at time of booking may be payable at the property at time of stay. Please see our partners for more details."},"trackingTitle":"HotelDealsDisclaimerSection","trackingKey":"{\"dt\":\"HOTEL_DEALS\",\"ik\":\"031acedb-8ad1-4e60-83b7-f420f74270d2_1\",\"sn\":\"HotelCommerceDeals\"}","stableDiffingType":"HotelDealsDisclaimerSection","clusterId":null},{"__typename":"AppPresentation_LogicalBreak","stableDiffingType":"LogicalBreakSection_1","spacing":"spacing-03","clusterId":null,"divider":null,"background":null}],"statusV2":{"__typename":"AppPresentation_SuccessQueryResponseStatus","partial":false,"pollingStatus":{"__typename":"AppPresentation_QueryResponsePollingStatus","delayForNextPollInMillis":10,"updateToken":"eyJ2ZXIiOiJ2MiIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2IiwidmVyc2lvbiI6IjEifQ.eyJvYmplY3QiOiJ7XCJAY1wiOlwiLlVwZGF0ZVRva2VuXCIsXCJ0eXBlXCI6XCJQT0xMSU5HXCIsXCJjbHVzdGVySWRzXCI6W1wiSE9URUxfQ09NTUVSQ0VfT0ZGRVJTXCJdLFwicHJvdmlkZXJVcGRhdGVUb2tlbnNcIjp7XCJIT1RFTF9ERVRBSUxfUFJPVklERVJcIjp7XCJAY1wiOlwiLlN0cmluZ1ZhbHVlUHJvdmlkZXJVcGRhdGVUb2tlblwiLFwidmFsdWVcIjpudWxsfX0sXCJwb2xsaW5nU2VxdWVuY2VOdW1cIjoxfSJ9.MjRlNTU4MmItMGIyNS00YTI1LTk4NDUtYTY0YjFiNzYxODc0Lk1FVUNJRVNiQlU2bWIxTjlvYV80VWx3eEwtTXY4UmZRal9fMGFvOXB1WGMtRG5rVUFpRUFsUDJPVUdMYzBHY2NpRTlXLVJOUXBtT3FLNlZKSHpfV3NmSFZmNlpjaFhV"}},"updatedClusterIds":[]}}}', 1); + + if (isset($result['data']['AppPresentation_queryHotelCommerceV2']['sections'][0]['offersV2'])) { + + $dataResults = $result['data']['AppPresentation_queryHotelCommerceV2']['sections'][0]['offersV2']; + + $dailyPricesByProvider = []; + foreach ($dataResults as $dataResult) { + + if ($dataResult['displayPrice']) { + $priceText = str_replace('$', '', $dataResult['displayPrice']['string']); + $provider = $dataResult['providerName']; + + if ($priceText) { + + $taxesAndFeesText = empty($taxesAndFeesText) ? 0 : $taxesAndFeesText; + $dailyPricesByProvider[] = [ + 'amount' => round(moneyDoubleFormatDecimal((($priceText + $taxesAndFeesText)) * $lastExchangeRate['data'])), + 'currencyCode' => $currencyRequest, + 'provider' => $provider, + 'logo' => config('app.url') . '/img/channel-icons/' . mb_strtolower(str_replace('.', '', $provider)) . '.png', + ]; + + } + } + + } + + if (!empty($dailyPricesByProvider)) { + + $rateCollect = collect($dailyPricesByProvider); + $rateCollect = $rateCollect->sortBy('amount')->toArray(); + $minRate = reset($rateCollect); + + $dailyPrices['amount'] = $minRate['amount']; + $dailyPrices['currency'] = $currencyRequest; + $dailyPrices['provider'] = $minRate['provider']; + $dailyPrices['all'] = array_values($rateCollect); + + if (Cache::has($cacheKey)) { + $dailyPricesCache = Cache::get($cacheKey); + } + + $dailyPrices['cacheTime'] = Carbon::now()->toDateTimeString(); + + $dailyPricesCache[$dailyPrices['competitorPropertyKey']][$dailyPrices['date']] = $dailyPrices; + + Cache::put($cacheKey, $dailyPricesCache, 60 * 5);//600 10 dk + } + + + } + + + } catch (Exception $e) { + Log::debug($e->getMessage()); + //dd($e->getMessage()); + } + + $timeEnd = Carbon::now(); + $timeDiff = Carbon::parse($timeStart)->floatDiffInRealSeconds(Carbon::parse($timeEnd)); + //$dailyPrices[$hotelKey][$day]['time'] = $timeDiff; + $dailyPrices['time'] = $timeDiff; + + } + + $timeEndOverall = Carbon::now(); + $timeDiffOverall = Carbon::parse($timeStartOverall)->floatDiffInRealSeconds(Carbon::parse($timeEndOverall)); + + //$dailyPrices['responseTime'] = $timeDiffOverall; + + $response = ['status' => true, 'message' => null, 'data' => $dailyPrices]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + return $response; + + } + + public function propertyCompetitorPrice(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $request->all(); + + + if (isset($params['property_id'])) { + + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_manager_id', 'condition' => '=', 'value' => 12], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + $params['competitor_property_key'] = $channelManagerPropertyMapping['data']['channel_manager_property_id']; + } else { + throw new ApiErrorException('Mapping not found.'); + } + + + } + + $paramCompetitorPrice = [ + 'date' => $params['date'], + 'competitor_property_key' => $params['competitor_property_key'], + 'currency' => $params['currency'], + ]; + + $propertyCompetitorPrice = $this->getPropertyCompetitorPrice($paramCompetitorPrice); + + if (!$propertyCompetitorPrice['status']) { + throw new ApiErrorException($propertyCompetitorPrice['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyCompetitorPrice['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function createPropertyCompetitor(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ] + ]; + + + $requestSelectResult = $this->competitorPriceAnalysisService->selectPropertyCompetitorMapping($requestSelectCriteria); + + if ($requestSelectResult['status'] != 'success') { + throw new ApiErrorException($requestSelectResult['message']); + } + + if (count($requestSelectResult['data']) >= $this->maxCompetitorPropertyAdd) { + //throw new ApiErrorException('Up to 5 records can be added.'); + } + + + $requestParam = [ + 'property_id' => $this->params['params']['property_id'], + 'competitor_property_name' => $this->params['params']['competitor_property_name'], + 'competitor_property_key' => $this->params['params']['competitor_property_key'], + 'created_by' => $request->auth->id, + 'updated_by' => $request->auth->id, + ]; + + $requestResult = $this->competitorPriceAnalysisService->createPropertyCompetitorMapping($requestParam); + + if ($requestResult['status'] != 'success') { + throw new ApiErrorException($requestResult['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $requestResult['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function syncPropertyCompetitor(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ] + ]; + + + $requestSelectResult = $this->competitorPriceAnalysisService->selectPropertyCompetitorMapping($requestSelectCriteria); + + if ($requestSelectResult['status'] != 'success') { + throw new ApiErrorException($requestSelectResult['message']); + } + + DB::beginTransaction(); + + if (!empty($requestSelectResult['data'])) { + $deleteIds = pickItemFromArray('id', $requestSelectResult['data']); + $deletePropertyCompetitorMapping = $this->competitorPriceAnalysisService->deletePropertyCompetitorMapping($deleteIds); + if ($deletePropertyCompetitorMapping['status'] != 'success') { + throw new ApiErrorException($deletePropertyCompetitorMapping['message']); + } + } + + + $competitorProperty = $this->params['params']['competitor']; + + + if (count($competitorProperty) > $this->maxCompetitorPropertyAdd) { + //throw new ApiErrorException('Up to 5 records can be added.'); + } + + $requestResultData = []; + foreach ($competitorProperty as $property) { + + $requestParam = [ + 'property_id' => $this->params['params']['property_id'], + 'competitor_property_name' => $property['name'], + "competitor_property_source" => fillOnUndefined($property, "competitor_property_source"), + 'competitor_property_key' => $property['key'], + 'created_by' => $request->auth->id, + 'updated_by' => $request->auth->id, + ]; + + $requestResult = $this->competitorPriceAnalysisService->createPropertyCompetitorMapping($requestParam); + + if ($requestResult['status'] != 'success') { + throw new ApiErrorException($requestResult['message']); + } + + $requestResultData[] = $requestResult['data']; + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $requestResultData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function getPropertyCompetitor(Request $request) + { + + $params = $request->all(); + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['params']['property_id']], + ] + ]; + + $columns = ['id', 'property_id', 'competitor_property_name', 'competitor_property_key','competitor_property_source']; + $requestSelectResult = $this->competitorPriceAnalysisService->selectPropertyCompetitorMapping($requestSelectCriteria, $columns); + + if ($requestSelectResult['status'] != 'success') { + throw new ApiErrorException($requestSelectResult['message']); + } + + if (empty($requestSelectResult['data'])) { + throw new ApiErrorException(lang('Mapping data not found')); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $requestSelectResult['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function deletePropertyCompetitor(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ['field' => 'id', 'condition' => '=', 'value' => $this->params['params']['property_competitor_mapping_id']] + ], 'firstRow' => true + ]; + + + $requestSelectResult = $this->competitorPriceAnalysisService->selectPropertyCompetitorMapping($requestSelectCriteria); + + if ($requestSelectResult['status'] != 'success') { + throw new ApiErrorException($requestSelectResult['message']); + } + + if (empty($requestSelectResult['data'])) { + throw new ApiErrorException(lang('Mapping data not found')); + } + + $deletePropertyCompetitorMapping = $this->competitorPriceAnalysisService->deletePropertyCompetitorMapping($requestSelectResult['data']['id']); + + if ($deletePropertyCompetitorMapping['status'] != 'success') { + throw new ApiErrorException($deletePropertyCompetitorMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function propertyCompetitorExcel(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->params['params']; + + if (Carbon::parse($params['start_date'])->isBefore(Carbon::now()) && !Carbon::parse($params['start_date'])->isToday()) { + throw new ApiErrorException('Comparison possible for today and next days.'); + } + + if (Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])) > 14) { + throw new ApiErrorException('A maximum of 14 days of data can be retrieved.'); + } + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ] + ]; + + $columns = ['id', 'property_id', 'competitor_property_name', 'competitor_property_key','competitor_property_source']; + $propertyCompetitor = $this->competitorPriceAnalysisService->selectPropertyCompetitorMapping($requestSelectCriteria, $columns); + + if ($propertyCompetitor['status'] != 'success') { + throw new ApiErrorException($propertyCompetitor['message']); + } + + if (empty($propertyCompetitor['data'])) { + throw new ApiErrorException(lang('Mapping data not found')); + } + + $dataTableData = []; + + $dateFirst = $params['start_date']; + $dateDiff = Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])); + foreach ($propertyCompetitor['data'] as $property) { + for ($i = 0; $i <= $dateDiff; $i++) { + + $date = Carbon::parse($dateFirst)->addDays($i)->toDateString(); + + + $propertyCompetitorPrice = [ + 'status' => false, + 'data' => [] + ]; + + $cacheKey = 'competitorPropertyHash-' . md5($property['key'] . '-' . $params['currency']); + + if (Cache::has($cacheKey)) { + $dailyPricesCache = Cache::get($cacheKey); + + if (isset($dailyPricesCache[$property['key']][$date])) { + $propertyCompetitorPrice = [ + 'status' => true, + 'data' => $dailyPricesCache[$property['key']][$date] + ]; + } + } + + /*if(empty($propertyCompetitorPrice)) { + $paramCompetitorPrice = [ + 'date' => $date, + 'competitor_property_key' => $property['key'], + 'currency' => $params['currency'], + ]; + + $propertyCompetitorPrice = $this->getPropertyCompetitorPrice($paramCompetitorPrice); + }*/ + + $dailyAmount = null; + if ($propertyCompetitorPrice['status']) { + $dailyAmount = $propertyCompetitorPrice['data']['amount']; + } + + $dataTableData['value'][$property['key']][$date] = [ + 'amount' => $dailyAmount, + 'currency' => $params['currency'] + ]; + + $dataTableData['property'][$property['key']] = $property['name']; + $dataTableData['date'][$date] = Carbon::parse($date)->format('d.m.Y'); + + } + + } + + + $fileName = 'PropertyCompetitorExport-' . md5(implode('-', $params)) . '.xlsx'; + $fileNamePath = 'excel/' . $fileName; + $fileNamePublic = config('app.url') . '/' . $fileNamePath; + + $excelStore = Excel::store(new PropertyCompetitorExport($dataTableData), $fileNamePath, 'public'); + + if (!$excelStore) { + throw new ApiErrorException(lang('Mapping data not found')); + } + + $data = [ + 'url' => $fileNamePublic + ]; + + //Delete files older than 1 hours + $excelFileDir = public_path('excel'); + foreach (glob($excelFileDir . '/' . "*") as $file) { + if (filemtime($file) < time() - 3600) { // 6 hours 21600 + unlink($file); + } + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $data]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + + public function bestAvailablePrice(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = json_decode($request->getContent(), 1); + $params = $this->params['params']; + + $params['type'] = fillOnUndefined($params, 'type', 'range'); + $bookingEnginePropertyId = $params['property_id']; + + $cacheKeyParam[] = $params['type']; + $cacheKeyParam[] = $bookingEnginePropertyId; + if ($params['type'] == 'range') { + + if (Carbon::parse($params['start_date'])->isBefore(Carbon::now()) && !Carbon::parse($params['start_date'])->isToday()) { + throw new ApiErrorException('Comparison possible for today and next days.'); + } + + if (Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])) > 14) { + throw new ApiErrorException('A maximum of 14 days of data can be retrieved.'); + } + + $cacheKeyParam[] = $params['start_date'] . '|' . $params['finish_date']; + } else if ($params['type'] == 'date') { + $cacheKeyParam[] = $params['date'] . '|' . $params['currency']; + } + + $cacheKey = 'CRR:' . md5(implode(',', $cacheKeyParam)); + + + //Cache::forget($cacheKey); + if (Cache::has($cacheKey)) { + $responseData = Cache::get($cacheKey); + } else { + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => 5], + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingEnginePropertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['currency'], + 'firstRow' => true + ]; + + $getChannelProperty = $this->propertyChannelMappingService->select($requestParam); + $getChannelProperty = $getChannelProperty['status'] == 'success' && !empty($getChannelProperty['data']) ? $getChannelProperty['data'] : null; + + if (!$getChannelProperty) { + throw new ApiErrorException('Property Channel not found'); + } + + + switch ($params['type']) { + + case 'range' : + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => 5], + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingEnginePropertyId], + ['field' => 'stop_sell', 'condition' => '=', 'value' => 0], + ['field' => 'date', 'condition' => '>=', 'value' => \Carbon\Carbon::parse($params['start_date'])->format('Y-m-d')], + ['field' => 'date', 'condition' => '<', 'value' => Carbon::parse($params['finish_date'])->addDay()->format('Y-m-d')], + ['field' => 'amount', 'condition' => '<>', 'value' => null], + ['field' => 'amount', 'condition' => '<>', 'value' => 0], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'roomRateMapping.propertyRoomRate' + ], + 'orderBy' => [ + ['field' => 'date', 'value' => 'ASC'], + ['field' => 'amount', 'value' => 'ASC'] + ], + ]; + + $getPropertyRoomRatePrice = $this->propertyRoomRatePriceService->select($requestParam); + + if ($getPropertyRoomRatePrice['status'] != 'success' || empty($getPropertyRoomRatePrice['data'])) { + throw new ApiErrorException(lang('PropertyRoomRatePrice not found')); + } + + $getPropertyRoomRatePrice = $getPropertyRoomRatePrice['status'] == 'success' && !empty($getPropertyRoomRatePrice['data']) ? $getPropertyRoomRatePrice['data'] : []; + + $firstDate = Carbon::parse($params['start_date'])->format('Y-m-d'); + $lastDate = Carbon::parse($params['finish_date'])->addDay()->format('Y-m-d'); + $dateDiff = Carbon::parse($firstDate)->diffInDays(Carbon::parse($lastDate)); + + $rateAndAvailability = []; + for ($i = 0; $i < $dateDiff; $i++) { + $date = Carbon::parse($firstDate)->addDays($i)->toDateString(); + + $bestPrice = collect($getPropertyRoomRatePrice) + ->where('date', $date) + ->where('room_rate_mapping.property_room_rate.name', 'Best Available Rate') + ->sortBy('amount')->first(); + $bestPrice = $bestPrice ? $bestPrice['amount'] : null; + + $rateAndAvailability[$date] = [ + 'rate' => round($bestPrice), + 'available' => $bestPrice ? true : false, + ]; + + } + + $responseData = [ + 'currency' => $getChannelProperty['currency']['code'], + 'currency_icon' => $getChannelProperty['currency']['symbol'], + 'date' => $rateAndAvailability + ]; + + + if (count($rateAndAvailability) == collect($rateAndAvailability)->where('available', false)->count()) { + $responseData = null; + } + + Cache::put($cacheKey, $responseData, 60 * 60);//1h 60 * 60 + + break; + + case 'date' : + + $requestParam = [ + 'criteria' => [ + ['field' => 'channel_id', 'condition' => '=', 'value' => 5], + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingEnginePropertyId], + ['field' => 'stop_sell', 'condition' => '=', 'value' => 0], + ['field' => 'date', 'condition' => '=', 'value' => Carbon::parse($params['date'])->format('Y-m-d')], + ['field' => 'amount', 'condition' => '<>', 'value' => null], + ['field' => 'amount', 'condition' => '<>', 'value' => 0], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'roomRateMapping.propertyRoomRate' + ], + 'orderBy' => [ + ['field' => 'date', 'value' => 'ASC'], + ['field' => 'amount', 'value' => 'ASC'] + ], + ]; + + $getPropertyRoomRatePrice = $this->propertyRoomRatePriceService->select($requestParam); + + if ($getPropertyRoomRatePrice['status'] != 'success' || empty($getPropertyRoomRatePrice['data'])) { + throw new ApiErrorException(lang('PropertyRoomRatePrice not found')); + } + + $getPropertyRoomRatePrice = $getPropertyRoomRatePrice['status'] == 'success' && !empty($getPropertyRoomRatePrice['data']) ? $getPropertyRoomRatePrice['data'] : []; + + + $rateAndAvailability = []; + $bestPriceSelect = collect($getPropertyRoomRatePrice) + ->where('date', $params['date']) + ->where('room_rate_mapping.property_room_rate.name', 'Best Available Rate') + ->sortBy('amount')->first(); + + $bestPrice = $bestPriceSelect ? $bestPriceSelect['amount'] : null; + + if ($params['currency'] != $bestPriceSelect['currency']) { + $lastExchangeRate = $this->currencyService->lastExchangeRate($bestPriceSelect['currency'], $params['currency']); + $bestPrice = $bestPrice * $lastExchangeRate['data']; + + } + + $rateAndAvailability[$params['date']] = [ + 'rate' => round($bestPrice), + 'available' => $bestPrice ? true : false, + ]; + + $responseData = [ + 'currency' => $params['currency'], + 'date' => $rateAndAvailability + ]; + + if (count($rateAndAvailability) == collect($rateAndAvailability)->where('available', false)->count()) { + $responseData = null; + } + + Cache::put($cacheKey, $responseData, 60 * 60);//1h 60 * 60 + + break; + + } + + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch + (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + public function propertyCompetitorAnalysis(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $data = null; + $params = $this->params['params']; + + $openAiToken = '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'; + + $client = new \GuzzleHttp\Client([ + 'max' => 5, + 'strict' => false, + 'referer' => false, + 'protocols' => ['https'], + 'timeout' => 30, + 'headers' => [ + 'Authorization' => 'Bearer ' . $openAiToken, + 'Content-Type' => 'application/json', + 'Cache-Control' => 'no-cache', + 'Connection' => 'keep-alive', + 'Accept-Encoding' => 'gzip' + ] + ] + ); + + + $query = [ + 'model' => 'gpt-4o', + 'response_format' => [ + 'type' => 'json_object' + ], + 'messages' => [ + [ + 'role' => 'system', + 'content' => 'Sen bir otel gelir yönetimi (revenue management) uzmanısın. Görev: 1. Verilen fiyatları analiz et. 2. İstenen dilde yorum ve öneri üret. Cevap alanları: {"property": "string", "currency": "string", "average_price": "float", "average_difference": "float (10,2)", "price_position": "string", "competitor_average_price": "float (10,2)", "highest_difference_day": "string (format: d.m.Y)", "highest_difference_percent": "float", "language": "string", "comment": "string", "recommendation": "string"}. price_position değeri higher, lower veya similar olabilir. highest_difference_day ve highest_difference_percent rakiplere göre en yüksek kalan güne göre, format: float(10.2). price_position, average_difference ve competitor_average_price, rakiplerin ortalamalarına göre, format: float. Sadece geçerli JSON döndür, başka açıklama veya metin ekleme.' + ], + [ + 'role' => 'user', + 'content' => json_encode($params) + ] + ] + + ]; + + $result = $client->post('https://api.openai.com/v1/chat/completions', [ + 'body' => json_encode($query) + ]); + + $result = $result->getBody()->getContents(); + $result = json_decode($result, 1); + + $choiceMessage = reset($result['choices']); + + if (isset($choiceMessage['message']['content'])) { + $choiceMessage = json_decode($choiceMessage['message']['content'], 1); + $data = $choiceMessage; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $data]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function promotionAvailable(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + $promotionAvailableData = []; + + try { + + $data = null; + $params = $this->params['params']; + + + //property_channel_coupon + $propertyChannelCouponData = null; + $propertyChannelCouponCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => 1], + ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::parse($params['date'])->toDateString()], + ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::parse($params['date'])->toDateString()], + ['field' => 'is_notify', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'value', 'value' => 'DESC'] + ], + ]; + + $propertyChannelCoupon = $this->propertyChannelCouponService->select($propertyChannelCouponCriteria); + + if ($propertyChannelCoupon['status'] == 'success') { + foreach ($propertyChannelCoupon['data'] as $coupon) { + if (!empty($coupon['reservation_start_date']) && !empty($coupon['reservation_end_date'])) { + if (Carbon::parse($params['date'])->betweenIncluded(Carbon::parse($coupon['reservation_start_date']), Carbon::parse($coupon['reservation_end_date']))) { + $propertyChannelCouponData = [ + 'type' => $coupon['type'], + 'value' => $coupon['value'], + ]; + break; + } else { + continue; + } + } else { + $propertyChannelCouponData = [ + 'type' => $coupon['type'], + 'value' => $coupon['value'], + ]; + } + } + } + $promotionAvailableData['popup'] = $propertyChannelCouponData; + //property_channel_coupon + + + //property_promotion + + + //property_promotion + $propertyPromotionData = null; + $promotionTypes = ['PRD', 'BFD', 'LST', 'DSC']; + $propertyPromotionCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['promotionType'], + 'orderBy' => [ + ['field' => 'promotion_type_id', 'value' => 'ASC'] + ], + ]; + + $propertyPromotions = $this->propertyPromotionService->select($propertyPromotionCriteria); + + $propertyPromotionGroup = null; + if ($propertyPromotions['status'] == 'success') { + + foreach ($propertyPromotions['data'] as $propertyPromotion) { + $propertyPromotionGroup[$propertyPromotion['promotion_type']['type_code']][$propertyPromotion['id']] = $propertyPromotion; + } + + + foreach ($promotionTypes as $promotionType) { + + $propertyPromotionData[$promotionType] = null; + if (isset($propertyPromotionGroup[$promotionType])) { + + switch ($promotionType) { + + case 'PRD': + + $propertyPromotionSelected = collect($propertyPromotionGroup[$promotionType]) + ->where('start_date', '<=', Carbon::parse($params['date'])->toDateString()) + ->where('end_date', '>=', Carbon::parse($params['date'])->toDateString()) + ->where('reservation_start_date', '<=', Carbon::parse($params['date'])->toDateString()) + ->where('reservation_end_date', '>=', Carbon::parse($params['date'])->toDateString()) + ->sortByDesc('amount') + ->first(); + + if ($propertyPromotionSelected) { + + $propertyPromotionData[$promotionType] = [ + 'type' => 'PER', + 'value' => $propertyPromotionSelected['amount'], + 'is_mobile' => $propertyPromotionSelected['is_mobile'], + ]; + + } + + + break; + + case 'BFD' || 'LST': + + + $propertyPromotionSelected = collect($propertyPromotionGroup[$promotionType]) + ->sortByDesc('amount') + ->toArray(); + + if ($propertyPromotionSelected) { + + foreach ($propertyPromotionSelected as $perPromotion) { + + if (Carbon::parse($params['date'])->subDays($perPromotion['day_before'])->lessThanOrEqualTo(Carbon::now())) { + $propertyPromotionData[$promotionType] = [ + 'type' => 'PER', + 'value' => $perPromotion['amount'], + 'is_mobile' => $perPromotion['is_mobile'], + ]; + break; + } + + } + } + + break; + case 'DSC': + + + $propertyPromotionSelected = collect($propertyPromotionGroup[$promotionType]) + ->sortByDesc('amount') + ->first(); + + if ($propertyPromotionSelected) { + $propertyPromotionData[$promotionType] = [ + 'type' => 'PER', + 'value' => $propertyPromotionSelected['amount'], + 'is_mobile' => $propertyPromotionSelected['is_mobile'], + ]; + } + + + break; + + } + + } + + } + + //dd($propertyPromotionGroup); + + $promotionAvailableData['promotion'] = $propertyPromotionData; + + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $promotionAvailableData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} + diff --git a/app/Http/Controllers/V1/CurrencyController.php b/app/Http/Controllers/V1/CurrencyController.php new file mode 100644 index 0000000..ce70cbe --- /dev/null +++ b/app/Http/Controllers/V1/CurrencyController.php @@ -0,0 +1,49 @@ +currencyService = $currencyService; + } + public function getListCurrency(){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $currencyList = $this->currencyService->getCurrencyList(); + + if($currencyList['status'] != 'success'){ + throw new Exception($currencyList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $currencyList['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } +} + diff --git a/app/Http/Controllers/V1/DestinationController.php b/app/Http/Controllers/V1/DestinationController.php new file mode 100644 index 0000000..e16b97e --- /dev/null +++ b/app/Http/Controllers/V1/DestinationController.php @@ -0,0 +1,75 @@ +request = $request; + $this->destinationService = $destinationService; + + } + + + + public function searchDestination(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'search_term' => fillOnUndefined($params, 'search_term'), + 'user_id' => $request->credentials->user_id, + ]; + + $destinationSearch = $this->destinationService->searchDestination($requestParams); + + if($destinationSearch['status'] != 'success'){ + throw new ApiErrorException($destinationSearch['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $destinationSearch['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/EnwContactFormController.php b/app/Http/Controllers/V1/EnwContactFormController.php new file mode 100644 index 0000000..44dd1aa --- /dev/null +++ b/app/Http/Controllers/V1/EnwContactFormController.php @@ -0,0 +1,74 @@ +enwContactFormService = $enwContactFormService; + } + + public function sendEmail(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $language = "en"; + if($request->headers->get('language')){ + $language = $request->headers->get('language'); + app('translator')->setLocale($language); + } + + $params = $request->params; + + $requestParams = [ + 'language' => $language, + 'name' => fillOnUndefined($params, 'name'), + 'surname' => fillOnUndefined($params, 'surname'), + 'email' => fillOnUndefined($params, 'email'), + 'subject' => fillOnUndefined($params, 'subject') + ]; + + $contactForm = $this->enwContactFormService->sendEmail($requestParams); + + if ($contactForm['status'] != 'success') { + throw new ApiErrorException($contactForm['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $contactForm['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + +} diff --git a/app/Http/Controllers/V1/ExportPdfController.php b/app/Http/Controllers/V1/ExportPdfController.php new file mode 100644 index 0000000..8fa698f --- /dev/null +++ b/app/Http/Controllers/V1/ExportPdfController.php @@ -0,0 +1,741 @@ +pdf = $pdf; + $this->pdfContentService = $pdfContentService; + $this->propertyRoomService = $propertyRoomService; + $this->mailer = $mailer; + + } + + + public function pdf(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(), 1); + $params = current($params); + + + if (isset($params['dataExport']) && $params['dataExport'] == 'json') { + + $pdfDataRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id', null), + 'locale' => app('translator')->getLocale() + ]; + $factSheetDataResponse = $this->pdfContentService->factSheetData($pdfDataRequest); + if ($factSheetDataResponse['status'] != 'success') { + throw new Exception($factSheetDataResponse['message']); + } + $factSheetData = $factSheetDataResponse['data']; + + return apiResponse(1, null, $factSheetData, 200); + } + + + if (isset($params['dataExport']) && $params['dataExport'] == 'catalog') { + + /*$pdfDataRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id', null), + 'locale' => fillOnUndefined($params, 'language', 'en'), + ]; + $factSheetDataResponse = $this->pdfContentService->factSheetData($pdfDataRequest); + if ($factSheetDataResponse['status'] != 'success') { + throw new Exception($factSheetDataResponse['message']); + } + + $pageContent = $factSheetDataResponse['data']; + $pdfLanguage = $pdfDataRequest['locale']; + + app('translator')->setLocale($pdfLanguage); + $pdfService = $this->pdf->loadView('pdf.propertyCatalog', compact('pageContent'), [], 'UTF8'); + //$pdfService->setOptions(['dpi' => 100, 'isHtml5ParserEnabled' => true, 'isRemoteEnabled' => true]); + $hotelName = Str::slug($pageContent['name'], '_', 'en') . '_' . $pdfLanguage; + + return $pdfService->download($hotelName . '.pdf');*/ + + + dispatch(new PropertyCatalogServiceJob( + fillOnUndefined($params, 'property_id', null), + fillOnUndefined($params, 'language', app('translator')->getLocale()), + fillOnUndefined($params, 'email', null) + )); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + $pdfDataRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id', null), + 'locale' => app('translator')->getLocale() + ]; + $pdfDataResponse = $this->pdfContentService->pdfData($pdfDataRequest); + if ($pdfDataResponse['status'] != 'success') { + throw new Exception($pdfDataResponse['message']); + } + + $pdfData = $pdfDataResponse['data']; + $pdfLanguage = upperCase(app('translator')->getLocale()); + + $logoBase64 = null; + if ($pdfData['property_brand']) { + if ($pdfData['property_brand']['logo_name']) { + $logoFile = Config::get('app.imageUrl') . '/property-photos/' . $pdfData['property_brand']['property_id'] . '/logo/' . $pdfData['property_brand']['logo_name'] . '_250x250.' . $pdfData['property_brand']['logo_file_ext']; + $logoBase64 = 'data:image/' . $pdfData['property_brand']['logo_file_ext'] . ';base64,' . base64_encode(file_get_contents($logoFile)); + } + } + + $ip = $request->ip(); + + $pdf = $this->pdf->loadView('pdf.hotelContent', compact('pdfData', 'logoBase64', 'ip'), [], 'UTF8'); + $hotelName = Str::slug($pdfData['name'], '_', 'en') . '_' . $pdfLanguage; + + return $pdf->download($hotelName . '.pdf'); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (\Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + // return $pdf->stream(); + } + + public function getPropertyPdfInventory(Request $request, $token) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $requestParams = [ + 'token' => $token + ]; + + $params = $this->propertyRoomService->getParamsForPdfInvenyory($requestParams); + $params = $params['data']; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + + ]; + + $inventoryData = $this->propertyRoomService->getPropertyRoomInventory($requestParams); + if ($inventoryData['status'] != 'success') { + throw new ApiErrorException($inventoryData['message']); + } + + $data = $inventoryData['data']; + + foreach ($data as $dataKey => $dataVal) { + + + //room_availability + $i = 0; + $monthKey = null; + foreach ($dataVal['room_availability'] as $key => $val) { + + $keyArr = explode('-', $key); + if ($monthKey != $keyArr[1]) { + $monthKey = $keyArr[1]; + $i++; + } + + if ($i > 6) { + continue; + } + + $data[$dataKey]['monthly_room_availability'][$i][$key] = $val; + } + + //room_stop_sell + $i = 0; + $monthKey = null; + foreach ($dataVal['room_stop_sell'] as $key => $val) { + + $keyArr = explode('-', $key); + if ($monthKey != $keyArr[1]) { + $monthKey = $keyArr[1]; + $i++; + } + + if ($i > 6) { + continue; + } + + $data[$dataKey]['monthly_room_stop_sell'][$i][$key] = $val; + } + + + foreach ($dataVal['property_room_rate_mapping'] as $propertyRoomRateMappingKey => $propertyRoomRateMappingVal) { + + foreach ($propertyRoomRateMappingVal['prices'] as $pricesKey => $pricesVal) { + + //price + $i = 0; + $monthKey = null; + foreach ($pricesVal['price'] as $key => $val) { + + $keyArr = explode('-', $key); + if ($monthKey != $keyArr[1]) { + $monthKey = $keyArr[1]; + $i++; + } + + if ($i > 6) { + continue; + } + + $data[$dataKey]['property_room_rate_mapping'][$propertyRoomRateMappingKey]['prices'][$pricesKey]['monthly_price'][$i][$key] = $val; + } + + //stop_sell + $i = 0; + $monthKey = null; + foreach ($pricesVal['stop_sell'] as $key => $val) { + + $keyArr = explode('-', $key); + if ($monthKey != $keyArr[1]) { + $monthKey = $keyArr[1]; + $i++; + } + + if ($i > 6) { + continue; + } + + $data[$dataKey]['property_room_rate_mapping'][$propertyRoomRateMappingKey]['prices'][$pricesKey]['monthly_stop_sell'][$i][$key] = $val; + } + + //min_stay + $i = 0; + $monthKey = null; + foreach ($pricesVal['min_stay'] as $key => $val) { + + $keyArr = explode('-', $key); + if ($monthKey != $keyArr[1]) { + $monthKey = $keyArr[1]; + $i++; + } + + if ($i > 6) { + continue; + } + + $data[$dataKey]['property_room_rate_mapping'][$propertyRoomRateMappingKey]['prices'][$pricesKey]['monthly_min_stay'][$i][$key] = $val; + } + + + } + + } + } + + $propertyDataRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id', null) + ]; + $propertyDataResponse = $this->pdfContentService->propertyBaseData($propertyDataRequest); + if ($propertyDataResponse['status'] != 'success') { + throw new Exception($propertyDataResponse['message']); + } + + $propertyData = $propertyDataResponse['data']; + + $logoBase64 = null; + if ($propertyData['property_brand']) { + if ($propertyData['property_brand']['logo_name']) { + $logoFile = Config::get('app.imageUrl') . '/property-photos/' . $propertyData['property_brand']['property_id'] . '/logo/' . $propertyData['property_brand']['logo_name'] . '_250x250.' . $propertyData['property_brand']['logo_file_ext']; + $logoBase64 = 'data:image/' . $propertyData['property_brand']['logo_file_ext'] . ';base64,' . base64_encode(file_get_contents($logoFile)); + } + } + + /*$pdf = $this->pdf->loadView('pdf.inventory', compact('data','propertyData','logoFile','logoBase64'), [], 'UTF8' )->setPaper('a4', 'landscape'); + $pdfLanguage = upperCase(app('translator')->getLocale()); + $fileName = Str::slug('inventory', '_','en').'_'.$pdfLanguage;*/ + + return view('pdf.inventory', compact('data', 'propertyData', 'logoFile', 'logoBase64')); + /*return $pdf->download($fileName.'.pdf');*/ + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + + public function propertyProductOffer(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(), 1); + $params = current($params); + + if (fillOnUndefined($params, 'version') == 'v2') { + + $offerKey = []; + $offerKey[] = $params['propertyName']; + $offerKey[] = $params['executiveName']; + $offerKey[] = $params['executiveEmail']; + $offerKey[] = $params['executivePhone']; + $offerKey[] = $params['accountManager']; + $offerKey[] = hash('sha256', json_encode($params['detail'], JSON_UNESCAPED_UNICODE)); + + $offerKey = md5(implode('-', $offerKey)); + + } else { + $offerKey = md5($params['propertyName'] . '-' . $params['executiveName'] . '-' . $params['package'] . '-' . $params['numberOfRooms'] . '-' . $params['promotionCode']); + + if ($offerKey != fillOnUndefined($params, 'hashKey')) { + throw new ApiErrorException('HASH verification error!'); + } + } + + + $createParam = [ + 'offer_key' => $offerKey, + 'property_name' => $params['propertyName'], + 'executive_name' => $params['executiveName'], + 'executive_email' => $params['executiveEmail'], + 'executive_phone' => $params['executivePhone'], + 'account_manager_name' => fillOnUndefined($params, 'accountManagerName'), + 'account_manager_email' => fillOnUndefined($params, 'accountManagerEmail'), + 'confirm' => fillOnUndefined($params, 'confirm'), + 'offer_date' => Carbon::now()->toDateTimeString(), + 'offer_expire_date' => Carbon::now()->addDays(2)->toDateTimeString(), + 'package' => $params['package'], + 'promotion_code' => $params['promotionCode'], + 'detail' => fillOnUndefined($params, 'detail') ? json_encode($params['detail']) : json_encode($params), + 'version' => fillOnUndefined($params, 'version', 'v1'), + 'ip_address' => $params['ipAddress'], + 'status' => 1, + 'created_at' => time(), + 'updated_at' => time() + ]; + + + $propertyProductOfferCheck = PropertyProductOffer::where('offer_key', $offerKey)->first(); + if ($propertyProductOfferCheck) { + $propertyProductOfferSync = PropertyProductOffer::where('offer_key', $offerKey)->update($createParam); + } else { + $propertyProductOfferSync = PropertyProductOffer::insert($createParam); + } + + if (!$propertyProductOfferSync) { + throw new ApiErrorException('Your offer could not be generated. Please try again.'); + } + + if(fillOnUndefined($params, 'sendEmail')) { + $propertyProductOfferMail = ['offerKey' => $offerKey]; + $this->mailer->onQueue('propertyProductOfferMail', new PropertyProductOfferMail($propertyProductOfferMail)); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['offer_key' => $offerKey]]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + //$response['message'] = $e->getMessage(); + $response['message'] = 'Your offer could not be generated. Please try again.'; + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + + public function propertyProductOfferExport(Request $request, $offerKey) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + $package = [ + 'LRG' => [ + 'commission' => 15, + 'minFee' => 2, + 'name' => 'Paket A', + 'creditRange' => [ + '25-50' => [ + 'min' => 25, + 'max' => 50, + 'monthly' => 250, + 'annually' => 3000, + ], + '51-75' => [ + 'min' => 51, + 'max' => 75, + 'monthly' => 500, + 'annually' => 6000, + ], + '76-100' => [ + 'min' => 76, + 'max' => 100, + 'monthly' => 750, + 'annually' => 9000, + ], + '101-150' => [ + 'min' => 101, + 'max' => 150, + 'monthly' => 1000, + 'annually' => 12000, + ], + '151-200' => [ + 'min' => 151, + 'max' => 200, + 'monthly' => 1500, + 'annually' => 18000, + ], + '201-250' => [ + 'min' => 201, + 'max' => 250, + 'monthly' => 2000, + 'annually' => 24000, + ], + '250+' => [ + 'min' => 251, + 'max' => null, + 'monthly' => 2500, + 'annually' => 30000, + ], + ] + ], + 'MED' => [ + 'commission' => 10, + 'minFee' => 3, + 'name' => 'Paket B', + 'creditRange' => [ + '25-50' => [ + 'min' => 25, + 'max' => 50, + 'monthly' => 375, + 'annually' => 4500, + ], + '51-75' => [ + 'min' => 51, + 'max' => 75, + 'monthly' => 750, + 'annually' => 9000, + ], + '76-100' => [ + 'min' => 76, + 'max' => 100, + 'monthly' => 1125, + 'annually' => 13500, + ], + '101-150' => [ + 'min' => 101, + 'max' => 150, + 'monthly' => 1500, + 'annually' => 18000, + ], + '151-200' => [ + 'min' => 151, + 'max' => 200, + 'monthly' => 2250, + 'annually' => 27000, + ], + '201-250' => [ + 'min' => 201, + 'max' => 250, + 'monthly' => 3000, + 'annually' => 36000, + ], + '250+' => [ + 'min' => 251, + 'max' => null, + 'monthly' => 3750, + 'annually' => 45000, + ], + ] + ], + 'SML' => [ + 'commission' => 5, + 'minFee' => 4, + 'name' => 'Paket C', + 'creditRange' => [ + '25-50' => [ + 'min' => 25, + 'max' => 50, + 'monthly' => 750, + 'annually' => 9000, + ], + '51-75' => [ + 'min' => 51, + 'max' => 75, + 'monthly' => 1500, + 'annually' => 18000, + ], + '76-100' => [ + 'min' => 76, + 'max' => 100, + 'monthly' => 2250, + 'annually' => 27000, + ], + '101-150' => [ + 'min' => 101, + 'max' => 150, + 'monthly' => 3000, + 'annually' => 36000, + ], + '151-200' => [ + 'min' => 151, + 'max' => 200, + 'monthly' => 4500, + 'annually' => 54000, + ], + '201-250' => [ + 'min' => 201, + 'max' => 250, + 'monthly' => 6000, + 'annually' => 72000, + ], + '250+' => [ + 'min' => 251, + 'max' => null, + 'monthly' => 7500, + 'annually' => 90000, + ], + ], + ] + ]; + + $campaign = [ + 'promotion' => [ + 'code' => [/*'ITF2025','ATF2025','TTI2025'*/], + 'percentage' => 50 + ], + 'oneYearAnnually' => [ + 'percentage' => 10 + ], + 'twoYearAnnually' => [ + 'percentage' => 20 + ] + ]; + + + $userListIds = [904, 941, 1485];//22-41 + + + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + + $userList = User::whereIn('id', $userListIds)->get()->toArray(); + + $offerDetail = PropertyProductOffer::where('offer_key', $offerKey)->first(); + $offerDetail = $offerDetail ? $offerDetail->toArray() : null; + + if (empty($offerDetail)) { + return redirect()->to(config('app.url')); + } + + $pageContent = [ + 'name' => $offerDetail['property_name'], + 'executiveName' => $offerDetail['executive_name'], + 'executiveEmail' => $offerDetail['executive_email'], + 'executivePhone' => $offerDetail['detailArray']['executivePhone'], + 'offerTime' => $offerDetail['offer_date'], + 'offerExpireTime' => $offerDetail['offer_expire_date'], + 'offerTimeFormatted' => $offerDetail['offerTimeFormatted'], + 'offerExpireTimeFormatted' => $offerDetail['offerExpireTimeFormatted'], + 'numberOfRooms' => $offerDetail['detailArray']['numberOfRooms'], + 'package' => $offerDetail['package'], + 'promotionCode' => $offerDetail['promotion_code'], + ]; + + + $pageContent['package'] = !empty($request->get('package')) ? $request->get('package') : $pageContent['package']; + $pageContent['numberOfRooms'] = !empty($request->get('numberOfRooms')) ? $request->get('numberOfRooms') : $pageContent['numberOfRooms']; + + + $creditPackage = []; + if ((int)$pageContent['numberOfRooms'] < 25) { + $creditPackage = $package[$pageContent['package']]['creditRange']['25-50']; + } elseif ((int)$pageContent['numberOfRooms'] > 250) { + $creditPackage = $package[$pageContent['package']]['creditRange']['250+']; + } else { + $creditPackage = collect($package[$pageContent['package']]['creditRange']) + ->where('min', '<=', (int)$pageContent['numberOfRooms']) + ->where('max', '>=', (int)$pageContent['numberOfRooms']) + ->first(); + } + + + $pricing = []; + + $pricing['minFee'] = $package[$pageContent['package']]['minFee']; + $pricing['minFeeBeforeDiscount'] = $package[$pageContent['package']]['minFee']; + + if (in_array($pageContent['promotionCode'], $campaign['promotion']['code'])) { + $pricing['minFee'] = (int)($pricing['minFee'] * (1 - ($campaign['promotion']['percentage'] / 100))); + } + + $pricing['commission'] = $package[$pageContent['package']]['commission']; + $pricing['minFee'] = $pricing['minFee']; + $pricing['minFeeBeforeDiscount'] = $pricing['minFeeBeforeDiscount']; + + + $pricing['monthly']['minFee'] = (int)$pricing['minFee'] * (int)$pageContent['numberOfRooms']; + $pricing['monthly']['minFeeBeforePromotion'] = (int)$pricing['minFeeBeforeDiscount'] * (int)$pageContent['numberOfRooms']; + $pricing['monthly']['commission'] = $package[$pageContent['package']]['commission']; + //$pricing['monthly']['minReservationTotal'] = number_format(((int)$pricing['monthly']['minFee'] * 100) / $package[$pageContent['package']]['commission'], 0, '', '.'); + $pricing['monthly']['minReservationTotal'] = number_format(fillOnUndefined($creditPackage, 'monthly', 0), 0, '', '.'); + + + $pricing['annually']['minFee'] = (int)$pricing['monthly']['minFee'] * 12; + $pricing['annually']['minFeeBeforePromotion'] = (int)$pricing['monthly']['minFeeBeforePromotion'] * 12; + $pricing['annually']['commission'] = $package[$pageContent['package']]['commission']; + + $pricing['annually']['minFeeDiscount'] = (int)$pricing['annually']['minFee'] * (1 - ($campaign['oneYearAnnually']['percentage'] / 100));//%10 + + //$pricing['annually']['minReservationTotal'] = number_format(((int)$pricing['annually']['minFeeDiscount'] * 100) / $package[$pageContent['package']]['commission'], 0, '', '.'); + $pricing['annually']['minReservationTotal'] = number_format(fillOnUndefined($creditPackage, 'annually', 0), 0, '', '.'); + + + if ($pricing['minFee'] == $pricing['minFeeBeforeDiscount']) { + $pricing['monthly']['minFeeBeforePromotion'] = null; + $pricing['annually']['minFeeBeforePromotion'] = null; + } + + $pageContent['packageName'] = $package[$pageContent['package']]['name']; + $pageContent['pricing'] = $pricing; + + + $accountUserDetail = collect($userList)->where('email', $offerDetail['detailArray']['accountManager'])->first(); + $pageContent['accountManagerName'] = fillOnUndefined($accountUserDetail, 'nameSurname'); + $pageContent['accountManagerEmail'] = fillOnUndefined($accountUserDetail, 'email'); + $pageContent['accountManagerPhone'] = fillOnUndefined($accountUserDetail, 'phone'); + + + $pdfService = $this->pdf->loadView('pdf.propertyProductOffer', compact('pageContent'), [], 'UTF8'); + $pdfService->setOptions(['dpi' => 100, 'isHtml5ParserEnabled' => true, 'isRemoteEnabled' => true]); + $hotelName = Str::slug($pageContent['name'], '_', 'en'); + return $pdfService->stream($hotelName . '.pdf'); + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function propertyProductOfferData(Request $request, $offerKey) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + + $userListIds = [904, 941, 1485];//22-41 + + + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + + $userList = User::whereIn('id', $userListIds)->get()->toArray(); + + $offerDetail = PropertyProductOffer::where('offer_key', $offerKey)->first(); + $offerDetail = $offerDetail ? $offerDetail->toArray() : null; + + if (empty($offerDetail)) { + return redirect()->to(config('app.url')); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $offerDetail]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} diff --git a/app/Http/Controllers/V1/LanguageController.php b/app/Http/Controllers/V1/LanguageController.php new file mode 100644 index 0000000..24dbdfe --- /dev/null +++ b/app/Http/Controllers/V1/LanguageController.php @@ -0,0 +1,138 @@ +languageService = $languageService; + $this->languageBaseService = $languageBaseService ; + } + + public function getAllLanguages($param) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500 ]; + try { + + if($param === "all"){ + + $languageCriteria = [ + 'criteria' => [ + ['field' => 'status' , 'condition' => '=', 'value' => 1 ] + ] , + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + + }elseif ($param === "app"){ + + $languageCriteria = [ + 'criteria' => [ + ['field' => 'status' , 'condition' => '=', 'value' => 1 ], + ['field' => 'is_application' , 'condition' => '=', 'value' => 1 ], + ['field' => 'is_published' , 'condition' => '=', 'value' => 1 ] + ] , + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + + }else{ + throw new ApiErrorException(lang('Parameter Error.')); + } + + $languages = $this->languageService->getAllLanguages($languageCriteria, ['id','code','name','status', 'language_key']); + + if ($languages['status'] != 'success') { + throw new ApiErrorException($languages['message']); + } + + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null,'data' => $languages['data'] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function createApplicationLanguageFiles() + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500 ]; + try { + $languageBaseResponse = $this->languageBaseService->createApplicationLanguageFiles() ; + if ($languageBaseResponse['status'] != 'success') { + throw new ApiErrorException($languageBaseResponse['message']); + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null,'data' => null ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getNullDescriptions() + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500 ]; + try { + $applicationLanguages = $this->languageService->getApplicationLanguages(); + $applicationLanguages = $applicationLanguages['data']; + $responseLangTitle = [] ; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangTitle[] = [ + 'language_code' => $langKey, + 'description' => isset($titleLangContents[$langKey]) ? $titleLangContents[$langKey] : null + ]; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null,'data' => $responseLangTitle ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + +} diff --git a/app/Http/Controllers/V1/MyWebContentController.php b/app/Http/Controllers/V1/MyWebContentController.php new file mode 100644 index 0000000..301dd4a --- /dev/null +++ b/app/Http/Controllers/V1/MyWebContentController.php @@ -0,0 +1,1277 @@ +request = $request; + $this->pdf = $pdf; + $this->myWebContentService = $myWebContentService; + $this->pdfContentService = $pdfContentService; + $this->languageService = $languageService; + $this->propertyWebPhotoMappingService = $propertyWebPhotoMappingService; + $this->propertyRoomPhotoMappingService = $propertyRoomPhotoMappingService; + $this->propertyPlaceService = $propertyPlaceService; + $this->propertyWebLogService = $propertyWebLogService; + $this->findCountryCodeService = $findCountryCodeService; + $this->propertyWebMetaService = $propertyWebMetaService; + + $this->languageCode = $request->headers->get('language'); + + } + + + public function webMetaTag($propertyId, $propertyWebMetaId, $code = null) + { + + $webMetaTag = null; + $webMetaTagCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'property_web_meta_id', 'condition' => '=', 'value' => $propertyWebMetaId] + ], + ]; + + if ($code) { + $webMetaTagCriteria['criteria'][] = ['field' => 'code', 'condition' => '=', 'value' => $code]; + } + + $webMetaTagRequest = $this->propertyWebMetaService->selectMapping($webMetaTagCriteria); + $webMetaTagRequest = $webMetaTagRequest['status'] == 'success' && !empty($webMetaTagRequest['status']) ? $webMetaTagRequest['data'] : []; + + foreach ($webMetaTagRequest as $webMetaTagData) { + + $webMetaTag[$webMetaTagData['property_web_meta_tag']['name']] = [ + 'property_web_meta' => $webMetaTagData['property_web_meta']['name'], + 'property_web_meta_tag' => $webMetaTagData['property_web_meta_tag']['name'], + 'content' => collect($webMetaTagData['textArray'])->where('language_code', $this->languageCode)->first()['content'] ?? null + ]; + + } + + return $webMetaTag; + + } + + public function home(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->home($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $agentIp = isset($params['agentInfo']['ip']) ? $params['agentInfo']['ip'] : null; + if ($agentIp !== null) { + // Find Country Code with IP + $ipResponse = $this->findCountryCodeService->findCountryWithIpAddress($agentIp); + if ($ipResponse['status'] !== 'success') { + //throw new ApiErrorException('IP Not Found'); + } + + // Create or Update Web Log + if (!empty($params['agentInfo']) && !empty($ipResponse['data']) && !empty($ipResponse['data']['code']) && $ipResponse['status']) { + + $agentParams = [ + 'web_id' => $params['property_web_id'], + 'ip' => $agentIp, + 'country_code' => $ipResponse['data'] ? $ipResponse['data']['code'] : null, + 'isRobot' => $params['agentInfo']['isRobot'] === true ? $params['agentInfo']['isRobot'] : false, + 'isDesktop' => $params['agentInfo']['isDesktop'] === true ? $params['agentInfo']['isDesktop'] : false, + 'isPhone' => $params['agentInfo']['isPhone'] === true ? $params['agentInfo']['isPhone'] : false + ]; + + if ($agentParams['isRobot'] === false) { + $webLogResponse = $this->propertyWebLogService->createPropertyWebLog($agentParams); + } + + } + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 1); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function aboutUs(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->aboutUs($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 2); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function gallery(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->gallery($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 9); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function rooms(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->rooms($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 17); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function roomDetail(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + if (!isset($params['room_id'])) { + throw new ApiErrorException('room_id required'); + } + + $responseData = $this->myWebContentService->roomDetail($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 18, $params['room_id']); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function contact(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->contact($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 5); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function executiveDetail(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->executiveDetail($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function contactForm(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + + if ($request->headers->get('language')) { + $language = $request->headers->get('language'); + app('translator')->setLocale($language); + } + + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->contactForm($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => $responseData['message'], 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function placeCategoryPlaces(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + if (!isset($params['place_category_id'])) { + throw new ApiErrorException('place_category_id required'); + } + + $responseData = $this->myWebContentService->placeCategoryPlaces($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 12); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPlace(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + if (!isset($params['place_id'])) { + throw new ApiErrorException('place_id required'); + } + + $responseData = $this->myWebContentService->getPlace($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 13, $params['place_id']); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function kvkk(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->kvkk($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 10); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + public function multimedia(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->multimedia($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + + $contentCodeConfirmToken = fillOnUndefined($params, 'contentCodeConfirmToken'); + $contentCodeConfirmTokenCache = Cache::get($contentCodeConfirmToken); + + + $multimedia = []; + if ($responseData['data']['id'] == $contentCodeConfirmTokenCache['propertyId']) { + + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + + + $factSheetParam = [ + 'contentCodeConfirmToken' => fillOnUndefined($params, 'contentCodeConfirmToken'), + 'language' => [] + ]; + + foreach ($availableLanguages['data'] as $availableLanguage) { + $factSheetParam['language'][$availableLanguage['code']] = $availableLanguage; + $factSheetParam['language'][$availableLanguage['code']]['url'] = Config::get('app.url') . '/multimedia/fact/' . $availableLanguage['code'] . '/' . $contentCodeConfirmToken; + } + + + $photoParam = []; + + //Gallery + $propertyWebPhotoMappingCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $responseData['data']['id']] + ], + 'with' => ['propertyPhoto'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + + $propertyWebPhotoMapping = $this->propertyWebPhotoMappingService->select($propertyWebPhotoMappingCriteria); + + if ($propertyWebPhotoMapping['status'] == 'success' && !empty($propertyWebPhotoMapping['data'])) { + + $propertyWebPhotoMapping = $propertyWebPhotoMapping['data']; + $firstPhoto = reset($propertyWebPhotoMapping); + + $photoParam['gallery']['all'] = [ + 'id' => 'all', + 'title' => 'Galeri', + 'language_key' => 'myweb-menu-gallery', + 'url' => Config::get('app.url') . '/multimedia/photo/gallery/all/' . $contentCodeConfirmToken, + 'photoUrl' => $firstPhoto['property_photo']['photoUrl'] + ]; + + } + + + //Room + $propertyRoomPhotoMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $responseData['data']['id']] + ], + 'with' => ['propertyRoomPhoto', 'propertyRoom'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $propertyRoomPhotoMapping = $this->propertyRoomPhotoMappingService->select($propertyRoomPhotoMappingCriteria); + + if ($propertyRoomPhotoMapping['status'] == 'success' && !empty($propertyRoomPhotoMapping['data'])) { + + $propertyRoomPhotoMapping = $propertyRoomPhotoMapping['data']; + $firstPhoto = reset($propertyWebPhotoMapping); + + $photoParam['room']['all'] = [ + 'id' => 'all', + 'title' => 'Tüm Odalar', + 'language_key' => 'api-multimedia-all_room_photos', + 'url' => Config::get('app.url') . '/multimedia/photo/room/all/' . $contentCodeConfirmToken, + 'photoUrl' => $firstPhoto['property_photo']['photoUrl'] + ]; + + + $propertyRoomPhotos = []; + foreach ($propertyRoomPhotoMapping as $propertyRoomPhoto) { + if ($propertyRoomPhoto['property_room']['status'] != 1) { + continue; + } + $propertyRoomPhotos[$propertyRoomPhoto['room_id']]['title'] = $propertyRoomPhoto['property_room']['name']; + $propertyRoomPhotos[$propertyRoomPhoto['room_id']]['photo'][] = $propertyRoomPhoto['property_room_photo']['photoUrl']; + } + + foreach ($propertyRoomPhotos as $propertyRoomId => $propertyRoomPhoto) { + $photoParam['room'][$propertyRoomId] = [ + 'id' => $propertyRoomId, + 'title' => $propertyRoomPhoto['title'], + 'language_key' => $propertyRoomPhoto['title'], + 'url' => Config::get('app.url') . '/multimedia/photo/room/' . $propertyRoomId . '/' . $contentCodeConfirmToken, + 'photoUrl' => reset($propertyRoomPhoto['photo']) + ]; + } + } + + //Place + $propertyPlacePhotoMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $responseData['data']['id']] + ], + 'with' => ['propertyPlacePhoto', 'propertyPlace'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $propertyPlacePhotoMapping = $this->propertyPlaceService->selectPhotoMapping($propertyPlacePhotoMappingCriteria); + + + if ($propertyPlacePhotoMapping['status'] == 'success' && !empty($propertyPlacePhotoMapping['data'])) { + + + $propertyPlacePhotoMapping = $propertyPlacePhotoMapping['data']; + $firstPhoto = reset($propertyPlacePhotoMapping); + + $photoParam['place']['all'] = [ + 'id' => 'all', + 'title' => 'Tüm Mekanlar', + 'language_key' => 'api-multimedia-all_place_photos', + 'url' => Config::get('app.url') . '/multimedia/photo/place/all/' . $contentCodeConfirmToken, + 'photoUrl' => $firstPhoto['property_place_photo']['photoUrl'] + ]; + + + $propertyPlacePhotos = []; + foreach ($propertyPlacePhotoMapping as $propertyPlacePhoto) { + $propertyPlacePhotos[$propertyPlacePhoto['place_id']]['title'] = $propertyPlacePhoto['property_place']['name']; + $propertyPlacePhotos[$propertyPlacePhoto['place_id']]['photo'][] = $propertyPlacePhoto['property_place_photo']['photoUrl']; + } + + foreach ($propertyPlacePhotos as $propertyPlaceId => $propertyPlacePhoto) { + $photoParam['place'][$propertyPlaceId] = [ + 'id' => $propertyRoomId, + 'title' => $propertyPlacePhoto['title'], + 'language_key' => $propertyPlacePhoto['title'], + 'url' => Config::get('app.url') . '/multimedia/photo/place/' . $propertyPlaceId . '/' . $contentCodeConfirmToken, + 'photoUrl' => reset($propertyPlacePhoto['photo']) + ]; + } + } + + + $multimedia = [ + "factSheet" => $factSheetParam, + "photo" => $photoParam + ]; + + } + + $responseData['data']['multimedia'] = $multimedia; + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 11); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function multimediaConfirm(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $contentCodeConfirmToken = null; + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->body['params']; + + $responseData = $this->myWebContentService->multimedia($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + if (empty($responseData['data']['content_code'])) { + throw new ApiErrorException('No security code has yet been created to download multimedia.'); + } + + if ($params['privateToken'] != md5($responseData['data']['content_code'])) { + throw new ApiErrorException('Security code could not be verified'); + } + + $contentCodeConfirmToken = md5($params['privateToken'] . '|' . $params['contentCodeToken']); + + $dataArray = [ + 'propertyId' => $responseData['data']['id'], + 'contentCodeConfirmToken' => $contentCodeConfirmToken + ]; + + Cache::put($contentCodeConfirmToken, $dataArray, 10 * 60); //10 Min, 600 TTL + + unset($dataArray['propertyId']); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $dataArray]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function multimediaFactDownload(Request $request, $language = 'en', $contentCodeConfirmToken) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $contentCodeConfirmTokenCache = Cache::get($contentCodeConfirmToken); + + if (is_null($contentCodeConfirmTokenCache)) { + throw new ApiErrorException('Security code could not be verified'); + } + + $responseData = $this->myWebContentService->multimedia(['property_id' => $contentCodeConfirmTokenCache['propertyId']]); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $pdfDataRequest = [ + 'locale' => $language, + 'property_id' => $contentCodeConfirmTokenCache['propertyId'], + ]; + + $pdfDataResponse = $this->pdfContentService->pdfData($pdfDataRequest); + if ($pdfDataResponse['status'] != 'success') { + throw new Exception($pdfDataResponse['message']); + } + + $pdfData = $pdfDataResponse['data']; + + $logoBase64 = null; + if ($pdfData['property_brand']) { + if ($pdfData['property_brand']['logo_name']) { + $logoFile = Config::get('app.imageUrl') . '/property-photos/' . $pdfData['property_brand']['property_id'] . '/logo/' . $pdfData['property_brand']['logo_name'] . '_250x250.' . $pdfData['property_brand']['logo_file_ext']; + $logoBase64 = 'data:image/' . $pdfData['property_brand']['logo_file_ext'] . ';base64,' . base64_encode(file_get_contents($logoFile)); + } + } + + + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + $availableLanguages = $availableLanguages['status'] == 'success' ? $availableLanguages['data'] : []; + $availableLanguages = pickItemFromArray('code', $availableLanguages); + + if (!in_array($language, $availableLanguages)) { + $language = 'en'; + } + + + app('translator')->setLocale($language); + + $this->pdf->loadView('pdf.hotelContent', compact('pdfData', 'logoBase64'), [], 'UTF8'); + $this->pdf->setOptions(['dpi' => 100, 'isHtml5ParserEnabled' => true, 'isRemoteEnabled' => true]); + + return $this->pdf->stream()->header('Content-Type', 'application/pdf'); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function multimediaPhotoDownload(Request $request, $type, $id, $contentCodeConfirmToken) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $contentCodeConfirmTokenCache = Cache::get($contentCodeConfirmToken); + + if (is_null($contentCodeConfirmTokenCache)) { + throw new ApiErrorException('Security code could not be verified'); + } + + $downloadPhotoIds = []; + + switch ($type) { + case 'gallery': + + $photoMappingCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $contentCodeConfirmTokenCache['propertyId']] + ], + 'with' => ['propertyPhoto'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + + $photoMapping = $this->propertyWebPhotoMappingService->select($photoMappingCriteria); + + if ($photoMapping['status'] == 'success' && !empty($photoMapping['data'])) { + $photoMapping = !empty($photoMapping['data']) ? $photoMapping['data'] : []; + $downloadPhotoIds = collect($photoMapping)->pluck('property_photo.photoUrl.fixed')->toArray(); + } + + + break; + case 'room': + + + $photoMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $contentCodeConfirmTokenCache['propertyId']] + ], + 'with' => ['propertyRoomPhoto', 'propertyRoom'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + if ($id != 'all' && is_numeric($id)) { + $photoMappingCriteria['criteria'][] = ['field' => 'room_id', 'condition' => '=', 'value' => $id]; + } + + $photoMapping = $this->propertyRoomPhotoMappingService->select($photoMappingCriteria); + + if ($photoMapping['status'] == 'success' && !empty($photoMapping['data'])) { + $photoMapping = !empty($photoMapping['data']) ? $photoMapping['data'] : []; + $downloadPhotoIds = collect($photoMapping)->pluck('property_room_photo.photoUrl.fixed')->toArray(); + } + + break; + case 'place': + + $photoMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $contentCodeConfirmTokenCache['propertyId']] + ], + 'with' => ['propertyPlacePhoto', 'propertyPlace'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + + ]; + + if ($id != 'all' && is_numeric($id)) { + $photoMappingCriteria['criteria'][] = ['field' => 'place_id', 'condition' => '=', 'value' => $id]; + } + + $photoMapping = $this->propertyPlaceService->selectPhotoMapping($photoMappingCriteria); + + if ($photoMapping['status'] == 'success' && !empty($photoMapping['data'])) { + $photoMapping = !empty($photoMapping['data']) ? $photoMapping['data'] : []; + $downloadPhotoIds = collect($photoMapping)->pluck('property_place_photo.photoUrl.fixed')->toArray(); + } + + break; + case 'default': + break; + + } + + if (empty($downloadPhotoIds)) { + throw new ApiErrorException('Photo list is empty'); + } + + + $publicDir = public_path(); + $zipFileStoragePath = $publicDir . '/property-zip-files'; + + if (!File::exists($zipFileStoragePath)) { + File::makeDirectory($zipFileStoragePath, 0777, true); + } + + $zipFileName = md5($contentCodeConfirmTokenCache['propertyId'] . '-' . $type . '-' . $id) . '.zip'; + + + $zipArchive = new ZipArchive; + if (file_exists($zipFileStoragePath . '/' . $zipFileName)) { + unlink($zipFileStoragePath . '/' . $zipFileName); + } + + if ($zipArchive->open($zipFileStoragePath . '/' . $zipFileName, ZipArchive::CREATE) === TRUE) { + foreach ($downloadPhotoIds as $downloadPhoto) { + + $downloadPhotoExplode = explode('/', $downloadPhoto); + $photoName = last($downloadPhotoExplode); + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $contentCodeConfirmTokenCache['propertyId'] . '/' . $photoName; + if (file_exists($photoUrlFilePath)) { + $zipArchive->addFile($photoUrlFilePath, $photoName); + } + } + $zipArchive->close(); + } + + $responseData = Config::get('app.zipFilesUrl') . $zipFileName; + + return redirect($responseData); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function content(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->content($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 6); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function contentDetail(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->contentDetail($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + + $responseData['data']['metaTag'] = isset($responseData['data']['property_content']['id']) ? $this->webMetaTag($params['property_id'], 7, $responseData['data']['property_content']['id']) : null; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function policy(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->policy($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 14); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function promotion(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + if ($request->headers->get('language')) { + $params['language'] = $request->headers->get('language'); + } + + $responseData = $this->myWebContentService->promotion($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 16); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + public function affiliateRequestMail(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + + if ($request->headers->get('language')) { + $params['language'] = $request->headers->get('language'); + } + + + $responseData = $this->myWebContentService->affiliateRequestMail($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + public function booking(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData['data'] = null; + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 4); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function facts(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->aboutUs($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 8); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function privacyPolicy(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData = $this->myWebContentService->aboutUs($params); + if ($responseData['status'] != 'success') { + throw new ApiErrorException($responseData['message']); + } + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 15); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function announcement(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->body['params']; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $responseData['data'] = null; + + $responseData['data']['metaTag'] = $this->webMetaTag($params['property_id'], 3); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + +} diff --git a/app/Http/Controllers/V1/PaymentController.php b/app/Http/Controllers/V1/PaymentController.php new file mode 100644 index 0000000..dd09dca --- /dev/null +++ b/app/Http/Controllers/V1/PaymentController.php @@ -0,0 +1,368 @@ +propertyPaymentService = $propertyPaymentService ; + $this->request = $request ; + $this->currencyService = $currencyService ; + $this->bookingPaymentService = $bookingPaymentService ; + + } + + + public function getPaymentTypeList(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $paymentList = $this->propertyPaymentService->getPaymentTypeList($params); + + if($paymentList['status'] != 'success'){ + throw new Exception($paymentList['message']); + } + + $currencyList = $this->currencyService->getCurrencyList(); + + if($currencyList['status'] != 'success'){ + throw new Exception($currencyList['message']); + } + + $currencyList = collect($currencyList['data'])->map(function ($value){ + return [ + 'code' => $value['code'], + 'name' => $value['name'], + 'language_key' => $value['language_key'], + ]; + }) ; + $responseData['payment_types'] = $paymentList['data'] ; + $responseData['currencies'] = $currencyList ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPaymentType(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $paymentList = $this->propertyPaymentService->getPaymentType($params); + + if($paymentList['status'] != 'success'){ + throw new Exception($paymentList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $paymentList['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPaymentMappingList(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $paymentList = $this->propertyPaymentService->getPaymentMappingList($params); + + if($paymentList['status'] != 'success'){ + throw new Exception($paymentList['message']); + } + + $responseData = $paymentList['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function createPaymentMapping(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + $responseData = null ; + $paymentList = $this->propertyPaymentService->createPaymentMapping($params); + + if($paymentList['status'] != 'success'){ + throw new Exception($paymentList['message']); + } + + $responseData = $paymentList['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePaymentMappingStatus(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + $responseData = null ; + $paymentList = $this->propertyPaymentService->updatePaymentMappingStatus($params); + + if($paymentList['status'] != 'success'){ + throw new Exception($paymentList['message']); + } + + $responseData = $paymentList['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function setPaymentMappingDefault(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + $responseData = null ; + $paymentList = $this->propertyPaymentService->setPaymentMappingDefault($params); + + if($paymentList['status'] != 'success'){ + throw new Exception($paymentList['message']); + } + + $responseData = $paymentList['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePaymentMapping(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + $responseData = null ; + $paymentList = $this->propertyPaymentService->updatePaymentMapping($params); + + if($paymentList['status'] != 'success'){ + throw new Exception($paymentList['message']); + } + + $responseData = $paymentList['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePaymentInstallments(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + $responseData = null ; + $paymentList = $this->propertyPaymentService->updateInstallments($params); + + if($paymentList['status'] != 'success'){ + throw new Exception($paymentList['message']); + } + + $responseData = $paymentList['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePaymentInstallmentStatus(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + $responseData = null ; + $paymentList = $this->propertyPaymentService->updatePaymentInstallmentStatus($params); + + if($paymentList['status'] != 'success'){ + throw new Exception($paymentList['message']); + } + + $responseData = $paymentList['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function paymentDashboard(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $paymentDashboard = $this->bookingPaymentService->getPaymentDashboard($params); + if($paymentDashboard['status'] != "success"){ + throw new ApiErrorException($paymentDashboard['message']); + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $paymentDashboard['data']]; + } catch (ApiErrorException $e) { + $responseData['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $responseData['message'] = $e->getMessage(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + + } +} diff --git a/app/Http/Controllers/V1/ProductController.php b/app/Http/Controllers/V1/ProductController.php new file mode 100644 index 0000000..a5a483d --- /dev/null +++ b/app/Http/Controllers/V1/ProductController.php @@ -0,0 +1,105 @@ +request = $request; + $this->productService = $productService; + } + + public function getPropertyProducts(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $propertyProducts = $this->productService->getPropertyProducts($requestParams); + if($propertyProducts['status'] != 'success'){ + throw new ApiErrorException($propertyProducts['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyProducts['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function propertyProductActivate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'product_id' => fillOnUndefined($params, 'product_id'), + ]; + + $propertyProducts = $this->productService->propertyProductActivate($requestParams); + if($propertyProducts['status'] != 'success'){ + throw new ApiErrorException($propertyProducts['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyProducts['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + +} diff --git a/app/Http/Controllers/V1/PropertyAdditionalInfoController.php b/app/Http/Controllers/V1/PropertyAdditionalInfoController.php new file mode 100644 index 0000000..73c0ea6 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyAdditionalInfoController.php @@ -0,0 +1,131 @@ +request = $request; + $this->propertyAdditionalInfoService = $propertyAdditionalInfoService; + } + + public function getPropertyAdditionalInfo() + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + + try { + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + //TODO : Validataion eklenecek. Eklendikten sonra if kaldırılması gerekiyor + if (!isset($params['locale']) || empty($params['locale'])) { + throw new ApiErrorException(lang('Location is a required field')); + } + + $getPropertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo($params); + $getPropertyAdditionalInfo = $getPropertyAdditionalInfo ? $getPropertyAdditionalInfo : null; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $getPropertyAdditionalInfo]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function insertPropertyAdditionalInfo() + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + + try { + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + + //TODO : Validataion eklenecek ( benzersiz alanları unutma!!!). Eklendikten sonra if'in kaldırılması gerekiyor. + if (!isset($params['data'][0]['additional_info_key_id']) || empty($params['data'][0]['additional_info_key_id'])) { + throw new ApiErrorException(lang('Location is a required field')); + } + + + + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePropertyAdditionalInfo() + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + + try { + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + + //TODO : Validataion eklenecek ( benzersiz alanları unutma!!!). Eklendikten sonra if'in kaldırılması gerekiyor. + if (!isset(current($params['data'])['additional_info_key_id']) || empty(current($params['data'])['additional_info_key_id'])) { + throw new ApiErrorException(lang('Location is a required field')); + } + + $response = $this->propertyAdditionalInfoService->updatePropertyAdditionalInfo($params); + + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + +} diff --git a/app/Http/Controllers/V1/PropertyAddonController.php b/app/Http/Controllers/V1/PropertyAddonController.php new file mode 100644 index 0000000..c41827b --- /dev/null +++ b/app/Http/Controllers/V1/PropertyAddonController.php @@ -0,0 +1,349 @@ +params = Input::all(); + $this->params = $this->params['params']; + $this->propertyAddonService = $propertyAddonService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyAddonValidator = $propertyAddonValidator; + $this->languageService = $languageService; + } + + protected function propertyChannelMappingCheck($propertyId, $channelId) + { + + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] == 'success') { + if (empty($propertyChannelMapping['data'])) { + return false; + } else { + return true; + } + } else { + return false; + } + + } + + public function getPropertyAddon(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + //TODO: Validator + + $propertyChannelMappingCheck = $this->propertyChannelMappingCheck($this->params['property_id'], $this->params['channel_id']); + if (!$propertyChannelMappingCheck) { + throw new ApiErrorException('Transactions cannot be made through a unconnected channel.'); + } + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['channel_id']], + ['field' => 'status', 'condition' => '!=', 'value' => 3], + ], + //'with' => ['propertyAddon'] + ]; + $columns = ['id', 'property_id', 'channel_id', 'property_addon_id', 'title', 'description', 'amount', 'type', 'min_stay','status']; + $requestSelectResult = $this->propertyAddonService->selectPropertyChannelAddon($requestSelectCriteria, $columns); + + $propertyChannelAddon = []; + if ($requestSelectResult['status'] == 'success') { + $propertyChannelAddon = $requestSelectResult['data']; + } + + $propertyChannelAddonCollect = collect($propertyChannelAddon); + + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['fact'], + 'orderBy' => [ + ['field' => 'order_number', 'value' => 'ASC'] + ], + ]; + + $columns = ['id', 'fact_id', 'title', 'attribute', 'order_number']; + $propertyAddonResult = $this->propertyAddonService->selectPropertyAddon($requestSelectCriteria, $columns); + + $propertyAddon = []; + if ($requestSelectResult['status'] == 'success') { + $propertyAddon = $propertyAddonResult['data']; + } + + $getApplicationLanguages = $this->languageService->getApplicationLanguages(); + if ($getApplicationLanguages['status'] != 'success') { + throw new ApiErrorException($getApplicationLanguages['message']); + } + $applicationLanguages = $getApplicationLanguages['data']; + + $propertyAddonList = []; + foreach ($propertyAddon as $addon) { + + $channelAddon = []; + $channelAddons = $propertyChannelAddonCollect->where('property_addon_id', $addon['id'])->toArray(); + + $isSelected = false; + if ($channelAddons) { + $channelAddons = array_values($channelAddons); + + foreach ($channelAddons as $channelAddonItem) { + + $responseLangDescription = []; + $descriptionLangContents = json_decode($channelAddonItem['description'], 1); + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangDescription[] = [ + 'language_code' => $langKey, + 'description' => isset($descriptionLangContents[$langKey]) ? $descriptionLangContents[$langKey] : null + ]; + } + + $channelAddonItem['description'] = $responseLangDescription; + $channelAddon[] = $channelAddonItem; + + } + + $isSelected = true; + + } else { + + $responseLangDescription = []; + foreach ($applicationLanguages as $applicationLanguage) { + $langKey = $applicationLanguage['code']; + $responseLangDescription[] = [ + 'language_code' => $langKey, + 'description' => null + ]; + } + + $channelAddon[] = [ + 'property_id' => $this->params['property_id'], + 'channel_id' => $this->params['channel_id'], + 'property_addon_id' => $addon['id'], + 'title' => '', + 'description' => $responseLangDescription, + 'amount' => '', + 'type' => '', + 'min_stay' => null, + 'status' => 1 + ]; + } + + $propertyAddonList[] = [ + 'id' => $addon['id'], + 'fact_id' => $addon['fact_id'], + 'name' => $addon['fact']['name'], + 'language_key' => $addon['fact']['language_key'], + 'icon' => $addon['fact']['icon'], + 'attributeArray' => $addon['attributeArray'], + 'is_selected' => $isSelected, + 'channelAddon' => $channelAddon + ]; + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyAddonList]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function syncPropertyAddon(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + /*$validationResult = $this->propertyAddonValidator->validate($this->params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + }*/ + + $propertyChannelMappingCheck = $this->propertyChannelMappingCheck($this->params['property_id'], $this->params['channel_id']); + if (!$propertyChannelMappingCheck) { + throw new ApiErrorException('Transactions cannot be made through a unconnected channel.'); + } + + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['channel_id']], + ['field' => 'property_addon_id', 'condition' => '=', 'value' => $this->params['property_addon_id']], + ] + ]; + $columns = ['id', 'property_id', 'channel_id', 'property_addon_id', 'amount', 'type']; + $requestSelectResult = $this->propertyAddonService->selectPropertyChannelAddon($requestSelectCriteria, $columns); + + $propertyChannelAddon = []; + if ($requestSelectResult['status'] == 'success') { + $propertyChannelAddon = $requestSelectResult['data']; + } + + $currentPropertyChannelAddon = $propertyChannelAddon; + $propertyChannelAddon = collect($propertyChannelAddon); + + DB::beginTransaction(); + + //Status Delete All ChannelAddon + foreach ($currentPropertyChannelAddon as $channelAddon) { + $updateParam = [ + 'property_id' => $channelAddon['property_id'], + 'channel_id' => $channelAddon['channel_id'], + 'property_addon_id' => $channelAddon['property_addon_id'], + 'status' => 3, + 'updated_by' => $request->auth->id, + ]; + $this->propertyAddonService->updatePropertyChannelAddon($channelAddon['id'], $updateParam); + } + //Status Delete All ChannelAddon + + + $channelAddonProcessed = []; + foreach ($this->params['channelAddon'] as $channelAddon) { + + + $description = []; + foreach (fillOnUndefined($channelAddon, "description", []) as $title) { + $description[$title['language_code']] = $title['description']; + } + + if (!isset($channelAddon['id'])) { + + $createParam = [ + 'property_id' => $this->params['property_id'], + 'channel_id' => $this->params['channel_id'], + 'property_addon_id' => $this->params['property_addon_id'], + 'title' => fillOnUndefined($channelAddon, 'title'), + 'description' => json_encode($description), + 'amount' => $channelAddon['amount'], + 'type' => $channelAddon['type'], + 'min_stay' => fillOnUndefined($channelAddon,'min_stay'), + 'status' => fillOnUndefined($channelAddon, 'status', 1) == 1 ? 1 : 0, + 'created_by' => $request->auth->id, + 'updated_by' => $request->auth->id, + ]; + + $syncPropertyAddon = $this->propertyAddonService->createPropertyChannelAddon($createParam); + + if ($syncPropertyAddon['status'] != 'success') { + throw new ApiErrorException($syncPropertyAddon['message']); + } + + $channelAddonProcessed[] = $syncPropertyAddon['data']; + + } elseif (isset($channelAddon['id']) && $propertyChannelAddon->where('id', $channelAddon['id'])->isNotEmpty()) { + + $updateParam = [ + 'property_id' => $this->params['property_id'], + 'channel_id' => $this->params['channel_id'], + 'property_addon_id' => $this->params['property_addon_id'], + 'title' => fillOnUndefined($channelAddon, 'title'), + 'description' => json_encode($description), + 'amount' => $channelAddon['amount'], + 'type' => $channelAddon['type'], + 'min_stay' => fillOnUndefined($channelAddon,'min_stay'), + 'status' => fillOnUndefined($channelAddon, 'status', 1) == 1 ? 1 : 0, + 'updated_by' => $request->auth->id, + ]; + + $syncPropertyAddon = $this->propertyAddonService->updatePropertyChannelAddon($channelAddon['id'], $updateParam); + + if ($syncPropertyAddon['status'] != 'success') { + throw new ApiErrorException($syncPropertyAddon['message']); + } + + $channelAddonProcessed[] = $syncPropertyAddon['data']; + } + + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $channelAddonProcessed]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} + diff --git a/app/Http/Controllers/V1/PropertyAwardCertificatesController.php b/app/Http/Controllers/V1/PropertyAwardCertificatesController.php new file mode 100644 index 0000000..5834011 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyAwardCertificatesController.php @@ -0,0 +1,256 @@ +request = $request ; + $this->propertyAwardsCertificateService = $propertyAwardsCertificateService ; + + } + + + public function getAwardCertificateCategories( ){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $getPropertyAwardsCertificateList = $this->propertyAwardsCertificateService->getCategoryList($params); + + if($getPropertyAwardsCertificateList['status'] != 'success'){ + throw new Exception($getPropertyAwardsCertificateList['message']); + } + + $responseData['award_certificate_categories'] = $getPropertyAwardsCertificateList['data'] ; + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function createAwardsCertificates(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $params = + [ + "property_id" => $request->input('property_id'), + "category_id" => $request->input('category_id'), + "language_code" => $request->input('language_code'), + "name" => $request->input('name'), + "date" => $request->input('date'), + "url" => $request->input('url'), + "file" => $request->file('file'), + ]; + + + $requestParams = $params; + $requestParams['user_id'] = $this->request->auth->id; + + + $storeAwardsCertificate = $this->propertyAwardsCertificateService->create($requestParams); + if ($storeAwardsCertificate['status'] != 'success') { + throw new ApiErrorException($storeAwardsCertificate['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $storeAwardsCertificate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function listAwardsCertificates(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + + $params['user_id'] = $this->request->auth->id; + + + $storeAwardsCertificate = $this->propertyAwardsCertificateService->listAwardsCertificates($params); + if ($storeAwardsCertificate['status'] != 'success') { + throw new ApiErrorException($storeAwardsCertificate['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $storeAwardsCertificate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function updateAwardsCertificates(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $params = + [ + "award_certificate_id" => $request->input('award_certificate_id'), + "property_id" => $request->input('property_id'), + "category_id" => $request->input('category_id'), + "language_code" => $request->input('language_code'), + "name" => $request->input('name'), + "date" => $request->input('date'), + "url" => $request->input('url'), + "file" => $request->file('file'), + ]; + + + $requestParams = $params; + $requestParams['user_id'] = $this->request->auth->id; + + + $storeAwardsCertificate = $this->propertyAwardsCertificateService->update($requestParams); + if ($storeAwardsCertificate['status'] != 'success') { + throw new ApiErrorException($storeAwardsCertificate['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $storeAwardsCertificate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function deletePhotoAwardsCertificates(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + + $params['user_id'] = $this->request->auth->id; + + + $storeAwardsCertificate = $this->propertyAwardsCertificateService->deletePhotoAwardsCertificates($params); + if ($storeAwardsCertificate['status'] != 'success') { + throw new ApiErrorException($storeAwardsCertificate['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $storeAwardsCertificate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function updateStatusAwardsCertificates(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + + $params['user_id'] = $this->request->auth->id; + + + $storeAwardsCertificate = $this->propertyAwardsCertificateService->updateStatus($params); + if ($storeAwardsCertificate['status'] != 'success') { + throw new ApiErrorException($storeAwardsCertificate['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $storeAwardsCertificate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} diff --git a/app/Http/Controllers/V1/PropertyBookingController.php b/app/Http/Controllers/V1/PropertyBookingController.php new file mode 100644 index 0000000..912ac1f --- /dev/null +++ b/app/Http/Controllers/V1/PropertyBookingController.php @@ -0,0 +1,888 @@ +request = $request; + $this->mailer = $mailer; + $this->bookingService = $bookingService; + $this->bookingRoomService = $bookingRoomService; + $this->propertyBookingEngineService = $propertyBookingEngineService; + $this->bookingTicketService = $bookingTicketService; + $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService; + $this->channelManagerBookingService = $channelManagerBookingService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->bookingUpdateValidator = $bookingUpdateValidator; + $this->newBookingMailService = $newBookingMailService; + } + + public function getPropertyBookingList(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $getPropertyBooking = $this->bookingService->getBookingList($params); + if ($getPropertyBooking['status'] != "success") { + throw new ApiErrorException($getPropertyBooking['message']); + } + + if (isset($params['excelExport']) && $params['excelExport']) { + + + foreach ($getPropertyBooking['data'] as $bookingKey => $booking) { + + $dataTableData[$bookingKey]['booking_channel'] = $booking['booking_channel']['name']; + $dataTableData[$bookingKey]['booking_code'] = $booking['booking_code']; + $dataTableData[$bookingKey]['name_surname'] = $booking['booking_contact']['nameSurname']; + $dataTableData[$bookingKey]['checkin_date'] = $booking['checkin_date']; + $dataTableData[$bookingKey]['checkout_date'] = $booking['checkout_date']; + $dataTableData[$bookingKey]['payment_type'] = $booking['booking_payment_type']['name']; + $dataTableData[$bookingKey]['total'] = $booking['total']; + $dataTableData[$bookingKey]['currency_code'] = $booking['currency_code']; + $dataTableData[$bookingKey]['status'] = $booking['booking_status']['name']; + $dataTableData[$bookingKey]['reservation_time'] = Carbon::createFromTimestamp($booking['reservation_time'])->toDateTimeString(); + + } + + + $fileNameHash = [ + 'property_id' => $params['property_id'], + 'channel_id' => fillOnUndefined($params['filter'], 'channel_id'), + 'booking_code' => fillOnUndefined($params['filter'], 'booking_code'), + 'payment_type_code' => fillOnUndefined($params['filter'], 'payment_type_code'), + 'status' => fillOnUndefined($params['filter'], 'status'), + 'date_type' => fillOnUndefined($params['filter'], 'date_type'), + 'date_range' => fillOnUndefined($params['filter'], 'date_range'), + ]; + + $fileName = 'PropertyBookingList-' . md5(implode('-', $fileNameHash)) . '.xlsx'; + $fileNamePath = 'excel/' . $fileName; + $fileNamePublic = config('app.url') . '/' . $fileNamePath; + + $excelStore = Excel::store(new PropertyBookingListExport($dataTableData), $fileNamePath, 'public'); + + if (!$excelStore) { + throw new ApiErrorException(lang('Mapping data not found')); + } + + $data = [ + 'url' => $fileNamePublic + ]; + + //Delete files older than 1 hours + $excelFileDir = public_path('excel'); + foreach (glob($excelFileDir . '/' . "*") as $file) { + if (filemtime($file) < time() - 3600) { // 6 hours 21600 + unlink($file); + } + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $data]; + + } else { + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['booking' => $getPropertyBooking['data']]]; + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyBookingListFilter(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $getPropertyBookingFilter = $this->bookingService->getPropertyBookingListFilter($params); + if ($getPropertyBookingFilter['status'] != "success") { + throw new ApiErrorException($getPropertyBookingFilter['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $getPropertyBookingFilter['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyBookingDetail(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $bookingDetail = $this->bookingService->getBookingDetail($params); + if ($bookingDetail['status'] != "success") { + throw new ApiErrorException($bookingDetail['message']); + } + + //BOOKING ADDON + $bookingAddons = $bookingDetail['data']['booking_addon']; + unset($bookingDetail['data']['booking_addon']); + + $bookingAddonList = []; + foreach ($bookingAddons as $bookingAddon) { + + $bookingAddonAttributeList = []; + $isHasAttribute = false; + $attributeArray = []; + if (!empty($bookingAddon['property_channel_addon']['property_addon']['attributeArray'])) { + $isHasAttribute = true; + $attributeArray = $bookingAddon['property_channel_addon']['property_addon']['attributeArray']; + $bookingAddonAttributes = json_decode($bookingAddon['attribute'], 1); + if (!is_null($bookingAddonAttributes)) { + foreach ($bookingAddonAttributes as $key => $bookingAddonAttribute) { + foreach ($bookingAddonAttribute as $bookingAddonAttributeKey => $bookingAddonAttributeValue) { + + if (isset($bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey])) { + $bookingAddonAttributeList[] = [ + 'key' => $bookingAddonAttributeKey, + 'name' => $bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey]['name'], + 'language_key' => $bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey]['language_key'], + 'value' => $bookingAddonAttributeValue, + ]; + } + + } + } + } + } + + $bookingAddonList[] = [ + 'id' => $bookingAddon['id'], + 'booking_id' => $bookingAddon['booking_id'], + 'property_channel_addon_id' => $bookingAddon['property_channel_addon_id'], + 'count' => $bookingAddon['count'], + 'amount' => $bookingAddon['amount'], + 'total' => $bookingAddon['total'], + 'min_stay' => $bookingAddon['property_channel_addon']['min_stay'], + 'currency_code' => $bookingAddon['currency_code'], + 'name' => $bookingAddon['property_channel_addon']['property_addon']['fact']['name'], + 'title' => $bookingAddon['property_channel_addon']['title'], + 'language_key' => $bookingAddon['property_channel_addon']['property_addon']['fact']['language_key'], + 'icon' => $bookingAddon['property_channel_addon']['property_addon']['fact']['icon'], + 'isHasAttribute' => $isHasAttribute, + 'attributeArray' => $attributeArray, + 'attribute' => $bookingAddonAttributeList + ]; + + } + + $bookingDetail['data']['booking_addon'] = $bookingAddonList; + //BOOKING ADDON + + //DAILY AMOUNT BY ROOM + foreach ($bookingDetail['data']['booking_room'] as $roomKey => $roomDetail) { + + if (!empty($roomDetail['room_rate_mapping']['property_room'])) { + $bookingDetail['data']['booking_room'][$roomKey]['room_name'] = $roomDetail['room_rate_mapping']['property_room']['name']; + } + + if (!empty($roomDetail['room_rate_mapping']['property_room_rate'])) { + $bookingDetail['data']['booking_room'][$roomKey]['room_rate_name'] = $roomDetail['room_rate_mapping']['property_room_rate']['name']; + } + + $diffInDays = Carbon::parse($roomDetail['checkin_date'])->diffInDays(Carbon::parse($roomDetail['checkout_date'])); + $baseRateDaily = moneyDoubleFormatDecimal($roomDetail['total'] / $diffInDays); + + $roomDailyAmount = []; + + + if (!empty($roomDetail['daily_amount'])) { + $roomDetailDailyAmount = json_decode($roomDetail['daily_amount'], 1); + if (!empty($roomDetailDailyAmount) && isset($roomDetailDailyAmount)) { + foreach ($roomDetailDailyAmount as $roomRateAmount) { + $roomDailyAmount[] = [ + 'date' => $roomRateAmount['date'], + 'amount' => $roomRateAmount['amount'], + 'currency_code' => $roomRateAmount['currency_code'], + ]; + } + } + } + + if (empty($roomDailyAmount)) { + $roomRateDetail = is_array($roomDetail['rate_detail']) ? $roomDetail['rate_detail'] : json_decode($roomDetail['rate_detail'], 1); + if (!empty($roomRateDetail) && isset($roomRateDetail['days'])) { + foreach ($roomRateDetail['days'] as $roomRateDay => $roomRateAmount) { + $roomDailyAmount[] = [ + 'date' => Carbon::parse($roomRateDay)->toDateString(), + 'amount' => $roomRateAmount, + 'currency_code' => $roomDetail['currency_code'], + ]; + } + } + } + + if (empty($roomDailyAmount)) { + $currentDate = $roomDetail['checkin_date']; + for ($i = 0; $i < $diffInDays; $i++) { + $roomDailyAmount[] = [ + 'date' => $currentDate, + 'amount' => $baseRateDaily, + 'currency_code' => $roomDetail['currency_code'], + ]; + $currentDate = Carbon::parse($currentDate)->addDay()->toDateString(); + } + } + + $bookingDetail['data']['booking_room'][$roomKey]['daily_amount'] = $roomDailyAmount; + + $extraParam = null; + if (!empty($roomDetail['extra_param'])) { + $extraParamDecode = json_decode($roomDetail['extra_param'], 1); + + foreach ($extraParamDecode as $extraParamKey => $extraParamValue) { + + $extraParamTitle = explode('_', $extraParamKey); + foreach ($extraParamTitle as $extraParamTitleKey => $extraParamTitleValue) { + $extraParamTitle[$extraParamTitleKey] = ucwords($extraParamTitleValue); + } + $extraParamTitle = implode(' ', $extraParamTitle); + + $extraParam[$extraParamKey] = [ + 'title' => $extraParamTitle, + 'value' => $extraParamValue, + ]; + } + + } + + $bookingDetail['data']['booking_room'][$roomKey]['extra_param'] = $extraParam; + $bookingDetail['data']['booking_room'][$roomKey]['occupancyFormatted'] = occupancyCodeFormatted($roomDetail['occupancy_code']); + + } + //DAILY AMOUNT BY ROOM + + + if (empty($bookingDetail['data']['is_viewed'])) { + $this->bookingService->update($params['booking_id'], ['is_viewed' => 1]); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['booking_detail' => $bookingDetail['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function sendBookingEmail(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $bookingDetail = $this->bookingService->getBookingDetail($params); + if ($bookingDetail['status'] != "success") { + throw new ApiErrorException($bookingDetail['message']); + } + + if (!in_array($bookingDetail['data']['status'], [0,1])) { + throw new ApiErrorException('Only active and canceled reservation emails can be sent'); + } + + if (in_array($bookingDetail['data']['status'], [0])) { + $mailParams = ['booking_id' => $params['booking_id']]; + $this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($mailParams)); + } + + if (in_array($bookingDetail['data']['status'], [1])) { + $mailParams = ['booking_id' => $params['booking_id']]; + $this->newBookingMailService->process($mailParams); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function bookEngineDashboard(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $getPropertyBookingEngine = $this->propertyBookingEngineService->bookEngineDashboard($params); + if ($getPropertyBookingEngine['status'] != "success") { + throw new ApiErrorException($getPropertyBookingEngine['message']); + } + + $responseData['booking_engine'] = $getPropertyBookingEngine['data']; + + + //Cache + $getBookingDetailedListCacheKey = md5('getBookingDetailedList-' . $params['property_id']); + if (Cache::has($getBookingDetailedListCacheKey)) { + $responseDataCache = Cache::get($getBookingDetailedListCacheKey); + + $responseData['all_booking_count'] = $responseDataCache['all_booking_count']; + $responseData['success_booking_count'] = $responseDataCache['success_booking_count']; + $responseData['pre_booking_count'] = $responseDataCache['pre_booking_count']; + $responseData['conversion_rate'] = $responseDataCache['conversion_rate']; + $responseData['total_pax_count'] = $responseDataCache['total_pax_count']; + + } else { + + $getPropertyBooking = $this->bookingService->getBookingDetailedList($params); + if ($getPropertyBooking['status'] != "success") { + throw new ApiErrorException($getPropertyBooking['message']); + } + + $bookings = collect($getPropertyBooking['data']); + $channelBookings = $bookings->where('channel_id', '=', 1); + $getBookingEngineBookings = $channelBookings->all(); + $paxCountArray = $channelBookings->where('status', '=', 1)->map(function ($booking) { + $roomPaxCount = collect($booking['booking_room'])->map(function ($room) { + return collect($room['room_pax'])->count(); + })->values()->first(); + return [ + 'booking_id' => $booking['id'], + 'room_pax_count' => $roomPaxCount, + ]; + + })->values()->all(); + + $totalPaxCount = collect($paxCountArray)->sum('room_pax_count'); + + $allBookingCount = $channelBookings->count(); + $preBookingCount = $channelBookings->where('status', '=', 2)->count(); + $successBookingCount = $channelBookings->where('status', '=', 1)->count(); + + $conversionRate = $allBookingCount > 0 ? ($successBookingCount * 100) / $allBookingCount : 0; + + $responseData['all_booking_count'] = $allBookingCount; + $responseData['success_booking_count'] = $successBookingCount; + $responseData['pre_booking_count'] = $preBookingCount; + $responseData['conversion_rate'] = number_format($conversionRate, 2); + $responseData['total_pax_count'] = $totalPaxCount; + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function bookingEngineContractUpload(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $params = + [ + "property_id" => $request->input('property_id'), + "contract_file" => $request->file('contract_file'), + "user_id" => $this->request->auth->id + ]; + + $uploadResponse = $this->propertyBookingEngineService->contractFileUpload($params); + if ($uploadResponse['status'] != "success") { + throw new ApiErrorException($uploadResponse['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $uploadResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function getPropertyTransactionList(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $getPropertyTransaction = $this->bookingService->getTransactionList($params); + if ($getPropertyTransaction['status'] != "success") { + throw new ApiErrorException($getPropertyTransaction['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['transactions' => $getPropertyTransaction['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updateBooking(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + DB::beginTransaction(); + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + if (!isset($params['property_id'])) { + $params = $this->request->requestParams; + } + + $validationResult = $this->bookingUpdateValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $bookingstatusList = $this->bookingService->getBookingstatusList(); + $bookingstatusListCollect = collect($bookingstatusList['data']); + if ($bookingstatusListCollect->where('id', $params['status'])->count() <= 0) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $bookingRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['booking_id']], + ], + 'with' => ['bookingRoom','propertyBookingEngineSearch'], + 'firstRow' => true + ]; + + $booking = $this->bookingService->select($bookingRequest); + + if ($booking['status'] != 'success') { + throw new ApiErrorException($booking['message']); + } + + if (empty($booking['data'])) { + throw new ApiErrorException('Booking not found.'); + } + + $booking = $booking['data']; + + $isNonRefundable = false; + if($params['status'] == 0) { + foreach ($booking['booking_room'] as $roomDetail) { + $cancellationPolicy = json_decode($roomDetail['cancellation_policy'],1); + if(!empty($cancellationPolicy) && is_array($cancellationPolicy)) { + if(isset($cancellationPolicy['isNonRefundable']) && $cancellationPolicy['isNonRefundable']) { + $isNonRefundable = true; + break; + } + } + } + } + + //Trivago Check + /*if(isset($booking['property_booking_engine_search']['referrer']) && $booking['property_booking_engine_search']['referrer'] == 'trivago:trivago') { + if($isNonRefundable) { + throw new ApiErrorException('Refunds are not permitted for non-refundable transactions received through Trivago.'); + } + }*/ + + + $bookingUpdateParam = []; + $bookingUpdateAvailableColumns = ['total', 'status', 'currency_code']; + foreach ($params as $param => $value) { + if (in_array($param, $bookingUpdateAvailableColumns)) { + $bookingUpdateParam[$param] = $value; + } + } + + $action = null; + $percentage = 1; + + if ($booking['status'] == 0) { + $percentage = 0; + } elseif ($bookingUpdateParam['total'] > $booking['total']) { + $action = 'INC'; + $percentage = ($bookingUpdateParam['total'] - $booking['total']) / $booking['total'] * 100; + } else { + $action = 'DEC'; + $percentage = ($booking['total'] - $bookingUpdateParam['total']) / $booking['total'] * 100; + } + + $bookingUpdate = $this->bookingService->update($booking['id'], $bookingUpdateParam); + + if ($bookingUpdate['status'] != 'success') { + throw new ApiErrorException($booking['message']); + } + + + foreach ($booking['booking_room'] as $room) { + + if (!is_null($action)) { + + $totalAmount = $room['total']; + $affectedAmount = ($room['total'] * $percentage) / 100; + + if ($action == 'INC') { + $totalAmount = $totalAmount + $affectedAmount; + } + if ($action == 'DEC') { + $totalAmount = $totalAmount - $affectedAmount; + } + + $bookingRoomUpdate = $this->bookingRoomService->update($room['id'], ['total' => $totalAmount]); + + if ($bookingUpdate['status'] != 'success') { + throw new ApiErrorException($bookingRoomUpdate['message']); + } + + } + + //Availability Decrease + if (in_array($params['status'], [0, 3]) && $booking['status'] == 1) { + + $dateByDay = []; + $diffInDays = Carbon::parse($room['checkin_date'])->floatDiffInDays(Carbon::parse($room['checkout_date'])); + for ($i = 0; $i < $diffInDays; $i++) { + $dateByDay[] = Carbon::parse($room['checkin_date'])->addDay($i)->format('Y-m-d'); + } + + foreach ($dateByDay as $day) { + + $requestParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $booking['property_id']], + ['field' => 'property_room_id', 'condition' => '=', 'value' => $room['room_id']], + ['field' => 'availability_type_id', 'condition' => '=', 'value' => 1], + ['field' => 'date', 'condition' => '=', 'value' => $day] + ], + 'firstRow' => true, + ]; + + $dateAvailability = $this->propertyRoomAvailabilityService->select($requestParam); + + if ($dateAvailability['status'] == 'success') { + $currentAvailability = $dateAvailability['data']['availability']; + $currentAvailability = $currentAvailability + 1; + $this->propertyRoomAvailabilityService->update($dateAvailability['data']['id'], ['availability' => $currentAvailability]); + } + + } + + + } + + } + + + //PUSH CHANNEL MANAGER QUEUE + //if ($booking['channel_id'] == 1) { + + $type = 'Modify'; + + if (in_array($params['status'], [1, 2, 3])) { + $type = 'Modify'; + } + + if (in_array($params['status'], [0])) { + $type = 'Cancel'; + } + + $channelManagerPropertyMappingCriteria = [ + 'criteria' => + [ + ['field' => 'property_id', 'condition' => '=', 'value' => $booking['property_id']], + ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria); + + if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) { + + foreach ($channelManagerPropertyMapping['data'] as $channelPropertyData) { + + $extraParamDecode = json_decode($booking['extra_param'], 1); + + if($channelPropertyData['channel_manager_id'] == 11 && !isset($extraParamDecode['trv_reference'])) { + continue; + } + + //Yandex + if(in_array($channelPropertyData['channel_manager_id'],[13])) { + continue; + } + + $channelManagerBookingCreateParam = [ + 'property_id' => $booking['property_id'], + 'booking_id' => $booking['id'], + 'channel_manager_id' => $channelPropertyData['channel_manager_id'], + 'type' => $type, + ]; + + $channelManagerBookingCreate = $this->channelManagerBookingService->create($channelManagerBookingCreateParam); + + } + + } + + //} + //PUSH CHANNEL MANAGER QUEUE + + + //Booking Ticket Log + $bookingTicketRequest = [ + 'criteria' => [ + ['field' => 'booking_id', 'condition' => '=', 'value' => $booking['id']], + ], + 'firstRow' => true + ]; + + $bookingTicket = $this->bookingTicketService->select($bookingTicketRequest, ['id']); + + $userId = isset($this->request->auth->id) ? $this->request->auth->id : 1; + + $createBookingTicketParams = []; + if (empty($bookingTicket['data'])) { + + $createBookingTicketParams = [ + 'booking_id' => $booking['id'], + 'code' => getTicketCodeGenerate('TCK'), + 'user_id' => $userId, + 'is_log' => true, + 'log' => json_encode($booking), + 'status' => 1 + ]; + + } else { + + $createBookingTicketParams = [ + 'parent_id' => $bookingTicket['data']['id'], + 'booking_id' => $booking['id'], + 'user_id' => $userId, + 'is_log' => true, + 'log' => json_encode($booking), + 'status' => 1 + ]; + + } + + $bookingTicketCreate = $this->bookingTicketService->create($createBookingTicketParams); + //Booking Ticket Log + + if ($type == 'Cancel') { + $notificationCancelToPropertyUser = [ + 'booking_id' => $booking['id'], + ]; + + $this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($notificationCancelToPropertyUser)); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['transactions' => $bookingUpdate['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getBookingPayment(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $getBookingPayment = $this->bookingService->getBookingPayment($params); + if ($getBookingPayment['status'] != "success") { + throw new ApiErrorException($getBookingPayment['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $getBookingPayment['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} diff --git a/app/Http/Controllers/V1/PropertyBookingTicketController.php b/app/Http/Controllers/V1/PropertyBookingTicketController.php new file mode 100644 index 0000000..8af8e17 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyBookingTicketController.php @@ -0,0 +1,293 @@ +bookingService = $bookingService; + $this->bookingTicketService = $bookingTicketService; + $this->propertyTicketGetValidator = $propertyTicketGetValidator; + $this->propertyTicketCreateValidator = $propertyTicketCreateValidator; + $this->mailer = $mailer; + $this->notificationService = $notificationService; + $this->params = json_decode($request->getContent(), 1); + $this->params = $this->params['params']; + } + + public function createTicket(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + DB::beginTransaction(); + + $params = $this->params; + if(is_null($params)) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params['ip_address'] = fillOnUndefined($params,'ip_address') ? $params['ip_address'] : $request->ip(); + + $validationResult = $this->propertyTicketCreateValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $bookingRequest = [ + 'criteria' => [ + ['field' => 'booking_code', 'condition' => '=', 'value' => $params['booking_code']], + ], + 'firstRow' => true + ]; + + $booking = $this->bookingService->select($bookingRequest); + + if ($booking['status'] != 'success') { + throw new ApiErrorException($booking['message']); + } + + if (empty($booking['data'])) { + throw new ApiErrorException('Booking not found.'); + } + + $booking = $booking['data']; + + $bookingTicketRequest = [ + 'criteria' => [ + ['field' => 'booking_id', 'condition' => '=', 'value' => $booking['id']], + ], + 'firstRow' => true + ]; + + $bookingTicket = $this->bookingTicketService->select($bookingTicketRequest, ['id']); + if ($bookingTicket['status'] != 'success') { + throw new ApiErrorException($bookingTicket['message']); + } + + $createParams = []; + if (empty($bookingTicket['data'])) { + + $createParams = [ + 'booking_id' => $booking['id'], + 'code' => getTicketCodeGenerate('TCK'), + 'user_id' => $params['user_id'], + 'message' => $params['message'], + 'is_note' => fillOnUndefined($params, 'is_note') == false ? null : $params['is_note'], + 'is_log' => fillOnUndefined($params, 'is_log'), + 'ip_address' => fillOnUndefined($params,'ip_address'), + 'status' => 1 + ]; + + } else { + + $createParams = [ + 'parent_id' => $bookingTicket['data']['id'], + 'booking_id' => $booking['id'], + 'user_id' => $params['user_id'], + 'message' => $params['message'], + 'is_note' => fillOnUndefined($params, 'is_note') == false ? null : $params['is_note'], + 'is_log' => fillOnUndefined($params, 'is_log'), + 'ip_address' => fillOnUndefined($params,'ip_address'), + 'status' => 1 + ]; + + } + + $bookingTicketCreate = $this->bookingTicketService->create($createParams); + + if ($bookingTicketCreate['status'] != 'success') { + throw new ApiErrorException($bookingTicketCreate['message']); + } + + //BookingTicketMail + if(is_null($createParams['is_note'])) { + $mailParams = [ + 'user' => $params['user_id'], + 'booking_id' => $booking['id'], + //'locale' => 'en' + ]; + + $this->mailer->onQueue('bookingTicketMail', new BookingTicketMail($mailParams)); + } + //BookingTicketMail + + //PUSH NOTIFICATION + if(empty($params['user_id'])) { + $notificationParam = ['booking_id' => $booking['id']]; + $this->notificationService->sendTicketNotification($notificationParam); + } + //PUSH NOTIFICATION + + if(empty($params['user_id'])) { + $this->bookingService->update($booking['id'], ['is_viewed' => null]); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $bookingTicketCreate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if($response['status']) { + DB::commit(); + }else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getTicketList() + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $params = $this->params; + if(is_null($params)) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $validationResult = $this->propertyTicketGetValidator->validate($params); + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $bookingRequest = [ + 'criteria' => [ + ['field' => 'booking_code', 'condition' => '=', 'value' => $params['booking_code']], + ], + 'with' => ['bookingContact'], + 'firstRow' => true + ]; + + $booking = $this->bookingService->select($bookingRequest); + if ($booking['status'] != 'success') { + throw new ApiErrorException($booking['message']); + } + + $booking = $booking['data']; + + $bookingTicketRequest = [ + 'criteria' => [ + ['field' => 'booking_id', 'condition' => '=', 'value' => $booking['id']], + ['field' => 'is_log', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + + ], + 'with' => ['user'], + 'orderBy' => [ + ['field' => 'created_at', 'value' => 'ASC'] + ] + ]; + + + if (!isset($params['user_id']) || is_null($params['user_id'])) { + $bookingTicketRequest['criteria'][] = ['field' => 'is_note', 'condition' => '=', 'value' => null]; + } + + $column = ['id', 'user_id', 'message', 'is_note', 'is_viewed', 'created_at']; + $bookingTicket = $this->bookingTicketService->select($bookingTicketRequest, $column); + if ($bookingTicket['status'] != 'success') { + throw new ApiErrorException($bookingTicket['message']); + } + + + foreach ($bookingTicket['data'] as $key => $row) { + + if(is_null($row['user_id'])) { + $bookingTicket['data'][$key]['nameSurname'] = $booking['booking_contact']['nameSurname']; + } else { + $bookingTicket['data'][$key]['nameSurname'] = $row['user']['nameSurname']; + } + + unset($bookingTicket['data'][$key]['user']); + } + + + $updateCriteria = [ + 'criteria' => [ + ['field' => 'booking_id', 'condition' => '=', 'value' => $booking['id']], + ['field' => 'is_log', 'condition' => '=', 'value' => null], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + + if (!isset($params['user_id']) || is_null($params['user_id'])) { + $updateCriteria['criteria'][] = ['field' => 'user_id', 'condition' => '!=', 'value' => null]; + $updateCriteria['criteria'][] = ['field' => 'is_viewed', 'condition' => '=', 'value' => null]; + } else { + $updateCriteria['criteria'][] = ['field' => 'user_id', 'condition' => '=', 'value' => null]; + $updateCriteria['criteria'][] = ['field' => 'is_viewed', 'condition' => '=', 'value' => null]; + } + + + $updateParam = [ + 'is_viewed' => 1 + ]; + + $this->bookingTicketService->updateWhere($updateCriteria, $updateParam); + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $bookingTicket['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + +} + diff --git a/app/Http/Controllers/V1/PropertyBrandController.php b/app/Http/Controllers/V1/PropertyBrandController.php new file mode 100644 index 0000000..4e1bf3c --- /dev/null +++ b/app/Http/Controllers/V1/PropertyBrandController.php @@ -0,0 +1,158 @@ +request = $request; + $this->propertyBrandService = $propertyBrandService; + $this->propertyConfigService = $propertyConfigService ; + + } + + public function updatePropertyBrand(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + + + $params = + [ + "property_id" => $request->input('property_id'), + "title" => $request->input("title"), + "color_codes" => $request->input("colors"), + "logo_path" => $request->input( "logo_path"), + "logo_name" => $request->input( "logo_name"), + "photo" => $request->file('photo'), + "user_id" => $this->request->auth->id + ]; + + + $updateResponse = $this->propertyBrandService->updatePropertyBrand($params); + + if($updateResponse['status'] != 'success'){ + throw new ApiErrorException($updateResponse['message']); + } + + $rateParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => fillOnUndefined($params, 'user_id'), + 'property_rate_for' => 'Property.Brand.Update', + ]; + $this->propertyConfigService->rateProperty(array_merge($params, $rateParams)); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['get_property_brand' => $updateResponse['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPropertyBrand(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $getPropertyBrand = $this->propertyBrandService->getPropertyBrand($params); + if($getPropertyBrand['status'] != "success"){ + throw new ApiErrorException($getPropertyBrand['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['get_property_brand' => $getPropertyBrand['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function clearBrandLogo(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->credentials->user_id; + + $getPropertyBrand = $this->propertyBrandService->clearBrandLogo($params); + + if($getPropertyBrand['status'] != "success"){ + throw new ApiErrorException($getPropertyBrand['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => '']; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + +} diff --git a/app/Http/Controllers/V1/PropertyCancellationPolicyController.php b/app/Http/Controllers/V1/PropertyCancellationPolicyController.php new file mode 100644 index 0000000..6c3f638 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyCancellationPolicyController.php @@ -0,0 +1,207 @@ +request = $request; + $this->propertyCancellationPolicyService = $propertyCancellationPolicyService; + } + + public function getPropertyCancellationPolicyList(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $getPropertyCancellationList = $this->propertyCancellationPolicyService->getPropertyCancellationPolicyList($params); + + if ($getPropertyCancellationList['status'] != "success") { + throw new ApiErrorException($getPropertyCancellationList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['cancellation_policies' => $getPropertyCancellationList['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function createPropertyCancellationPolicy(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + if (!isset($params['is_date_range']) || (isset($params['is_date_range']) && $params['is_date_range'] == 0)) { + $params['is_date_range'] = 0; + unset($params['start_date']); + unset($params['finish_date']); + } + + $createResponse = $this->propertyCancellationPolicyService->createPropertyCancellationPolicy($params); + + if ($createResponse['status'] != 'success') { + throw new ApiErrorException($createResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePropertyCancellationPolicy(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + if (!isset($params['is_date_range']) || (isset($params['is_date_range']) && $params['is_date_range'] == 0)) { + $params['is_date_range'] = 0; + unset($params['start_date']); + unset($params['finish_date']); + } + + $updateResponse = $this->propertyCancellationPolicyService->updatePropertyCancellationPolicy($params); + + if ($updateResponse['status'] != 'success') { + throw new ApiErrorException($updateResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $updateResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getRoomRateChannelCancellationPolicy(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $getPropertyCancellationList = $this->propertyCancellationPolicyService->getRoomRateChannelCancellationPolicy($params); + + if ($getPropertyCancellationList['status'] != "success") { + throw new ApiErrorException($getPropertyCancellationList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['rooms' => $getPropertyCancellationList['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updateRoomRateChannelCancellationPolicy(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + + $getPropertyCancellationList = $this->propertyCancellationPolicyService->updateRoomRateChannelCancellationPolicy($params); + + if ($getPropertyCancellationList['status'] != "success") { + throw new ApiErrorException($getPropertyCancellationList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} diff --git a/app/Http/Controllers/V1/PropertyChainController.php b/app/Http/Controllers/V1/PropertyChainController.php new file mode 100644 index 0000000..2de0000 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyChainController.php @@ -0,0 +1,72 @@ +request = $request; + $this->propertyChainService = $propertyChainService; + + } + + + + public function getPropertyChains(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + ]; + + $propertyChains = $this->propertyChainService->getPropertyChains($requestParams); + if($propertyChains['status'] != 'success'){ + throw new ApiErrorException($propertyChains['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChains['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyChannelBookingPaymentSetupController.php b/app/Http/Controllers/V1/PropertyChannelBookingPaymentSetupController.php new file mode 100644 index 0000000..ae66d7b --- /dev/null +++ b/app/Http/Controllers/V1/PropertyChannelBookingPaymentSetupController.php @@ -0,0 +1,111 @@ +request = $request; + $this->propertyChannelBookingPaymentSetupService = $propertyChannelBookingPaymentSetupService; + + } + + + + public function getPropertyChannelBookingPaymentSetup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + ]; + + $propertyChannelBookingPaymentSetup = $this->propertyChannelBookingPaymentSetupService->getChannelBookingPaymentSetup($requestParams); + + if($propertyChannelBookingPaymentSetup['status'] != 'success'){ + throw new ApiErrorException($propertyChannelBookingPaymentSetup['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelBookingPaymentSetup['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyChannelBookingPaymentSetup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $requestParams = $params ; + $requestParams['user_id'] = $this->request->auth->id; + + $propertyChannelBookingPaymentSetup = $this->propertyChannelBookingPaymentSetupService->addChannelBookingPaymentSetup($requestParams); + + if($propertyChannelBookingPaymentSetup['status'] != 'success'){ + throw new ApiErrorException($propertyChannelBookingPaymentSetup['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelBookingPaymentSetup['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} diff --git a/app/Http/Controllers/V1/PropertyChannelCategoryController.php b/app/Http/Controllers/V1/PropertyChannelCategoryController.php new file mode 100644 index 0000000..5e62a10 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyChannelCategoryController.php @@ -0,0 +1,74 @@ +request = $request; + $this->propertyChannelCategoryService = $propertyChannelCategoryService; + + } + + + + public function getPropertyChannelCategories(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + ]; + + $propertyChannelCategory = $this->propertyChannelCategoryService->getPropertyChannelCategories($requestParams); + if($propertyChannelCategory['status'] != 'success'){ + throw new ApiErrorException($propertyChannelCategory['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelCategory['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyChannelContactController.php b/app/Http/Controllers/V1/PropertyChannelContactController.php new file mode 100644 index 0000000..d6216f5 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyChannelContactController.php @@ -0,0 +1,262 @@ +request = $request; + $this->propertyChannelService = $propertyChannelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyChannelContactService = $propertyChannelContactService; + } + + + public function getPropertyChannelContact(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $channelMappingRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + ]; + + $channelMapping = $this->propertyChannelMappingService->getOnlyPropertyChannelMapping($channelMappingRequest); + if($channelMapping['status'] != 'success'){ + throw new ApiErrorException($channelMapping['message']); + } + + if(!count($channelMapping['data'])){ + throw new ApiErrorException("This channel is not connected with this property"); + } + + $channelContactRequest = [ + 'property_channel_mapping_id' => $channelMapping["data"]["id"], + ]; + + if(isset($request->params['id'])){ + $channelContactRequest = [ + 'id' => fillOnUndefined($params, 'id'), + 'property_channel_mapping_id' => $channelMapping["data"]["id"], + ]; + } + + $channelContact = $this->propertyChannelContactService->getContact($channelContactRequest); + if($channelContact['status'] != 'success'){ + throw new ApiErrorException($channelContact['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $channelContact['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + public function addPropertyChannelContact(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $channelMappingRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + ]; + + $channelMapping = $this->propertyChannelMappingService->getOnlyPropertyChannelMapping($channelMappingRequest); + if($channelMapping['status'] != 'success'){ + throw new ApiErrorException($channelMapping['message']); + } + + if(!count($channelMapping['data'])){ + throw new ApiErrorException("This channel is not connected with this property"); + } + + $requestParams = [ + 'property_channel_mapping_id' => $channelMapping["data"]["id"], + 'name' => fillOnUndefined($params, 'name', null), + 'email' => fillOnUndefined($params, 'email', null), + 'user_id' => $this->request->auth->id, + ]; + + $channelContact = $this->propertyChannelContactService->add($requestParams); + if($channelContact['status'] != 'success'){ + throw new ApiErrorException($channelContact['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $channelContact['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyChannelContact(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $channelMappingRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + ]; + + $channelMapping = $this->propertyChannelMappingService->getOnlyPropertyChannelMapping($channelMappingRequest); + if($channelMapping['status'] != 'success'){ + throw new ApiErrorException($channelMapping['message']); + } + + if(!count($channelMapping['data']) || $channelMapping['data']['id'] != $params['property_channel_mapping_id']){ + throw new ApiErrorException("This channel is not connected with this property"); + } + + $requestParams = [ + 'name' => fillOnUndefined($params, 'name', null), + 'email' => fillOnUndefined($params, 'email', null), + 'updated_by' => $this->request->auth->id, + 'updated_at' => time() + ]; + + $channelContact = $this->propertyChannelContactService->update($params['id'],$requestParams); + if($channelContact['status'] != 'success'){ + throw new ApiErrorException($channelContact['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $channelContact['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function deletePropertyChannelContact(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $channelMappingRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + ]; + + $channelMapping = $this->propertyChannelMappingService->getOnlyPropertyChannelMapping($channelMappingRequest); + if($channelMapping['status'] != 'success'){ + throw new ApiErrorException($channelMapping['message']); + } + + if(!count($channelMapping['data']) || $channelMapping['data']['id'] != $params['property_channel_mapping_id']){ + throw new ApiErrorException("This channel is not connected with this property"); + } + + $requestParams = [ + 'id' => $params["id"], + 'property_channel_mapping_id' => $params['property_channel_mapping_id'] + ]; + + $channelContact = $this->propertyChannelContactService->delete($requestParams); + if($channelContact['status'] != 'success'){ + throw new ApiErrorException($channelContact['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $channelContact['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyChannelController.php b/app/Http/Controllers/V1/PropertyChannelController.php new file mode 100644 index 0000000..41ce539 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyChannelController.php @@ -0,0 +1,207 @@ +request = $request; + $this->propertyChannelService = $propertyChannelService; + + } + + + + public function getPropertyChannel(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'parent_id' => fillOnUndefined($params, 'parent_id'), + 'type' => fillOnUndefined($params, 'type'), + 'channel_category_id' => fillOnUndefined($params, 'channel_category_id'), + 'country_code' => fillOnUndefined($params, 'country_code'), + ]; + + $propertyChannel = $this->propertyChannelService->getPropertyChannels($requestParams); + if($propertyChannel['status'] != 'success'){ + throw new ApiErrorException($propertyChannel['message']); + } + + //CHANNEL RESTRICTION PROPERTIES + foreach ($propertyChannel['data'] as $channelId => $channel) { + if(!empty($channel['restriction'])) { + $channelRestriction = json_decode($channel['restriction'],1); + if(!in_array($params['property_id'],$channelRestriction)) { + unset($propertyChannel['data'][$channelId]); + } + } + } + + + //dd($propertyChannel['data'], $params['property_id']); + + //TODO: Channell sıralaması için bir alan seçilip kurgulanacak + //$propertyChannelCollect = collect($propertyChannel['data']); + //$propertyChannel['data'] = $propertyChannelCollect->sortByDesc('fax', SORT_NUMERIC )->toArray(); + //$propertyChannel['data'] = array_values($propertyChannel['data']); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannel['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyChannel(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'parent_id' => fillOnUndefined($params, 'parent_id'), + 'name' => fillOnUndefined($params, 'name'), + 'official_name' => fillOnUndefined($params, 'official_name'), + 'description' => fillOnUndefined($params, 'description'), + 'channel_category_id' => fillOnUndefined($params, 'channel_category_id'), + 'country_code' => fillOnUndefined($params, 'country_code'), + 'currency_code' => fillOnUndefined($params, 'currency_code', []), + 'logo' => fillOnUndefined($params, 'logo'), + 'address' => fillOnUndefined($params, 'address'), + 'zip_code' => fillOnUndefined($params, 'zip_code'), + 'email' => fillOnUndefined($params, 'email'), + 'phone' => fillOnUndefined($params, 'phone'), + 'phone2' => fillOnUndefined($params, 'phone2'), + 'fax' => fillOnUndefined($params, 'fax'), + 'score' => fillOnUndefined($params, 'score'), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyChannel = $this->propertyChannelService->addPropertyChannel($requestParams); + if($propertyChannel['status'] != 'success'){ + throw new ApiErrorException($propertyChannel['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannel['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyChannel(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'id' => fillOnUndefined($params, 'id'), + 'parent_id' => fillOnUndefined($params, 'parent_id'), + 'name' => fillOnUndefined($params, 'name'), + 'official_name' => fillOnUndefined($params, 'official_name'), + 'description' => fillOnUndefined($params, 'description'), + 'channel_category_id' => fillOnUndefined($params, 'channel_category_id'), + 'country_code' => fillOnUndefined($params, 'country_code'), + 'currency_code' => fillOnUndefined($params, 'currency_code', []), + 'logo' => fillOnUndefined($params, 'logo'), + 'address' => fillOnUndefined($params, 'address'), + 'zip_code' => fillOnUndefined($params, 'zip_code'), + 'email' => fillOnUndefined($params, 'email'), + 'phone' => fillOnUndefined($params, 'phone'), + 'phone2' => fillOnUndefined($params, 'phone2'), + 'fax' => fillOnUndefined($params, 'fax'), + 'score' => fillOnUndefined($params, 'score'), + 'user_id' => $this->request->auth->id, + ]; + + $propertyChannel = $this->propertyChannelService->updatePropertyChannel($requestParams); + if($propertyChannel['status'] != 'success'){ + throw new ApiErrorException($propertyChannel['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannel['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + + + + +} diff --git a/app/Http/Controllers/V1/PropertyChannelGroupController.php b/app/Http/Controllers/V1/PropertyChannelGroupController.php new file mode 100644 index 0000000..e6e6663 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyChannelGroupController.php @@ -0,0 +1,497 @@ +request = $request; + $this->propertyChannelGroupService = $propertyChannelGroupService; + $this->propertyChannelGroupChannelMappingRepository = $propertyChannelGroupChannelMappingRepository; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomService = $propertyRoomService; + } + + + public function getPropertyChannelGroup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + if(isset($request->params['group_id'])){ + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'id' => fillOnUndefined($params, 'group_id'), + ]; + } + + $propertyChannelGroup = $this->propertyChannelGroupService->getPropertyChannelGroup($requestParams); + if($propertyChannelGroup['status'] != 'success'){ + throw new ApiErrorException($propertyChannelGroup['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelGroup['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyChannelGroup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + DB::beginTransaction(); + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id', null), + 'name' => fillOnUndefined($params, 'name', null), + 'created_by' => $this->request->auth->id, + 'updated_by' => $this->request->auth->id, + ]; + + + $propertyChannelGroup = $this->propertyChannelGroupService->create($requestParams); + if($propertyChannelGroup['status'] != 'success'){ + throw new ApiErrorException($propertyChannelGroup['message']); + } + + $channels = fillOnUndefined($params, 'channels',[]); + + $insertData = []; + foreach ($channels as $channel){ + $insertData[] = [ + 'channel_group_id' => $propertyChannelGroup['data']['id'], + 'channel_id' => $channel, + 'status' => 1, + 'created_by' => $this->request->auth->id, + 'updated_by' => $this->request->auth->id, + 'created_at' => time(), + 'updated_at' => time() + ]; + } + + $propertyChannelGroupChannels = $this->propertyChannelGroupChannelMappingRepository->insert($insertData); + if($propertyChannelGroupChannels['status'] != 'success'){ + throw new ApiErrorException($propertyChannelGroupChannels['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelGroup['data']]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + DB::rollBack(); + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyChannelGroup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + DB::beginTransaction(); + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'name' => fillOnUndefined($params, 'name', null), + 'updated_by' => $this->request->auth->id, + 'updated_at' => time() + ]; + + + $propertyChannelGroup = $this->propertyChannelGroupService->update($params['group_id'], $requestParams); + if($propertyChannelGroup['status'] != 'success'){ + throw new ApiErrorException($propertyChannelGroup['message']); + } + + $channels = fillOnUndefined($params, 'channels',[]); + + $channelRequest = [ + 'criteria' => [ + ['field' => 'channel_group_id', 'condition' => '=', 'value' => $params['group_id']], + ] + ]; + + $propertyChannelGroupChannelDelete = $this->propertyChannelGroupChannelMappingRepository->delete($channelRequest); + + $insertData = []; + foreach ($channels as $channel){ + $insertData[] = [ + 'channel_group_id' => $params['group_id'], + 'channel_id' => $channel, + 'status' => 1, + 'created_by' => $this->request->auth->id, + 'updated_by' => $this->request->auth->id, + 'created_at' => time(), + 'updated_at' => time() + ]; + } + + $propertyChannelGroupChannels = $this->propertyChannelGroupChannelMappingRepository->insert($insertData); + if($propertyChannelGroupChannels['status'] != 'success'){ + throw new ApiErrorException($propertyChannelGroupChannels['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelGroup['data']]; + DB::commit(); + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + DB::rollBack(); + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyGroupInventory(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $startDate = fillOnUndefinedAndEmpty($params, 'start_date', date('Y-m-d')) ; + $endDate = fillOnUndefinedAndEmpty($params, 'end_date', Carbon::parse($startDate)->addDay(15)->format('Y-m-d')) ; + + if($endDate < $startDate){ + throw new ApiErrorException('date error') ; + } + $diffInDays = Carbon::parse($startDate)->diffInDays($endDate); + if($diffInDays > 15){ + throw new ApiErrorException('date error') ; + } + + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_group_id' => fillOnUndefined($params, 'channel_group_id'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + + ]; + + $groupActiveChannels = $this->propertyChannelGroupService->getPropertyChannelGroupActiveChannels($requestParams); + if($groupActiveChannels['status'] != 'success'){ + throw new ApiErrorException($groupActiveChannels['message']); + } + + $inventoryData = $groupActiveChannels["data"]; + $inventoryData['channel_ids'] = []; + $inventoryData['currencies'] = []; + foreach ($groupActiveChannels['data']['channel_group_active_channels'] as $groupActiveChannel){ + + $channelId = $groupActiveChannel['channel_id']; + + $checkPropertyChannelMappingRequestParams = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'status', 'condition' => '=', 'value' => 1] + ] + ]; + + $checkPropertyChannelMapping = $this->propertyChannelMappingService->select($checkPropertyChannelMappingRequestParams,['id', 'property_id', 'status']) ; + if($checkPropertyChannelMapping['status'] != 'success'){ + throw new ApiErrorException($checkPropertyChannelMapping['message']); + } + + if(!empty($checkPropertyChannelMapping['data'])){ + $inventoryData['channels'][$channelId] = $groupActiveChannel['channel']; + $inventoryData['channel_ids'][$channelId] = $channelId; + } + } + + if(!count($inventoryData['channel_ids'])){ + throw new ApiErrorException("There isn't any active channel in this channel group"); + } + + $channelMappingRequestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => $params['property_id'], + 'channel_ids' => $inventoryData['channel_ids'], + ]; + + $getChannelMappingResponse = $this->propertyChannelMappingService->getPropertyChannelMappingForChannelGroup($channelMappingRequestParams) ; + if($getChannelMappingResponse['status'] != 'success'){ + throw new ApiErrorException($getChannelMappingResponse['message']); + } + + foreach ($getChannelMappingResponse["data"] as $channelMapping){ + $channelId = $channelMapping['channel_id']; + + $inventoryData['channels'][$channelId]['currency_code'] = $channelMapping['currency_code']; + $inventoryData['channels'][$channelId]['property_availability_type_id'] = $channelMapping['property_availability_type_id']; + + $inventoryData['currencies'][$channelMapping['currency_code']] = $channelMapping['currency_code']; + } + + + foreach ($inventoryData['channel_ids'] as $channelId){ + + $getGroupInventoryRequestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => $params['property_id'], + 'channel_id' => $channelId, + ]; + + $inventoryData['channels'][$channelId]['rooms_temp'] = $this->propertyRoomService->inventoryRoomList($getGroupInventoryRequestParams); + + } + + + foreach ($inventoryData['channels'] as $channel){ + + $channelId = $channel['id']; + $currency = $channel['currency_code']; + + foreach ($channel['rooms_temp'] as $roomTemp){ + + $roomId = $roomTemp['id']; + + $dateKey = Carbon::parse($params['start_date']); + for ($i = 0; $i <= $diffInDays; $i++) { + + $responseAVAKey = 'AVA_1_'.$roomId.'_'. '0' .'_'.$dateKey->format('Ymd') ; + $responseSTSKey = 'STS_1_'.$roomId.'_'. '0' .'_'.$dateKey->format('Ymd') ; + + $roomAvailability[$dateKey->format('Y-m-d')] = [ + 'key' => $responseAVAKey, + 'value' => null, + 'stop_sell' => 0 + ]; + + $roomStopSell[$dateKey->format('Y-m-d')] = [ + 'key' => $responseSTSKey, + 'value' => 0, + ]; + + $dateKey = $dateKey->addDay(); + } + + foreach ($roomTemp['property_room_rate_mapping'] as $roomRateMapping ){ + + $roomRate = $roomRateMapping['property_room_rate']; + $roomRateId = $roomRate['id']; + $roomRateChannel = $roomRateMapping['property_room_rate_channel']; + + + foreach ($roomRateChannel as $roomRateChan){ + + if($roomRateChan['channel_id'] == $channelId && $roomRateChan['status'] == 1){ + + $inventoryData['rooms'][$roomId]['id'] = $roomTemp['id']; + $inventoryData['rooms'][$roomId]['name'] = $roomTemp['name']; + $inventoryData['rooms'][$roomId]['room_selected'] = false; + $inventoryData['rooms'][$roomId]['availability'] = $roomAvailability; + $inventoryData['rooms'][$roomId]['room_stop_sell'] = $roomStopSell; + + $rate['id'] = $roomRateId; + $rate['name'] = $roomRate['name']; + $rate['currency'] = $currency; + $rate['rate_selected'] = false; + $rate['room_rate_mapping_id'] = $roomRateChan['room_rate_mapping_id']; + + + $dateKey = Carbon::parse($params['start_date']); + for ($i = 0; $i <= $diffInDays; $i++) { + + $responsePRCKey = 'PRC_1'.'_'.$roomRateMapping['room_id'].'_'. $roomRateMapping['id'].'_'.$dateKey->format('Ymd').'_'.$currency ; + $responseSTSKey = 'STS_1'.'_'.$roomRateMapping['room_id'].'_'. $roomRateMapping['id'].'_'.$dateKey->format('Ymd'); + $responseMNSKey = 'MNS_1'.'_'.$roomRateMapping['room_id'].'_'. $roomRateMapping['id'].'_'.$dateKey->format('Ymd'); + + $price[$dateKey->format('Y-m-d')] = [ + 'key' => $responsePRCKey, + 'value' => null, + 'stop_sell' => 0 + ]; + + $stopSell[$dateKey->format('Y-m-d')] = [ + 'key' => $responseSTSKey, + 'value' => 0, + ]; + + $minStay[$dateKey->format('Y-m-d')] = [ + 'key' => $responseMNSKey, + 'value' => 1, + ]; + + $dateKey = $dateKey->addDay(); + } + + $rate['price'] = $price; + $rate['stop_sell'] = $stopSell; + $rate['min_stay'] = $minStay; + + + $inventoryData['rooms'][$roomId]['rates'][$roomRateId][$currency] = $rate; + + } + } + } + } + + } + + if(isset($inventoryData['channel_group_active_channels'])){ + unset($inventoryData['channel_group_active_channels']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $inventoryData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function createPropertyChannelGroup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $mappedChannelCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'], + ]; + + $propertyChannelsForGroup = $this->propertyChannelMappingService->select($mappedChannelCriteria); + if($propertyChannelsForGroup['status'] != 'success'){ + throw new ApiErrorException($propertyChannelsForGroup['message']); + } + + $channels = []; + + foreach ($propertyChannelsForGroup['data'] as $data){ + if($data['property_availability_type_id'] == 1 && $data['channel']['parent_id'] == null){ + $channels[$data['id']] = $data['channel']; + } + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $channels]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyChannelMappingController.php b/app/Http/Controllers/V1/PropertyChannelMappingController.php new file mode 100644 index 0000000..6b522cb --- /dev/null +++ b/app/Http/Controllers/V1/PropertyChannelMappingController.php @@ -0,0 +1,447 @@ +request = $request; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyChannelService = $propertyChannelService ; + $this->propertyBookingEngineService = $propertyBookingEngineService; + + } + + + + public function getPropertyChannelMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $this->request->auth->id, + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->getPropertyChannelMapping($requestParams); + if($propertyChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyChannelMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channels' => fillOnUndefined($params, 'channels', []), + 'user_id' => $this->request->auth->id, + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->addPropertyChannelMapping($requestParams); + if($propertyChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function removePropertyChannelMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channels' => fillOnUndefined($params, 'channels', []), + 'user_id' => $this->request->auth->id, + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->removePropertyChannelMapping($requestParams); + if($propertyChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyChannelMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $propertyChannelMapping = $this->propertyChannelMappingService->updatePropertyChannelMapping($params); + if($propertyChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyChannelSetup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = + [ + "property_id" => $request->input('property_id'), + "channel_id" => $request->input('channel_id'), + "booking_type_id" => $request->input('booking_type_id') != 'null' ? $request->input('booking_type_id') : null, + "availability_type_id" => $request->input('availability_type_id') != 'null' ? $request->input('availability_type_id') : null, + "room_pricing_type_id" => $request->input('room_pricing_type_id') != 'null' ? $request->input('room_pricing_type_id') : null, + "currency_code" => $request->input('currency_code') != 'null' ? $request->input('currency_code') : null, + "connected_channel_id" => $request->input('connected_channel_id') != 'null' ? $request->input('connected_channel_id') : null, + "connected_channel_action" => $request->input('connected_channel_action') != 'null' ? $request->input('connected_channel_action') : null, + "contract_file" => $request->file('contract_file') + ]; + + $requestParams = $params ; + $requestParams['user_id'] = $this->request->auth->id; + + $getChannelRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + $getChannelResponse = $this->propertyChannelService->select($getChannelRequest, ['id', 'parent_id', 'channel_category_id']) ; + if($getChannelResponse['status'] != 'success' && !empty($getChannelResponse['data'])){ + throw new ApiErrorException($getChannelResponse['message']); + } + + + if($getChannelResponse['data']['channel_category_id'] === self::CHANNEL_MANAGER && $getChannelResponse['data']['parent_id'] === self::PARENT_ID + && !$requestParams['booking_type_id'] && !$requestParams['availability_type_id'] && !$requestParams['room_pricing_type_id'] ){ + + $propertyChannelMapping = $this->propertyChannelMappingService->addPropertyChannelSetupWithMissingParameters($requestParams); + + if($propertyChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelMapping['data']]; + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + $propertyChannelMapping = $this->propertyChannelMappingService->addPropertyChannelSetup($requestParams); + if($propertyChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $getChannelRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['parentChannel', 'propertyChannelCategory'], + 'firstRow' => 1 + ]; + $getChannelResponse = $this->propertyChannelService->select($getChannelRequest, ['id', 'parent_id', 'name', 'official_name', 'description', 'channel_category_id', 'default_currency', 'country_code', 'currency_code', 'logo', 'address', 'zip_code', 'email', 'phone', 'phone2', 'fax', 'score', 'status']) ; + if($getChannelResponse['status'] != 'success'){ + throw new ApiErrorException($getChannelResponse['message']); + } + $channelData['channel'] = $getChannelResponse['data'] ; + + + $getChannelMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + $getChannelMappingResponse = $this->propertyChannelMappingService->select($getChannelMappingRequest) ; + + if($getChannelMappingResponse['status'] != 'success'){ + throw new ApiErrorException($getChannelMappingResponse['message']); + } + $channelData['channel']['currency_list'] = json_decode($channelData['channel']['currency_code'] , 1); + $channelData['channel']['currency'] = isset($getChannelMappingResponse['data']['currency_code']) ? $getChannelMappingResponse['data']['currency_code'] : $channelData['channel']['default_currency'] ; + $channelData['channel']['is_connected'] = $getChannelMappingResponse['data'] ? true : false ; + + + $propertyChannelMapping = $this->propertyChannelMappingService->getPropertyChannelSetup($requestParams); + if($propertyChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $channelData = array_merge($channelData, $propertyChannelMapping['data']) ; + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $channelData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyChannelSetup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $requestParams = $params ; + $requestParams['user_id'] = $this->request->auth->id; + + $getChannelRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['parentChannel', 'propertyChannelCategory'], + 'firstRow' => 1 + ]; + $getChannelResponse = $this->propertyChannelService->select($getChannelRequest, ['id', 'parent_id', 'name', 'official_name', 'default_currency', 'description', 'channel_category_id', 'country_code', 'currency_code', 'logo', 'address', 'zip_code', 'email', 'phone', 'phone2', 'fax', 'score', 'status']) ; + if($getChannelResponse['status'] != 'success'){ + throw new ApiErrorException($getChannelResponse['message']); + } + $channelData['channel'] = $getChannelResponse['data'] ; + + $childChannels = []; + if(!empty($channelData['channel']) && $channelData['channel']['parent_id'] === NULL + && $channelData['channel']['property_channel_category']['id'] == 4 ){ + + $childChannels = $this->propertyChannelMappingService->getPropertyChildChannel(['property_id' => $params['property_id']]); + + $childChannels = $childChannels['data']; + + } + + + $getChannelMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ], + 'with' => ['channelBookingType', 'channelAvailabilityType', 'channelRoomPricingType'] , + 'firstRow' => 1 + ]; + $getChannelMappingResponse = $this->propertyChannelMappingService->select($getChannelMappingRequest); + + if($getChannelMappingResponse['status'] != 'success'){ + throw new ApiErrorException($getChannelMappingResponse['message']); + } + + $getChannelMappingRequestData = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ], + 'firstRow' => 1 + ]; + $getChannelMappingResponseData = $this->propertyChannelMappingService->select($getChannelMappingRequestData) ; + + if($getChannelMappingResponseData['status'] != 'success'){ + throw new ApiErrorException($getChannelMappingResponseData['message']); + } + + $isPending = false; + if(isset($getChannelMappingResponse['data']['status'])){ + $isPending = $getChannelMappingResponse['data']['status'] === 2 ? true : false ; + } + + + $isConnected = false ; + if(isset($getChannelMappingResponse['data']['status'])){ + $isConnected = $getChannelMappingResponse['data']['status'] ? true : false ; + } + + $channelData['channel']['is_connected'] = $isConnected ; + $channelData['channel']['is_pending'] = $isPending ; + $channelData['channel']['currency_list'] = json_decode($channelData['channel']['currency_code'] , 1); + $channelData['channel']['currency'] = isset($getChannelMappingResponse['data']['currency_code']) ? $getChannelMappingResponse['data']['currency_code'] : $channelData['channel']['default_currency'] ; + $channelData['channel']['channel_booking_type'] = isset($getChannelMappingResponse['data']['channel_booking_type']) ? $getChannelMappingResponse['data']['channel_booking_type'] : null; + $channelData['channel']['channel_availability_type'] = isset($getChannelMappingResponse['data']['channel_availability_type']) ? $getChannelMappingResponse['data']['channel_availability_type'] : null ; + $channelData['channel']['channel_room_pricing_type'] = isset($getChannelMappingResponse['data']['channel_room_pricing_type'] ) ? $getChannelMappingResponse['data']['channel_room_pricing_type'] : null; + $channelData['channel']['connected_channel_id'] = isset($getChannelMappingResponse['data']['connected_channel_id'] ) ? $getChannelMappingResponse['data']['connected_channel_id'] : null; + $channelData['channel']['connected_channel_action'] = isset($getChannelMappingResponse['data']['connected_channel_action'] ) ? $getChannelMappingResponse['data']['connected_channel_action'] : null; + + $propertyChannelMapping = $this->propertyChannelMappingService->getPropertyChannelSetup($requestParams); + if($propertyChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $channelData = array_merge($channelData, $propertyChannelMapping['data']) ; + $channelData['mapping_child_channels'] = $childChannels; + + + $channelData['channel']['bookingEngineUrl'] = null; + $channelData['channel']['bookingEngineToken'] = null; + $propertyBookingEngineRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + $propertyBookingEngine = $this->propertyBookingEngineService->select($propertyBookingEngineRequest); + + $bookingEngineUrl = null; + $bookingEngineToken = null; + if($propertyBookingEngine['status'] == 'success' && !empty($propertyBookingEngine['data'])) { + $bookingEngineToken = $propertyBookingEngine['data']['token']; + $bookingEngineUrl = Config::get('app.bookingEngineUrl').'/'.$propertyBookingEngine['data']['token']; + } + + $channelData['channel']['bookingEngineUrl'] = $bookingEngineUrl; + $channelData['channel']['bookingEngineToken'] = $bookingEngineToken; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $channelData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + + +} diff --git a/app/Http/Controllers/V1/PropertyCompetitorGroupController.php b/app/Http/Controllers/V1/PropertyCompetitorGroupController.php new file mode 100644 index 0000000..fbba37d --- /dev/null +++ b/app/Http/Controllers/V1/PropertyCompetitorGroupController.php @@ -0,0 +1,184 @@ +propertyCompetitorGroupService = $propertyCompetitorGroupService; + $this->request = $request; + $this->params = $request->all(); + } + + public function getPropertyCompetitorGroup(Request $request) + { + + $params = $this->params; + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['params']['property_id']], + ] + ]; + + // created_by, updated_by, created_at, updated_at alanları get cevaplarında gizlenir + $columns = ['id', 'property_id', 'name', 'status']; + $requestSelectResult = $this->propertyCompetitorGroupService->selectPropertyCompetitorGroup($requestSelectCriteria, $columns); + + if ($requestSelectResult['status'] != 'success') { + throw new ApiErrorException($requestSelectResult['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $requestSelectResult['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function syncPropertyCompetitorGroup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + DB::beginTransaction(); + + try { + + $requestParams = [ + 'id' => $this->params['params']['id'] ?? null, + 'property_id' => $this->params['params']['property_id'] ?? null, + 'name' => $this->params['params']['name'] ?? null, + 'status' => $this->params['params']['status'] ?? 1, + // Öncelik: auth üzerinden gelen kullanıcı + 'user_id' => ($this->request->auth->id ?? null) ?: ($this->params['params']['user_id'] ?? null), + ]; + + $requestResult = $this->propertyCompetitorGroupService->syncPropertyCompetitorGroup($requestParams); + + if ($requestResult['status'] != 'success') { + throw new ApiErrorException($requestResult['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $requestResult['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function getPropertyCompetitorGroupMapping(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + $requestParams = [ + 'property_id' => $this->params['params']['property_id'] ?? null, + 'competitor_group_id' => $this->params['params']['competitor_group_id'] ?? null, + ]; + + $result = $this->propertyCompetitorGroupService->getPropertyCompetitorGroupMapping($requestParams); + + if (($result['status'] ?? false) !== true && ($result['status'] ?? '') !== 'success') { + throw new ApiErrorException($result['message'] ?? 'unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $result['data'] ?? null]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function syncPropertyCompetitorGroupMapping(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + DB::beginTransaction(); + try { + $requestParams = [ + 'property_id' => $this->params['params']['property_id'] ?? null, + 'competitor_group_id' => $this->params['params']['competitor_group_id'] ?? null, + 'competitor' => $this->params['params']['competitor'] ?? [], + // Öncelik: auth üzerinden gelen kullanıcı + 'user_id' => ($this->request->auth->id ?? null) ?: ($this->params['params']['user_id'] ?? null), + ]; + + $result = $this->propertyCompetitorGroupService->syncPropertyCompetitorGroupMapping($requestParams); + + if (($result['status'] ?? false) !== true && ($result['status'] ?? '') !== 'success') { + throw new ApiErrorException($result['message'] ?? 'unknown_error'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $result['data'] ?? null]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + +} diff --git a/app/Http/Controllers/V1/PropertyConfigController.php b/app/Http/Controllers/V1/PropertyConfigController.php new file mode 100644 index 0000000..536e4dc --- /dev/null +++ b/app/Http/Controllers/V1/PropertyConfigController.php @@ -0,0 +1,165 @@ +request = $request; + $this->propertyConfigService = $propertyConfigService; + + } + + public function getPropertyConfig() + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'config_keys' => fillOnUndefined($params, 'config_keys'), + + + ]; + $propertyConfig = $this->propertyConfigService->getPropertyConfig($requestParams); + + if($propertyConfig['status'] != 'success'){ + throw new ApiErrorException($propertyConfig['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyConfig['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyConfig(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'config_keys' => fillOnUndefined($params, 'config_keys'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + + + + $propertyConfig = $this->propertyConfigService->propertyConfigUpdate($requestParams); + + if($propertyConfig['status'] != 'success'){ + + throw new ApiErrorException($propertyConfig['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyConfig['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function deletePropertyConfig(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'executive_type' => fillOnUndefined($params, 'executive_type'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + + + + $propertyConfig = $this->propertyConfigService->propertyConfigDelete($requestParams); + + if($propertyConfig['status'] != 'success'){ + + throw new ApiErrorException($propertyConfig['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyConfig['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} diff --git a/app/Http/Controllers/V1/PropertyContactController.php b/app/Http/Controllers/V1/PropertyContactController.php new file mode 100644 index 0000000..20a6bc5 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyContactController.php @@ -0,0 +1,175 @@ +request = $request; + $this->propertyService = $propertyService ; + $this->propertyContactService = $propertyContactService; + + } + + /** + * @return \Illuminate\Http\JsonResponse + */ + public function getPropertyContact() + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + $propertyContact = $this->propertyContactService->propertyContact($requestParams); + + if ($propertyContact['status'] != 'success'){ + throw new Exception($propertyContact['message']); + } + $requestParams = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ], + 'firstRow' => 1 + ]; + $property = $this->propertyService->select($requestParams, ['country']); + if($property['status'] != 'success'){ + throw new Exception($property['message']); + } + $propertyContact['data']['country_code'] = strtolower($property['data']['country']) ; + + + $responseData = $propertyContact['data']; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'official_name','tax_office', 'tax_number']; + $property = $this->propertyService->select($propertyRequest, $getPropertyFields); + + if($property['status'] != 'success'){ + throw new Exception($property['message']); + } + $responseData['get_property'] = $property['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + /** + * @param Request $request + * + * @return \Illuminate\Http\JsonResponse + */ + public function updateOrCreatePropertyContact(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'contact' => fillOnUndefined($params, 'contact'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + $propertyContact = $this->propertyContactService->propertyContactUpdateOrCreate($requestParams); + + + if($propertyContact['status'] != 'success'){ + + throw new ApiErrorException($propertyContact['message']); + } + + + $propertyData = fillOnUndefined($params, 'property_info'); + + if($propertyData){ + $updateData = []; + $validateKeys = ['official_name', 'tax_office', 'tax_number']; + foreach ($propertyData as $key => $value) { + + if (!in_array($key, $validateKeys)) { + throw new ApiErrorException(lang('Error Columns')); + } + $updateData[$key] = $value; + } + + if ($updateData) { + $updateData['updated_by'] = $requestParams['user_id']; + $updateData['updated_at'] = time(); + } + $propertyUpdateResult = $this->propertyService->update($params['property_id'], $updateData); + + if ($propertyUpdateResult['status'] != 'success') { + throw new ApiErrorException($propertyUpdateResult['message']); + } + + } + + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyContact['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + +} diff --git a/app/Http/Controllers/V1/PropertyContentController.php b/app/Http/Controllers/V1/PropertyContentController.php new file mode 100644 index 0000000..748625f --- /dev/null +++ b/app/Http/Controllers/V1/PropertyContentController.php @@ -0,0 +1,122 @@ +request = $request; + $this->propertyContentService = $propertyContentService; + + } + + public function getPropertyContent() + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $requestParams = [ + + 'language_code' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'category_id' => fillOnUndefined($params, 'category_id'), + + ]; + $propertyContent = $this->propertyContentService->propertyContent($requestParams); + + if($propertyContent['status'] != 'success'){ + throw new Exception($propertyContent['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyContent['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyContent(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'active_user' => $this->request->credentials->user_id, + 'locale' => fillOnUndefined($params, 'locale'), + 'contents' => fillOnUndefined($params, 'contents'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + + + + $propertyContent = $this->propertyContentService->propertyContentUpdate($requestParams); + + if($propertyContent['status'] != 'success'){ + + throw new ApiErrorException($propertyContent['message']); + } + + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyContent['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} diff --git a/app/Http/Controllers/V1/PropertyController.php b/app/Http/Controllers/V1/PropertyController.php new file mode 100644 index 0000000..e73f14d --- /dev/null +++ b/app/Http/Controllers/V1/PropertyController.php @@ -0,0 +1,1220 @@ +request = $request; + $this->propertyService = $propertyService; + $this->propertyTypeService = $propertyTypeService; + $this->propertyChainService = $propertyChainService; + $this->userPropertyMappingService = $userPropertyMappingService; + $this->permissionService = $permissionService; + $this->propertyConfigService = $propertyConfigService; + $this->siteConfigService = $siteConfigService; + $this->countryService = $countryService; + $this->generalTimezoneService = $generalTimezoneService; + $this->propertyNetworkService = $propertyNetworkService; + $this->productService = $productService; + $this->dashboardPlusService = $dashboardPlusService; + } + + public function listProperty(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'user_id' => $request->credentials->user_id, + + ]; + + $property = $this->propertyService->getPropertyList($requestParams); + + if ($property['status'] != 'success') { + throw new Exception($property['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $property['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getProperty(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $return = []; + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + + ]; + + $property = $this->propertyService->getProperty($requestParams); + + if ($property['status'] != 'success') { + throw new Exception($property['message']); + } + $return['get_property'] = $property['data']['get_property']; + $return['minimum_age_policies'] = $property['data']['minimum_age_policies']; + + + $propertyType = $this->propertyTypeService->getPropertyTypes($requestParams); + if ($propertyType['status'] != 'success') { + throw new ApiErrorException($propertyType['message']); + } + $return['property_type'] = $propertyType['data']['property_type']; + + + $propertyChains = $this->propertyChainService->getPropertyChains($requestParams); + if ($propertyChains['status'] != 'success') { + throw new ApiErrorException($propertyChains['message']); + } + $return['property_chains'] = $propertyChains['data']['property_chains']; + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + $countries = $this->countryService->getCountryList($criteria); + if ($countries['status'] != 'success') { + throw new ApiErrorException($countries['message']); + } + $return['countries'] = $countries['data']; + + $generalTimeZones = $this->generalTimezoneService->getAllaGeneralTimezone($params); + if ($generalTimeZones['status'] != 'success') { + throw new ApiErrorException($generalTimeZones['message']); + } + $return['general_timezone'] = $generalTimeZones['data']; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updateProperty(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_info' => fillOnUndefined($params, 'property_info'), + 'additional_info' => fillOnUndefined($params, 'additional_info'), + 'property_language_spoken' => fillOnUndefined($params, 'property_language_spoken'), + 'has_locale_name' => fillOnUndefined($params, 'has_locale_name', false), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + + + $property = $this->propertyService->propertyUpdate($requestParams); + + if ($property['status'] != 'success') { + + throw new ApiErrorException($property['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $property['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyContentCode(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'content_code' => fillOnUndefined($params, 'content_code') + ]; + + $validator = Validator::make($requestParams, ['content_code' => 'nullable|min:5|max:10']); + if (!empty($validator->errors()->messages())) { + throw new ApiErrorException('The verification code must be a minimum of 5 and a maximum of 10 digits.'); + } + + $property = $this->propertyService->update($params['property_id'], $requestParams); + + if ($property['status'] != 'success') { + throw new ApiErrorException($property['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function userPropertyMenu(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + $userId = $request->credentials->user_id; + $params = $request->params; + + $mappingPropertiesCriteria = [ + 'criteria' => [ + ['field' => 'user_id', 'condition' => '=', 'value' => $userId], + ], + 'with' => ['property'], + ]; + if (isset($params['property_id'])) { + $mappingPropertiesCriteria['criteria'][] = ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']]; + } + + $mappingProperties = $this->userPropertyMappingService->select($mappingPropertiesCriteria); + if (!$mappingProperties['data']) { + throw new ApiErrorException(lang('User Property mapping not found')); + } + + $propertyList = collect($mappingProperties['data'])->map(function ($value) use ($userId, $params) { + + $menuParams = [ + 'user_id' => $userId, + 'property_id' => $value['property']['id'], + 'locale' => fillOnUndefined($params, 'locale') + ]; + if (is_array($value['property'])) { + return $value['property'] = [ + 'id' => $value['property']['id'], + 'name' => $value['property']['name'], + 'property_menu' => $this->permissionService->getMenuTreeForUser($menuParams) + ]; + } + })->toArray(); + $return['property_list'] = $propertyList; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function propertyDashBoard(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $propertyDashBoardparams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + 'locale' => array_shift($request->header()['language']) + ]; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => true + ]; + $property = $this->propertyService->select($propertyRequest); + if ($property['status'] != 'success' || !isset($property['data'])) { + throw new ApiErrorException($property['message']); + } + $property = isset($property['data']) ? $property['data'] : []; + + $siteConfig = $this->propertyConfigService->propertyDashBoard($propertyDashBoardparams); + if ($siteConfig['status'] != 'success') { + throw new ApiErrorException($siteConfig['message']); + } + $return = $siteConfig['data']; + $return['hotel_name'] = $property['name']; + + $siteHints = $this->siteConfigService->siteHints($propertyDashBoardparams); + if ($siteHints['status'] != 'success') { + throw new ApiErrorException($siteConfig['message']); + } + $return['site_hints'] = $siteHints['data']; + + $return['content_code'] = $property['content_code']; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function propertyPabDashBoard(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestData = [ + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + + $property = $this->propertyNetworkService->getAllDashboardData($requestData); + + if ($property['status'] != 'success' || !isset($property['data'])) { + throw new ApiErrorException($property['message']); + } + + $property = isset($property['data']) ? $property['data'] : []; + + + /*$return = [ + 'hotel_name' => $property['name'], + + 'room_and_rate' => [ + 'room_count' => 16, + 'rate_count' => 8 + + ], + 'channels' => [ + 'channel_percent' => 25, + 'avail_channels' => 48, + 'saved_channel' => 12 + ], + 'reservation' => [ + [ + 'day' => 'Mon', + 'reservation_count' => 16, + 'reservation_percent' => 50 + ], + [ + 'day' => 'Tue', + 'reservation_count' => 5, + 'reservation_percent' => 15 + ], + [ + 'day' => 'Wed', + 'reservation_count' => 12, + 'reservation_percent' => 30 + ], + [ + 'day' => 'Thu', + 'reservation_count' => 15, + 'reservation_percent' => 50 + ], + [ + 'day' => 'Fri', + 'reservation_count' => 45, + 'reservation_percent' => 95 + ], + [ + 'day' => 'Sat', + 'reservation_count' => 47, + 'reservation_percent' => 97 + ], + [ + 'day' => 'Mon', + 'reservation_count' => 49, + 'reservation_percent' => 100 + ], + + ], + 'for_cast' => [ + 'total' => 40, + 'popular_channels' => [ + + [ + 'name' => 'Booking.Com', + 'cast' => 12, + ], + [ + 'name' => 'Agoda', + 'cast' => 13, + ], [ + 'name' => 'Trivago', + 'cast' => 15, + ], + ] + + ] + + + ];*/ + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $property]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function createProperty(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + DB::beginTransaction(); + + $return = []; + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $userId = $request->credentials->user_id; + $propertyInsertData = [ + 'name' => __('enw-your_property_name'), + 'property_chain' => 1, + 'status' => 2, + 'created_by' => $userId, + 'updated_by' => $userId, + 'created_at' => time(), + 'updated_at' => time(), + ]; + $propertyCreate = $this->propertyService->create($propertyInsertData); + if ($propertyCreate['status'] != 'success') { + throw new ApiErrorException($propertyCreate['message']); + } + + $return = $propertyCreate['data']; + $userPropertyMappingData = [ + 'user_id' => $userId, + 'status' => 1, + 'property_id' => $propertyCreate['data']['id'], + 'created_by' => $userId, + 'updated_by' => $userId, + 'created_at' => time(), + 'updated_at' => time(), + ]; + $userPropertyMappingCreate = $this->userPropertyMappingService->create($userPropertyMappingData); + if ($userPropertyMappingCreate['status'] != 'success') { + throw new ApiErrorException($userPropertyMappingCreate['message']); + } + + $propertyProducts = $this->productService->setDefaultPropertyProducts($userPropertyMappingData); + if ($propertyProducts['status'] != 'success') { + throw new ApiErrorException($propertyProducts['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $return]; + DB::commit(); + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + DB::rollBack(); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + DB::rollBack(); + + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + /* + * Dashboard Plus + */ + + public function propertyDashBoardPlus(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + //Date Check + $diffInDays = Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])); + if ($diffInDays > 180) { + throw new ApiErrorException('A maximum of 180 days of data can be retrieved.'); + } + if (Carbon::parse($params['finish_date'])->isBefore(Carbon::parse($params['start_date']))) { + throw new ApiErrorException('The finish date cannot be earlier than the start date.'); + } + + + $propertyCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => true + ]; + $property = $this->propertyService->select($propertyCriteria); + + if ($property['status'] != 'success' || !isset($property['data'])) { + throw new ApiErrorException($property['message']); + } + + $property = isset($property['data']) ? $property['data'] : []; + + + $dashBoardParam = [ + 'property_id' => $params['property_id'], + 'start_date' => fillOnUndefined($params, 'start_date', Carbon::now()->subYear()->toDateString()), + 'finish_date' => fillOnUndefined($params, 'finish_date', Carbon::now()->subDay()->toDateString()), + ]; + + + $todayCheckin = $this->dashboardPlusService->todayCheckin($dashBoardParam); + $todayCheckin = $todayCheckin['status'] ? $todayCheckin['data'] : 0; + + $todayCheckout = $this->dashboardPlusService->todayCheckout($dashBoardParam); + $todayCheckout = $todayCheckout['status'] ? $todayCheckout['data'] : 0; + + $lengthOfStay = $this->dashboardPlusService->lengthOfStay($dashBoardParam); + $lengthOfStay = $lengthOfStay['status'] ? $lengthOfStay['data'] : 0; + + $lengthOfBooking = $this->dashboardPlusService->lengthOfBooking($dashBoardParam); + $lengthOfBooking = $lengthOfBooking['status'] ? $lengthOfBooking['data'] : 0; + + $totalBooking = $this->dashboardPlusService->totalBooking($dashBoardParam); + $totalBooking = $totalBooking['status'] ? $totalBooking['data'] : []; + + $averageDailyRate = $this->dashboardPlusService->averageDailyRate($dashBoardParam); + $averageDailyRate = $averageDailyRate['status'] ? $averageDailyRate['data'] : []; + + $totalPax = $this->dashboardPlusService->totalPax($dashBoardParam); + $totalPax = $totalPax['status'] ? $totalPax['data'] : []; + + $responseData = [ + 'propertyId' => $property['id'], + 'propertyName' => $property['name'], + 'todayCheckin' => $todayCheckin, + 'todayCheckout' => $todayCheckout, + 'lengthOfStay' => $lengthOfStay, + 'lengthOfBooking' => $lengthOfBooking, + 'totalBooking' => $totalBooking, + 'totalPax' => $totalPax, + 'averageDailyRate' => $averageDailyRate, + ]; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function propertyDashBoardPlusWebVisitor(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + //Date Check + $diffInDays = Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])); + if ($diffInDays > 180) { + throw new ApiErrorException('A maximum of 180 days of data can be retrieved.'); + } + if (Carbon::parse($params['finish_date'])->isBefore(Carbon::parse($params['start_date']))) { + throw new ApiErrorException('The finish date cannot be earlier than the start date.'); + } + + $webVisitorParam = [ + 'property_id' => $params['property_id'], + 'start_date' => fillOnUndefined($params, 'start_date', Carbon::now()->subYear()->toDateString()), + 'finish_date' => fillOnUndefined($params, 'finish_date', Carbon::now()->subDay()->toDateString()), + ]; + + $webVisitor = $this->dashboardPlusService->webVisitor($webVisitorParam); + if (!$webVisitor['status']) { + throw new ApiErrorException($webVisitor['message']); + } + + $responseData = $webVisitor['data']; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function propertyDashBoardPlusGuestDemographic(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + //Date Check + $diffInDays = Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])); + if ($diffInDays > 180) { + throw new ApiErrorException('A maximum of 180 days of data can be retrieved.'); + } + if (Carbon::parse($params['finish_date'])->isBefore(Carbon::parse($params['start_date']))) { + throw new ApiErrorException('The finish date cannot be earlier than the start date.'); + } + + $webVisitorParam = [ + 'property_id' => $params['property_id'], + 'start_date' => fillOnUndefined($params, 'start_date', Carbon::now()->subYear()->toDateString()), + 'finish_date' => fillOnUndefined($params, 'finish_date', Carbon::now()->subDay()->toDateString()), + ]; + + $webVisitor = $this->dashboardPlusService->guestDemographic($webVisitorParam); + if (!$webVisitor['status']) { + throw new ApiErrorException($webVisitor['message']); + } + + $responseData = $webVisitor['data']; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function propertyDashBoardPlusTopChannel(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + //Date Check + $diffInDays = Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])); + if ($diffInDays > 180) { + throw new ApiErrorException('A maximum of 180 days of data can be retrieved.'); + } + if (Carbon::parse($params['finish_date'])->isBefore(Carbon::parse($params['start_date']))) { + throw new ApiErrorException('The finish date cannot be earlier than the start date.'); + } + + $topChannelParam = [ + 'property_id' => $params['property_id'], + 'start_date' => fillOnUndefined($params, 'start_date', Carbon::now()->subYear()->toDateString()), + 'finish_date' => fillOnUndefined($params, 'finish_date', Carbon::now()->subDay()->toDateString()), + ]; + + $webVisitor = $this->dashboardPlusService->topChannel($topChannelParam); + if (!$webVisitor['status']) { + throw new ApiErrorException($webVisitor['message']); + } + + $responseData = $webVisitor['data']; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function propertyDashBoardPlusChannelForecast(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + //Date Check + $diffInDays = Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])); + if ($diffInDays > 180) { + throw new ApiErrorException('A maximum of 180 days of data can be retrieved.'); + } + if (Carbon::parse($params['finish_date'])->isBefore(Carbon::parse($params['start_date']))) { + throw new ApiErrorException('The finish date cannot be earlier than the start date.'); + } + + $topChannelParam = [ + 'property_id' => $params['property_id'], + 'start_date' => fillOnUndefined($params, 'start_date', Carbon::now()->subYear()->toDateString()), + 'finish_date' => fillOnUndefined($params, 'finish_date', Carbon::now()->subDay()->toDateString()), + ]; + + $webVisitor = $this->dashboardPlusService->channelForecast($topChannelParam); + if (!$webVisitor['status']) { + throw new ApiErrorException($webVisitor['message']); + } + + $responseData = $webVisitor['data']; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + public function bookingEngineReport(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + + try { + + $reportTypes = [ + 'TRS' => 'Transaction Report', + 'GTR' => 'Guest Transaction Report', + 'DSR' => 'Date Search Report', + 'DSS' => 'Date Search Stay Report', + 'UCR' => 'User Country Report', + 'ULR' => 'User Language Report', + ]; + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + //Date Check + $diffInDays = Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])); + if ($diffInDays > 180) { + throw new ApiErrorException('A maximum of 180 days of data can be retrieved.'); + } + if (Carbon::parse($params['finish_date'])->isBefore(Carbon::parse($params['start_date']))) { + throw new ApiErrorException('The finish date cannot be earlier than the start date.'); + } + + if (!in_array(fillOnUndefined($params, 'type'), array_keys($reportTypes))) { + throw new ApiErrorException('Undefined report type.'); + + } + + $propertyCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => true + ]; + $property = $this->propertyService->select($propertyCriteria); + + if ($property['status'] != 'success' || !isset($property['data'])) { + throw new ApiErrorException($property['message']); + } + + $property = isset($property['data']) ? $property['data'] : []; + + $reportType = $params['type']; + + if($reportType != 'DSS') { + $searchData = vwBookingEngineSearch::where('property_id', $property['id']) + ->whereBetween('date', [$params['start_date'], $params['finish_date']]) + ->get()->toArray(); + } + + $responseData = []; + + switch ($reportType) { + case 'TRS': + + $responseData['search'] = collect($searchData)->count(); + $responseData['roomFound'] = collect($searchData)->where('status', 1)->count(); + $responseData['roomNotFound'] = collect($searchData)->where('status', 0)->count(); + $responseData['preBooking'] = collect($searchData)->where('status', 2)->count(); + $responseData['booking'] = collect($searchData)->where('status', 3)->count(); + + break; + case 'GTR': + + $occupancyCodes = collect($searchData)->groupBy('pax')->keys()->toArray(); + foreach ($occupancyCodes as $occupancyCode) { + $responseData[$occupancyCode]['text'] = occupancyCodeFormatted($occupancyCode); + $responseData[$occupancyCode]['count'] = collect($searchData)->where('pax', $occupancyCode)->count(); + } + + $responseData = collect($responseData)->sortByDesc('count')->toArray(); + + break; + case 'DSR': + + //Daily Intensity + $dailyIntensity = []; + foreach ($searchData as $data) { + $checkInDate = $data['checkin_date']; + $checkOutDate = $data['checkout_date']; + $dateDiff = Carbon::parse($data['checkout_date'])->diffInDays(Carbon::parse($data['checkin_date'])); + + for ($i = 0; $i < $dateDiff; $i++) { + $date = Carbon::parse($checkInDate)->addDays($i)->toDateString(); + + if (!isset($dailyIntensity[$date])) { + $dailyIntensity[$date]['text'] = Carbon::parse($date)->format('d.m.Y'); + $dailyIntensity[$date]['search'] = 0; + $dailyIntensity[$date]['roomFound'] = 0; + $dailyIntensity[$date]['roomNotFound'] = 0; + $dailyIntensity[$date]['preBooking'] = 0; + $dailyIntensity[$date]['booking'] = 0; + } + + $dailyIntensity[$date]['search']++; + $dailyIntensity[$date]['roomFound'] += $data['status'] == 1 ? 1 : 0; + $dailyIntensity[$date]['roomNotFound'] += $data['status'] == 0 ? 1 : 0; + $dailyIntensity[$date]['preBooking'] += $data['status'] == 2 ? 1 : 0; + $dailyIntensity[$date]['booking'] += $data['status'] == 3 ? 1 : 0; + + } + } + ksort($dailyIntensity); + $responseData = $dailyIntensity; + //Daily Intensity + break; + case 'DSS': + + //Daily Intensity + + + $searchData = vwBookingEngineSearch::where('property_id', $property['id']) + ->where('channel_id', $params['channel_id']) + ->whereBetween('checkin_date', [$params['start_date'], $params['finish_date']]) + ->orderByDesc('id') + ->get()->toArray(); + + + $dailyIntensity = []; + foreach ($searchData as $data) { + + $checkInDate = $data['checkin_date']; + $checkOutDate = $data['checkout_date']; + $dateDiff = Carbon::parse($data['checkout_date'])->diffInDays(Carbon::parse($data['checkin_date'])); + + for ($i = 0; $i < $dateDiff; $i++) { + $date = Carbon::parse($checkInDate)->addDays($i)->toDateString(); + + if(!Carbon::parse($date)->betweenIncluded($params['start_date'], $params['finish_date'])) { + continue; + } + + if (!isset($dailyIntensity[$date])) { + $dailyIntensity[$date]['text'] = Carbon::parse($date)->format('d.m.Y'); + $dailyIntensity[$date]['search'] = 0; + $dailyIntensity[$date]['roomFound'] = 0; + $dailyIntensity[$date]['roomNotFound'] = 0; + $dailyIntensity[$date]['preBooking'] = 0; + $dailyIntensity[$date]['booking'] = 0; + $dailyIntensity[$date]['bookingCode'] = []; + } + + $dailyIntensity[$date]['search']++; + $dailyIntensity[$date]['roomFound'] += $data['status'] == 1 ? 1 : 0; + $dailyIntensity[$date]['roomNotFound'] += $data['status'] == 0 ? 1 : 0; + $dailyIntensity[$date]['preBooking'] += $data['status'] == 2 ? 1 : 0; + $dailyIntensity[$date]['booking'] += $data['status'] == 3 ? 1 : 0; + + if(!empty($data['booking_code'])) { + $dailyIntensity[$date]['bookingCode'][] = $data['booking_code']; + } + + } + } + ksort($dailyIntensity); + $responseData = $dailyIntensity; + //Daily Intensity + break; + case 'UCR': + + $countryList = Country::all()->toArray(); + $countryCodes = collect($searchData)->where('country_code', '!=', null)->groupBy('country_code')->keys()->toArray(); + + foreach ($countryCodes as $countryCode) { + $countryText = $countryCode; + $countryTextCheck = collect($countryList)->where('country_code', mb_strtoupper($countryCode))->first(); + if (!empty($countryTextCheck)) { + $countryText = $countryTextCheck['name']; + } + $responseData[$countryCode]['text'] = $countryText; + $responseData[$countryCode]['search'] = collect($searchData)->where('country_code', $countryCode)->count(); + $responseData[$countryCode]['roomFound'] = collect($searchData)->where('country_code', $countryCode)->where('status', 1)->count(); + $responseData[$countryCode]['roomNotFound'] = collect($searchData)->where('country_code', $countryCode)->where('status', 0)->count(); + $responseData[$countryCode]['preBooking'] = collect($searchData)->where('country_code', $countryCode)->where('status', 2)->count(); + $responseData[$countryCode]['booking'] = collect($searchData)->where('country_code', $countryCode)->where('status', 3)->count(); + } + + break; + case 'ULR': + + $languageList = Language::all()->toArray(); + $languageCodes = collect($searchData)->groupBy('language_code')->keys()->toArray(); + foreach ($languageCodes as $languageCode) { + $languageText = $languageCode; + $languageTextCheck = collect($languageList)->where('code', $languageCode)->first(); + if (!empty($languageTextCheck)) { + $languageText = $languageTextCheck['name']; + } + $responseData[$languageCode]['text'] = $languageText; + $responseData[$languageCode]['search'] = collect($searchData)->where('language_code', $languageCode)->count(); + $responseData[$languageCode]['roomFound'] = collect($searchData)->where('language_code', $languageCode)->where('status', 1)->count(); + $responseData[$languageCode]['roomNotFound'] = collect($searchData)->where('language_code', $languageCode)->where('status', 0)->count(); + $responseData[$languageCode]['preBooking'] = collect($searchData)->where('language_code', $languageCode)->where('status', 2)->count(); + $responseData[$languageCode]['booking'] = collect($searchData)->where('language_code', $languageCode)->where('status', 3)->count(); + } + + $responseData = collect($responseData)->sortByDesc('search')->toArray(); + + break; + case 'default': + break; + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + public function couponCodeReport(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400]; + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + //Date Check + $diffInDays = Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['finish_date'])); + if ($diffInDays > 180) { + throw new ApiErrorException('A maximum of 180 days of data can be retrieved.'); + } + if (Carbon::parse($params['finish_date'])->isBefore(Carbon::parse($params['start_date']))) { + throw new ApiErrorException('The finish date cannot be earlier than the start date.'); + } + + $propertyCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => true + ]; + $property = $this->propertyService->select($propertyCriteria); + + if ($property['status'] != 'success' || !isset($property['data'])) { + throw new ApiErrorException($property['message']); + } + + $property = isset($property['data']) ? $property['data'] : []; + + + $startDate = Carbon::parse($params['start_date'])->startOfDay()->toDateTimeString(); + $finishDate = Carbon::parse($params['finish_date'])->endOfDay()->toDateTimeString(); + + $status = fillOnUndefined($params,'status', 1); + + if (empty(fillOnUndefined($params, 'code'))) { + $searchData = vwBookingSummaryAll::where('property_id', $property['id']) + ->where('coupon_code', '!=', null) + ->where('status', '=', $status) + ->whereBetween('time', [$startDate, $finishDate]) + ->with('bookingStatus') + ->get()->toArray(); + } else { + $searchData = vwBookingSummaryAll::where('property_id', $property['id']) + ->where('coupon_code', $params['code']) + ->where('status', '=', $status) + ->whereBetween('time', [$startDate, $finishDate]) + ->with('bookingStatus') + ->get()->toArray(); + } + + $responseData = []; + + foreach ($searchData as $data) { + + $responseData[] = [ + 'id' => $data['id'], + 'transaction_period' => $data['transaction_period'], + 'checkout_period' => $data['checkout_period'], + 'code' => $data['coupon_code'], + 'booking_code' => $data['booking_code'], + 'name_surname' => $data['name_surname'], + 'checkin_date' => $data['checkin_date'], + 'checkout_date' => $data['checkout_date'], + 'length_of_stay' => $data['length_of_stay'], + 'length_of_booking' => $data['length_of_booking'], + 'total' => $data['total'], + 'total_formatted' => $data['total_formatted'], + 'currency_code' => $data['currency_code'], + 'status' => $data['status'], + 'status_name' => $data['booking_status']['name'], + 'status_language_key' => $data['booking_status']['language_key'], + 'time' => $data['time'], + 'time_formatted' => Carbon::parse($data['time'])->format('d.m.Y H:i:s') + ]; + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 400; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + +} diff --git a/app/Http/Controllers/V1/PropertyCouponController.php b/app/Http/Controllers/V1/PropertyCouponController.php new file mode 100644 index 0000000..8c21cd4 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyCouponController.php @@ -0,0 +1,204 @@ +params = Input::all(); + $this->params = $this->params['params']; + $this->propertyChannelCouponService = $propertyChannelCouponService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyChannelCouponValidator = $propertyChannelCouponValidator; + } + + protected function propertyChannelMappingCheck($propertyId, $channelId) + { + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] == 'success') { + if (empty($propertyChannelMapping['data'])) { + return false; + } else { + return true; + } + } else { + return false; + } + + } + + public function getPropertyChannelCoupon(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $propertyChannelMappingCheck = $this->propertyChannelMappingCheck($this->params['property_id'], $this->params['channel_id']); + if (!$propertyChannelMappingCheck) { + throw new ApiErrorException('Transactions cannot be made through a unconnected channel.'); + } + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['channel_id']], + ], + ]; + $columns = ['id', 'title', 'property_id', 'channel_id', 'code', 'start_date', 'end_date', 'reservation_start_date', 'reservation_end_date', 'type', 'value', 'is_notify', 'email', 'status']; + $requestSelectResult = $this->propertyChannelCouponService->select($requestSelectCriteria, $columns); + + $propertyChannelCoupon = []; + if ($requestSelectResult['status'] == 'success') { + $propertyChannelCoupon = $requestSelectResult['data']; + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyChannelCoupon]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function syncPropertyChannelCoupon(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $validationResult = $this->propertyChannelCouponValidator->validate($this->params); + + if ($validationResult->errors()->first()) { + $errors = $validationResult->errors()->all(); + throw new ApiErrorException($errors); + } + + $propertyChannelMappingCheck = $this->propertyChannelMappingCheck($this->params['property_id'], $this->params['channel_id']); + if (!$propertyChannelMappingCheck) { + throw new ApiErrorException('Transactions cannot be made through a unconnected channel.'); + } + + $propertyChannelCoupon = []; + if (isset($this->params['id'])) { + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $this->params['id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['channel_id']], + ], + 'firstRow' => true + ]; + $columns = ['id', 'property_id', 'channel_id', 'code', 'start_date', 'end_date', 'type', 'value', 'status']; + $requestSelectResult = $this->propertyChannelCouponService->select($requestSelectCriteria, $columns); + + + if ($requestSelectResult['status'] == 'success') { + $propertyChannelCoupon = $requestSelectResult['data']; + } + } + + DB::beginTransaction(); + + if (empty($propertyChannelCoupon)) { + + $createParam = [ + 'title' => fillOnUndefined($this->params, 'title'), + 'property_id' => $this->params['property_id'], + 'channel_id' => $this->params['channel_id'], + 'code' => $this->params['code'], + 'start_date' => $this->params['start_date'], + 'end_date' => $this->params['end_date'], + 'reservation_start_date' => fillOnUndefined($this->params, 'reservation_start_date'), + 'reservation_end_date' => fillOnUndefined($this->params, 'reservation_end_date'), + 'type' => $this->params['type'], + 'value' => $this->params['value'], + 'is_notify' => fillOnUndefined($this->params, 'is_notify'), + 'email' => fillOnUndefined($this->params, 'email'), + 'status' => fillOnUndefined($this->params, 'status', 1), + 'created_by' => $request->auth->id, + 'updated_by' => $request->auth->id, + ]; + + $syncPropertyChannelCoupon = $this->propertyChannelCouponService->create($createParam); + } else { + $this->params['updated_by'] = $request->auth->id; + $syncPropertyChannelCoupon = $this->propertyChannelCouponService->update($propertyChannelCoupon['id'], $this->params); + + } + + if ($syncPropertyChannelCoupon['status'] != 'success') { + throw new ApiErrorException($syncPropertyChannelCoupon['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $syncPropertyChannelCoupon['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} + diff --git a/app/Http/Controllers/V1/PropertyExecutiveController.php b/app/Http/Controllers/V1/PropertyExecutiveController.php new file mode 100644 index 0000000..b495b18 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyExecutiveController.php @@ -0,0 +1,271 @@ +request = $request; + $this->propertyExecutiveService = $propertyExecutiveService; + $this->propertyService = $propertyService; + + } + + public function getPropertyExecutive() + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + + ]; + $propertyExecutive = $this->propertyExecutiveService->getPropertyExecutive($requestParams); + + if($propertyExecutive['status'] != 'success'){ + throw new Exception($propertyExecutive['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyExecutive['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + public function listPropertyExecutive() + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + + ]; + $propertyExecutive = $this->propertyExecutiveService->listPropertyExecutive($requestParams); + if($propertyExecutive['status'] != 'success'){ + throw new Exception($propertyExecutive['message']); + } + + $requestParams = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ], + 'firstRow' => 1 + ]; + $property = $this->propertyService->select($requestParams, ['country']); + if($property['status'] != 'success'){ + throw new Exception($property['message']); + } + $propertyExecutive['data']['country_code'] = strtolower($property['data']['country']) ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyExecutive['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyExecutive(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'executive_type' => fillOnUndefined($params, 'executive_type'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + + $propertyExecutive = $this->propertyExecutiveService->propertyExecutiveAdd($requestParams); + if($propertyExecutive['status'] != 'success'){ + throw new ApiErrorException($propertyExecutive['message']); + } + + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyExecutive['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyExecutive(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + $params = $this->request->params; + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'executive_type' => fillOnUndefined($params, 'executive_type'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + $oldPropertyExecutive = $this->propertyExecutiveService->listPropertyExecutive($requestParams); + + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'executive_type' => fillOnUndefined($params, 'executive_type'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + + $propertyExecutive = $this->propertyExecutiveService->propertyExecutiveUpdate($requestParams); + + if($propertyExecutive['status'] != 'success'){ + throw new ApiErrorException($propertyExecutive['message']); + } + + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyExecutive['data']]; + + } catch (ApiErrorException $e) { + $response['data'] = $oldPropertyExecutive['data'] ; + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['data'] = $oldPropertyExecutive['data'] ; + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function deletePropertyExecutive(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_executive_id' => fillOnUndefined($params, 'property_executive_id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + + + + $propertyExecutive = $this->propertyExecutiveService->propertyExecutivePassive($requestParams); + + if($propertyExecutive['status'] != 'success'){ + + throw new ApiErrorException($propertyExecutive['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyExecutive['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} diff --git a/app/Http/Controllers/V1/PropertyFactController.php b/app/Http/Controllers/V1/PropertyFactController.php new file mode 100644 index 0000000..883c64d --- /dev/null +++ b/app/Http/Controllers/V1/PropertyFactController.php @@ -0,0 +1,183 @@ +request = $request; + $this->propertyFactService = $propertyFactService; + + } + + public function getPropertyFact(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestFact = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $getPropertyFact = $this->propertyFactService->getPropertyFact($requestFact); + + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['get_facts' => $getPropertyFact['data']] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function searchPropertyFact(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestFact = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'search_term' => fillOnUndefined($params, 'search_term'), + ]; + + $getPropertyFact = $this->propertyFactService->searchPropertyFact($requestFact); + + if($getPropertyFact['status'] != 'success'){ + + throw new ApiErrorException($getPropertyFact['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['get_facts' => $getPropertyFact['data']] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getSubCategoryFacts(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestFact = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'sub_category_id' => fillOnUndefined($params, 'sub_category_id'), + ]; + + $factRequestData = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['sub_category_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'type', 'condition' => '=', 'value' => 0], + ], + 'with' => ['propertyFactParent'], + 'firstRow' => 1 + ]; + $thisFact = $this->propertyFactService->select($factRequestData, ['id', 'parent_id', 'name', 'language_key', 'title_language_key']); + if($thisFact['status'] != 'success' || !$thisFact['data']){ + throw new ApiErrorException('Fact data not found') ; + } + + + $subCategoryFacts = $this->propertyFactService->getSubCategoryFacts($requestFact); + if($subCategoryFacts['status'] != 'success'){ + throw new ApiErrorException($subCategoryFacts['message']) ; + } + + $requestFact['parent_id'] = $thisFact['data']['parent_id'] ; + + $thisMenus = $this->propertyFactService->getSubCategoryMenus($requestFact); + if($thisMenus['status'] != 'success'){ + throw new ApiErrorException($thisMenus['message']) ; + } + $thisSubCategory = $thisFact['data'] ; + $thisMainCategory = $thisSubCategory['property_fact_parent'] ; + unset($thisSubCategory['property_fact_parent']) ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => + [ + 'get_facts' => $subCategoryFacts['data'], + 'get_menus' => $thisMenus['data'], + 'this_sub_category' => $thisSubCategory, + 'this_main_category' => $thisMainCategory, + ] + + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} diff --git a/app/Http/Controllers/V1/PropertyFactMappingController.php b/app/Http/Controllers/V1/PropertyFactMappingController.php new file mode 100644 index 0000000..9019f37 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyFactMappingController.php @@ -0,0 +1,165 @@ +request = $request; + $this->propertyFactMappingService = $propertyFactMappingService; + + } + + public function getPropertyFactMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = json_decode($request->getContent(),1); + + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $params]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function removePropertyFactMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(),1); + + $params = $params['property_fact_mapping_remove'] ; + + $requestParams = [ + 'active_user' => $this->request->credentials->user_id, + 'facts' => fillOnUndefined($params, 'facts'), + ]; + + $removeResponse = $this->propertyFactMappingService->removePropertyFactMapping($requestParams); + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $removeResponse]; + //$response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyContent['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function addPropertyFactMapping() + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try + { + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = json_decode($this->request->getContent(),1); + $params = current($params); + $params['user_id'] = $this->request->auth->id; + + $response = $this->propertyFactMappingService->addPropertyFactMapping($params); + + + + }catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePropertyFactMapping(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try + { + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = json_decode($this->request->getContent(),1); + $params = current($params); + $params['user_id'] = $this->request->auth->id; + + $response = $this->propertyFactMappingService->updatePropertyFactMapping($params); + + }catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + + +} diff --git a/app/Http/Controllers/V1/PropertyNonrefundableController.php b/app/Http/Controllers/V1/PropertyNonrefundableController.php new file mode 100644 index 0000000..b64fc45 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyNonrefundableController.php @@ -0,0 +1,130 @@ +request = $request; + $this->propertyNonrefundableService = $propertyNonrefundableService; + } + + public function createPropertyChannelNonrefundable(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $getPropertyNonrefundable = $this->propertyNonrefundableService->createForm($params); + if($getPropertyNonrefundable['status'] != "success"){ + throw new ApiErrorException($getPropertyNonrefundable['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $getPropertyNonrefundable['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function storePropertyChannelNonrefundable(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + $updateResponse = $this->propertyNonrefundableService->storePropertyNonrefundable($params); + + if($updateResponse['status'] != 'success'){ + throw new ApiErrorException($updateResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $updateResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPropertyChannelNonrefundable(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $getPropertyNonrefundable = $this->propertyNonrefundableService->getNonrefundableData($params); + if($getPropertyNonrefundable['status'] != "success"){ + throw new ApiErrorException($getPropertyNonrefundable['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['channel_list' => $getPropertyNonrefundable['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + +} diff --git a/app/Http/Controllers/V1/PropertyOfferController.php b/app/Http/Controllers/V1/PropertyOfferController.php new file mode 100644 index 0000000..2f13313 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyOfferController.php @@ -0,0 +1,1073 @@ +request = $request; + $this->mailer = $mailer; + $this->offerService = $offerService; + $this->propertyFactService = $propertyFactService; + $this->propertyExecutiveService = $propertyExecutiveService; + $this->propertyRoomService = $propertyRoomService; + $this->propertyPhotoService = $propertyPhotoService; + $this->languageService = $languageService; + $this->currencyService = $currencyService; + $this->propertyBrandService = $propertyBrandService; + $this->propertyService = $propertyService; + $this->propertyContactService = $propertyContactService; + $this->propertyPaymentService = $propertyPaymentService; + // $this->qrCode = $qrCode; + + } + + + public function listPropertyOffer(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ], + 'with' => ['offerAcceptStatus','paymentTransaction'], + "orderBy" => [ + ["field" => "id", "value" => "DESC"] + ] + ]; + $ticketCode = fillOnUndefinedAndEmpty($params, 'ticket_code'); + if ($ticketCode) { + $propertyRequest['criteria'][] = ['field' => 'ticket_code', 'condition' => 'like', 'value' => '%' . $ticketCode . '%']; + } + + $propertyOffers = $this->offerService->select($propertyRequest); + + if ($propertyOffers['status'] != 'success') { + throw new Exception($propertyOffers['message']); + } + + $offerList = []; + foreach ($propertyOffers['data'] as $offerKey => $value) { + $offerList[$offerKey] = $value; + $offerList[$offerKey]['total'] = moneyDoubleFormat($value['total']); + if(!empty($value['payment_type_mapping_id'])) { + $offerList[$offerKey]['is_payment_received'] = false; + $paymentTransactionList = collect($value['payment_transaction']); + if($paymentTransactionList->where('status',1)->count() > 0) { + $offerList[$offerKey]['is_payment_received'] = true; + } + } + + unset($offerList[$offerKey]['payment_transaction']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $offerList]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function createPropertyOffer(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + $getPropertyFact = $this->propertyFactService->getPropertyFact($requestParams); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $getPropertyFact = $this->propertyFactService->getPropertyOfferFilter($getPropertyFact['data'], []); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $accommodationTypes = $getPropertyFact['data']['accommodation_types']; + $propertyFacts = $getPropertyFact['data']['get_facts']; + + $propertyExecutive = $this->propertyExecutiveService->listPropertyExecutive($requestParams); + if ($propertyExecutive['status'] != 'success') { + throw new Exception($propertyExecutive['message']); + } + + + $propertyRooms = $this->propertyRoomService->getPropertyRooms($requestParams); + if ($propertyRooms['status'] != 'success') { + throw new ApiErrorException($propertyRooms['message']); + } + + $propertyPhotos = $this->propertyPhotoService->getPropertyPhotos($params); + if ($propertyPhotos['status'] != 'success') { + throw new ApiErrorException($propertyPhotos['message']); + } + $languageCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + + $languages = $this->languageService->getAllLanguages($languageCriteria, ['id', 'code', 'name', 'status', 'language_key']); + if ($languages['status'] != 'success') { + throw new ApiErrorException($languages['message']); + } + + + $currencies = $this->currencyService->getCurrencyList(['justBasicCurrencies' => true]); + if ($currencies['status'] != 'success') { + throw new ApiErrorException($currencies['message']); + } + + $offerConfirmType = $this->offerService->selectOfferConfirmType([], ['code', 'name', 'language_key']); + if ($offerConfirmType['status'] != 'success') { + throw new ApiErrorException($offerConfirmType['message']); + } + + $offerConfirmType = $offerConfirmType['data']; + + $paymentList = $this->propertyPaymentService->getPaymentMappingList($params); + if ($paymentList['status'] != 'success') { + throw new ApiErrorException($paymentList['message']); + } + + $paymentListType = collect($paymentList['data'])->map(function ($payment) { + return [ + 'id' => $payment['id'], + 'name' => $payment['name'], + 'currency_code' => $payment['currency_code'], + 'title' => $payment['name'] . ' - ' . $payment['currency_code'], + 'status' => $payment['status'] + ]; + })->sortBy('title')->toArray(); + $paymentListType = array_values($paymentListType); + + $responseData = [ + 'get_facts' => $propertyFacts, + 'accommodation_types' => $accommodationTypes, + 'executives' => $propertyExecutive['data']['executives'], + 'property_rooms' => $propertyRooms['data'], + 'photos' => $propertyPhotos['data'], + 'languages' => $languages['data'], + 'currencies' => $currencies['data'], + 'offer_confirm_type' => $offerConfirmType, + 'payment_list_type' => $paymentListType, + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function storePropertyOffer(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = $params; + $requestParams['user_id'] = $this->request->auth->id; + + $storeOffer = $this->offerService->addNewOffer($requestParams); + if ($storeOffer['status'] != 'success') { + throw new ApiErrorException($storeOffer['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $storeOffer['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function getPricePropertyOffer(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = $params; + $requestParams['user_id'] = $this->request->auth->id; + + $checkPropertyOfferPermissionAccess = $this->offerService->checkPropertyOfferPermissionAccess(['property_id' => $params['property_id'], 'offer_id' => $params['offer_id']]); + if ($checkPropertyOfferPermissionAccess['status'] != 'success') { + throw new ApiErrorException($checkPropertyOfferPermissionAccess['message']); + } + + $storeOffer = $this->offerService->getPriceList($requestParams); + if ($storeOffer['status'] != 'success') { + throw new ApiErrorException($storeOffer['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $storeOffer['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function storePricePropertyOffer(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = $params; + $requestParams['user_id'] = $this->request->auth->id; + + $checkPropertyOfferPermissionAccess = $this->offerService->checkPropertyOfferPermissionAccess(['property_id' => $params['property_id'], 'offer_id' => $params['offer_id']]); + if ($checkPropertyOfferPermissionAccess['status'] != 'success') { + throw new ApiErrorException($checkPropertyOfferPermissionAccess['message']); + } + + $storeOffer = $this->offerService->storePriceList($requestParams); + if ($storeOffer['status'] != 'success') { + throw new ApiErrorException($storeOffer['message']); + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $storeOffer['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function editPropertyOffer(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $checkPropertyOfferPermissionAccess = $this->offerService->checkPropertyOfferPermissionAccess(['property_id' => $params['property_id'], 'offer_id' => $params['offer_id']]); + if ($checkPropertyOfferPermissionAccess['status'] != 'success') { + throw new ApiErrorException($checkPropertyOfferPermissionAccess['message']); + } + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'offer_id' => fillOnUndefined($params, 'offer_id'), + ]; + + $getOffer = $this->offerService->findOffer($requestParams); + if ($getOffer['status'] != 'success') { + throw new ApiErrorException($getOffer['message']); + } + + $offerPrice = $getOffer['data']['offer_price']; + $startDate = collect($offerPrice)->keyBy('date')->keys()->min(); + $endDate = collect($offerPrice)->keyBy('date')->keys()->max(); + + $getPropertyFact = $this->propertyFactService->getPropertyFact($requestParams); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + + $offerAccommodationMapping = collect($getOffer['data']['offer_accommodation_mapping'])->keyBy('id')->keys()->all(); + $offerFactMapping = collect($getOffer['data']['offer_fact_mapping'])->keyBy('id')->keys()->all(); + $offerAllFactIds = array_merge($offerAccommodationMapping, $offerFactMapping); + + $getPropertyFact = $this->propertyFactService->getPropertyOfferFilter($getPropertyFact['data'], $offerAllFactIds); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $accommodationTypes = $getPropertyFact['data']['accommodation_types']; + $propertyFacts = $getPropertyFact['data']['get_facts']; + + + $propertyExecutive = $this->propertyExecutiveService->listPropertyExecutive($requestParams); + if ($propertyExecutive['status'] != 'success') { + throw new Exception($propertyExecutive['message']); + } + + $propertyRooms = $this->propertyRoomService->getPropertyRooms($requestParams); + if ($propertyRooms['status'] != 'success') { + throw new ApiErrorException($propertyRooms['message']); + } + + $propertyPhotos = $this->propertyPhotoService->getPropertyPhotos($params); + if ($propertyPhotos['status'] != 'success') { + throw new ApiErrorException($propertyPhotos['message']); + } + + $languageCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + + $languages = $this->languageService->getAllLanguages($languageCriteria, ['id', 'code', 'name', 'status', 'language_key']); + if ($languages['status'] != 'success') { + throw new ApiErrorException($languages['message']); + } + + + $currencies = $this->currencyService->getCurrencyList(); + if ($currencies['status'] != 'success') { + throw new ApiErrorException($currencies['message']); + } + + $offerConfirmTypes = $this->offerService->selectOfferConfirmType([], ['code', 'name', 'language_key']); + if ($offerConfirmTypes['status'] != 'success') { + throw new ApiErrorException($offerConfirmTypes['message']); + } + + $offerConfirmType = []; + foreach ($offerConfirmTypes['data'] as $offerConfirmTypeData) { + + $isSelectedOfferConfirmType = false; + if ($offerConfirmTypeData['code'] == $getOffer['data']['confirm_type']) { + $isSelectedOfferConfirmType = true; + } + + $offerConfirmType[] = [ + 'code' => $offerConfirmTypeData['code'], + 'name' => $offerConfirmTypeData['name'], + 'language_key' => $offerConfirmTypeData['language_key'], + 'is_selected' => $isSelectedOfferConfirmType + ]; + } + + $paymentList = $this->propertyPaymentService->getPaymentMappingList($params); + if ($paymentList['status'] != 'success') { + throw new ApiErrorException($paymentList['message']); + } + + $paymentListType = collect($paymentList['data'])->map(function ($payment) use ($getOffer) { + + + $isSelectedPaymentType = false; + if ($payment['id'] == $getOffer['data']['payment_type_mapping_id']) { + $isSelectedPaymentType = true; + } + + return [ + 'id' => $payment['id'], + 'name' => $payment['name'], + 'currency_code' => $payment['currency_code'], + 'title' => $payment['name'] . ' - ' . $payment['currency_code'], + 'status' => $payment['status'], + 'is_selected' => $isSelectedPaymentType + ]; + })->sortBy('title')->toArray(); + + $paymentListType = array_values($paymentListType); + + $responseData = [ + 'get_offer' => $getOffer['data'], + 'get_facts' => $propertyFacts, + 'accommodation_types' => $accommodationTypes, + 'executives' => $propertyExecutive['data']['executives'], + 'property_rooms' => $propertyRooms['data'], + 'photos' => $propertyPhotos['data'], + 'languages' => $languages['data'], + 'currencies' => $currencies['data'], + 'start_date' => fillOnUndefined($getOffer['data'], "check_in"), + 'end_date' => fillOnUndefined($getOffer['data'], "check_out"), + 'offer_confirm_type' => $offerConfirmType, + 'payment_list_type' => $paymentListType, + ]; + + $responseData = $this->offerService->editFormCheckValues($responseData); + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyOffer(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = $params; + $requestParams['user_id'] = $this->request->auth->id; + + $checkPropertyOfferPermissionAccess = $this->offerService->checkPropertyOfferPermissionAccess(['property_id' => $params['property_id'], 'offer_id' => $params['offer_id']]); + if ($checkPropertyOfferPermissionAccess['status'] != 'success') { + throw new ApiErrorException($checkPropertyOfferPermissionAccess['message']); + } + + $storeOffer = $this->offerService->updateOffer($requestParams); + if ($storeOffer['status'] != 'success') { + throw new ApiErrorException($storeOffer['message']); + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $storeOffer['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function listPropertyOfferPublish(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $checkOffer = [ + 'criteria' => [ + ['field' => 'offer_code', 'condition' => '=', 'value' => fillOnUndefined($params, 'offer_code')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['paymentTransaction'], + 'whereIn' => [ + ['field' => 'accept_status', 'value' => [1, 2, 3]], + ], + 'firstRow' => 1 + ]; + $checkOfferData = $this->offerService->select($checkOffer); + if ($checkOfferData['status'] != "success" || !$checkOfferData['data']) { + throw new ApiErrorException('offer not found'); + } + $params['offer_id'] = $checkOfferData['data']['id']; + $params['property_id'] = $checkOfferData['data']['property_id']; + $params['offer_code'] = $checkOfferData['data']['offer_code']; + + /* $offerUrl = config('app.client_server')."/offer/".$params['offer_code']; + $generatedQRCode = $this->qrCode->generate($offerUrl, 250); + + if($generatedQRCode['status'] === false){ + throw new ApiErrorException($generatedQRCode['message']); + } + + $qrCode = (string) Image::make($generatedQRCode['data'])->encode('data-url');*/ + + $requestParams = $params; + + $getOffer = $this->offerService->findOffer($requestParams); + + if ($getOffer['status'] != 'success') { + throw new ApiErrorException($getOffer['message']); + } + + $offerLanguage = $getOffer['data']['language']; + $requestParams['locale'] = $offerLanguage; + + $requestBrand = [ + + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + + ], + 'firstRow' => 1 + ]; + $getPropertyBrand = $this->propertyBrandService->select($requestBrand); + $getPropertyBrand['data']['color_codes'] = json_decode($getPropertyBrand['data']['color_codes'], true); + if ($getPropertyBrand['status'] != "success") { + throw new ApiErrorException($getPropertyBrand['message']); + } + + $defaultLogo = "/assets/img/logo/logo.png"; + $getPropertyBrand['data']['logo_url'] = isset($getPropertyBrand['data']['logo_name']) ? Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/logo/' . $getPropertyBrand['data']['logo_name'] . '_250x250.' . $getPropertyBrand['data']['logo_file_ext'] : $defaultLogo; + + $getOffer['data']['expire_date'] = humanReadableDate($getOffer['data']['expire_date']); + $getPropertyFact = $this->propertyFactService->getPropertyFact($requestParams); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + $offerAccommodationMapping = collect($getOffer['data']['offer_accommodation_mapping'])->keyBy('id')->keys()->all(); + $offerFactMapping = collect($getOffer['data']['offer_fact_mapping'])->keyBy('id')->keys()->all(); + $offerAllFactIds = array_merge($offerAccommodationMapping, $offerFactMapping); + $getPropertyFact = $this->propertyFactService->getPropertyOfferFilterForHtml($getPropertyFact['data'], $offerAllFactIds); + if ($getPropertyFact['status'] != 'success') { + throw new ApiErrorException($getPropertyFact['message']); + } + + + $getProperty = $this->propertyService->getPropertyDetail($requestParams); + + if ($getProperty['status'] != 'success') { + throw new ApiErrorException($getProperty['message']); + } + + $propertyContact = $this->propertyContactService->propertyContact($requestParams); + + if ($propertyContact['status'] != 'success') { + throw new Exception($propertyContact['message']); + } + + $getProperty['data']['contact'] = $propertyContact['data']['property_contact']; + $getRoomPriceParams = [ + + 'offer_id' => $getOffer['data']['id'], + 'property_id' => fillOnUndefined($params, 'property_id'), + 'locale' => $offerLanguage, + + ]; + + $getRoomPrice = $this->offerService->getPrice($getRoomPriceParams); + + if ($getRoomPrice['status'] != 'success') { + throw new ApiErrorException($getRoomPrice['message']); + } + + + $offerPayment = []; + if(!empty($checkOfferData['data']['payment_type_mapping_id'])) { + $paymentTransaction = collect($checkOfferData['data']['payment_transaction']); + + $paymentLink = $paymentTransaction->where('code',null)->first(); + + if(isset($paymentLink['manuelPaymentLink'])) { + $offerPayment['payment_link'] = $paymentLink['manuelPaymentLink']; + $offerPayment['is_payment_received'] = false; + } + + if($paymentTransaction->where('status',1)->count() > 0) { + $offerPayment['is_payment_received'] = true; + } + + } + + unset($getOffer['data']['offer_accommodation_mapping']); + unset($getOffer['data']['offer_fact_mapping']); + unset($getOffer['data']['offer_price']); + unset($getOffer['data']['offer_room_mapping']); + + $offerPublishData = [ + 'property_brand' => fillOnUndefined($getPropertyBrand, "data", []), + 'offer' => fillOnUndefined($getOffer, 'data', []), + 'property' => fillOnUndefined($getProperty, 'data', []), + 'property_fact' => fillOnUndefined($getPropertyFact, 'data', []), + 'room_price' => fillOnUndefined($getRoomPrice, 'data', []), + 'offer_payment' => $offerPayment + // "qr_code" => $qrCode + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $offerPublishData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 404; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePropertyOfferStatus(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = $params; + + $requestParams['user_id'] = $this->request->auth->id; + + $checkPropertyOfferPermissionAccess = $this->offerService->checkPropertyOfferPermissionAccess(['property_id' => $params['property_id'], 'offer_id' => $params['offer_id']]); + if ($checkPropertyOfferPermissionAccess['status'] != 'success') { + throw new ApiErrorException($checkPropertyOfferPermissionAccess['message']); + } + + $statusOffer = $this->offerService->updateStatus($requestParams); + if ($statusOffer['status'] != 'success') { + throw new ApiErrorException($statusOffer['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $statusOffer['data']]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function updatePropertyOfferAceptStatus(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams['user_id'] = $this->request->auth->id; + + $checkPropertyOfferPermissionAccess = $this->offerService->checkPropertyOfferPermissionAccess(['property_id' => $params['property_id'], 'offer_id' => $params['offer_id']]); + if ($checkPropertyOfferPermissionAccess['status'] != 'success') { + throw new ApiErrorException($checkPropertyOfferPermissionAccess['message']); + } + + $requestParams['offer_id'] = $checkPropertyOfferPermissionAccess['data']['id']; + $requestParams['offer_code'] = $checkPropertyOfferPermissionAccess['data']['offer_code']; + + if ($checkPropertyOfferPermissionAccess['data']['confirm_type'] == 'HTL' && $checkPropertyOfferPermissionAccess['data']['accept_status'] == 2) { + $requestParams['accept_status'] = 1; + + $statusOffer = $this->offerService->updateAcceptStatus($requestParams); + if ($statusOffer['status'] != 'success') { + throw new ApiErrorException($statusOffer['message']); + } + + $offerAcceptMailParam = [ + 'offer_id' => $checkPropertyOfferPermissionAccess['data']['id'], + 'property_id' => $checkPropertyOfferPermissionAccess['data']['property_id'], + 'paymentUrl' => null + ]; + + //Integrated Payment Link + if (!is_null($checkPropertyOfferPermissionAccess['data']['payment_type_mapping_id'])) { + + $paymentLinkParam = [ + 'property_id' => $checkPropertyOfferPermissionAccess['data']['property_id'], + 'title' => $checkPropertyOfferPermissionAccess['data']['title'], + 'description' => $checkPropertyOfferPermissionAccess['data']['ticket_code'] . '-' . $checkPropertyOfferPermissionAccess['data']['offer_code'], + 'email' => $checkPropertyOfferPermissionAccess['data']['email'], + 'currency' => $checkPropertyOfferPermissionAccess['data']['currency'], + 'base_amount' => $checkPropertyOfferPermissionAccess['data']['total'], + 'commission' => '0', + 'payment_method' => 'online', + 'payment_type_mapping_id' => $checkPropertyOfferPermissionAccess['data']['payment_type_mapping_id'], + 'locale' => $checkPropertyOfferPermissionAccess['data']['language'], + 'user_id' => $checkPropertyOfferPermissionAccess['data']['created_by'], + ]; + + $createPaymentLink = $this->propertyPaymentService->createManualPayment($paymentLinkParam); + + if ($createPaymentLink['status'] == "success") { + $offerAcceptMailParam['paymentUrl'] = $createPaymentLink['data']['payment_link']; + $this->offerService->update($checkPropertyOfferPermissionAccess['data']['id'], ['payment_transaction_order_id' => $createPaymentLink['data']['order_id']]); + } + + } + + $this->mailer->onQueue('offerAcceptMail', new OfferAcceptMail($offerAcceptMailParam)); + + } else { + throw new ApiErrorException('Offer status not available'); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function dashboardPropertyOffer(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + ]; + $propertyOffers = $this->offerService->select($propertyRequest); + $propertyOffers = collect($propertyOffers['data'])->groupBy('accept_status')->map->count()->toArray(); + + $propertyPassiveRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 0], + ], + ]; + $propertyPassiveOffers = $this->offerService->select($propertyPassiveRequest); + $passiveCounts = collect($propertyPassiveOffers['data'])->count(); + $acceptAndPendingCount = collect($propertyOffers)->sum(); + $offerDashboardData = + [ + "passive" => $passiveCounts, + "canceled" => fillOnUndefined($propertyOffers, 0, 0), + "accepted" => fillOnUndefined($propertyOffers, 1, 0), + "pending" => fillOnUndefined($propertyOffers, 2, 0), + "total" => $passiveCounts + $acceptAndPendingCount, + + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $offerDashboardData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePropertyOfferAcceptStatus(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $requestParams = $this->request->params; + + $checkOfferApprovedParam = [ + 'offer_id' => $requestParams['offer_id'] + ]; + $checkOfferApprovedStatus = $this->offerService->checkOfferApprovedStatus($checkOfferApprovedParam); + + if ($checkOfferApprovedStatus['status'] != 'success') { + throw new ApiErrorException($checkOfferApprovedStatus['message']); + } + + $requestParams['accept_status'] = 1; + if ($checkOfferApprovedStatus['data']['confirm_type'] == 'HTL') { + $requestParams['accept_status'] = 2; + } + + + $acceptStatusOffer = $this->offerService->updateAcceptStatus($requestParams); + if ($acceptStatusOffer['status'] != 'success') { + throw new ApiErrorException($acceptStatusOffer['message']); + } + + if ($checkOfferApprovedStatus['data']['confirm_type'] == 'INS') { + + $offerAcceptMailParam = [ + 'offer_id' => $checkOfferApprovedStatus['data']['id'], + 'property_id' => $checkOfferApprovedStatus['data']['property_id'], + 'paymentUrl' => null + ]; + + + //Integrated Payment Link + if (!is_null($checkOfferApprovedStatus['data']['payment_type_mapping_id'])) { + + + $paymentLinkParam = [ + 'property_id' => $checkOfferApprovedStatus['data']['property_id'], + 'title' => $checkOfferApprovedStatus['data']['title'], + 'description' => $checkOfferApprovedStatus['data']['ticket_code'] . '-' . $checkOfferApprovedStatus['data']['offer_code'], + 'email' => $checkOfferApprovedStatus['data']['email'], + 'currency' => $checkOfferApprovedStatus['data']['currency'], + 'base_amount' => $checkOfferApprovedStatus['data']['total'], + 'commission' => '0', + 'payment_method' => 'online', + 'payment_type_mapping_id' => $checkOfferApprovedStatus['data']['payment_type_mapping_id'], + 'locale' => $checkOfferApprovedStatus['data']['language'], + 'user_id' => $checkOfferApprovedStatus['data']['created_by'], + ]; + + $createPaymentLink = $this->propertyPaymentService->createManualPayment($paymentLinkParam); + + if ($createPaymentLink['status'] == "success") { + $offerAcceptMailParam['paymentUrl'] = $createPaymentLink['data']['payment_link']; + $this->offerService->update($checkOfferApprovedStatus['data']['id'], ['payment_transaction_order_id' => $createPaymentLink['data']['order_id']]); + } + + } + + $this->mailer->onQueue('offerAcceptMail', new OfferAcceptMail($offerAcceptMailParam)); + + } elseif ($checkOfferApprovedStatus['data']['confirm_type'] == 'HTL') { + + $offerAcceptMailParam = [ + 'offer_id' => $checkOfferApprovedStatus['data']['id'], + 'property_id' => $checkOfferApprovedStatus['data']['property_id'], + ]; + + $this->mailer->onQueue('offerPreConfirmCustomerMail', new OfferPreConfirmCustomerMail($offerAcceptMailParam)); + + $this->mailer->onQueue('offerPreConfirmPropertyMail', new OfferPreConfirmPropertyMail($offerAcceptMailParam)); + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function sendOfferEmail(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $checkPropertyOfferPermissionAccess = $this->offerService->checkPropertyOfferPermissionAccess(['property_id' => $params['property_id'], 'offer_id' => $params['offer_id']]); + if ($checkPropertyOfferPermissionAccess['status'] != 'success') { + throw new ApiErrorException($checkPropertyOfferPermissionAccess['message']); + } + + + $offerSendMailParam = [ + 'offer_id' => $params['offer_id'], + 'property_id' => $params['property_id'] + ]; + + $this->mailer->onQueue('offerSendMail', new OfferSendMail($offerSendMailParam)); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + + +} diff --git a/app/Http/Controllers/V1/PropertyPersonPricingPolicyController.php b/app/Http/Controllers/V1/PropertyPersonPricingPolicyController.php new file mode 100644 index 0000000..51bc52b --- /dev/null +++ b/app/Http/Controllers/V1/PropertyPersonPricingPolicyController.php @@ -0,0 +1,431 @@ +request = $request; + $this->propertyPersonPricingPolicyService = $propertyPersonPricingPolicyService; + } + + public function getPropertyPersonPricingPolicyList(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + + $getPropertyPersonPricingList = $this->propertyPersonPricingPolicyService->getPropertyPersonPricingPolicyList($params); + + if ($getPropertyPersonPricingList['status'] != "success") { + throw new ApiErrorException($getPropertyPersonPricingList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['pricing_policies' => $getPropertyPersonPricingList['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function createPropertyPersonPricingAdultPolicy(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $userId = $request->credentials->user_id; + $params = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'adult_action_type' => fillOnUndefined($params, 'adult_action_type'), + 'adult' => fillOnUndefined($params, 'adult'), + 'action_type' => fillOnUndefined($params, 'action_type'), + 'type' => fillOnUndefined($params, 'type'), + 'value' => fillOnUndefined($params, 'value'), + 'reservation_start_date' => fillOnUndefined($params, 'reservation_start_date'), + 'reservation_end_date' => fillOnUndefined($params, 'reservation_end_date'), + 'user_id' => $userId, + ]; + + $createResponse = $this->propertyPersonPricingPolicyService->createPropertyPersonPricingAdultPolicy($params); + + if ($createResponse['status'] != 'success') { + throw new ApiErrorException($createResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function createPropertyPersonPricingChildPolicy(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $userId = $request->credentials->user_id; + $params = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'adult' => fillOnUndefined($params, 'adult'), + 'child_order' => fillOnUndefined($params, 'child_order'), + 'child_age_start' => fillOnUndefined($params, 'child_age_start'), + 'child_age_end' => fillOnUndefined($params, 'child_age_end'), + 'type' => fillOnUndefined($params, 'type'), + 'value' => fillOnUndefined($params, 'value'), + 'reservation_start_date' => fillOnUndefined($params, 'reservation_start_date'), + 'reservation_end_date' => fillOnUndefined($params, 'reservation_end_date'), + 'user_id' => $userId, + ]; + + $createResponse = $this->propertyPersonPricingPolicyService->createPropertyPersonPricingChildPolicy($params); + + if ($createResponse['status'] != 'success') { + throw new ApiErrorException($createResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePropertyPersonPricingAdultPolicy(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $userId = $request->credentials->user_id; + $params = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'pricing_policy_id' => fillOnUndefined($params, 'pricing_policy_id'), + 'name' => fillOnUndefined($params, 'name'), + 'adult_action_type' => fillOnUndefined($params, 'adult_action_type'), + 'adult' => fillOnUndefined($params, 'adult'), + 'action_type' => fillOnUndefined($params, 'action_type'), + 'type' => fillOnUndefined($params, 'type'), + 'value' => fillOnUndefined($params, 'value'), + 'reservation_start_date' => fillOnUndefined($params, 'reservation_start_date'), + 'reservation_end_date' => fillOnUndefined($params, 'reservation_end_date'), + 'user_id' => $userId, + ]; + + $createResponse = $this->propertyPersonPricingPolicyService->updatePropertyPersonPricingAdultPolicy($params); + + if ($createResponse['status'] != 'success') { + throw new ApiErrorException($createResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePropertyPersonPricingChildPolicy(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $userId = $request->credentials->user_id; + $params = + [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'pricing_policy_id' => fillOnUndefined($params, 'pricing_policy_id'), + 'name' => fillOnUndefined($params, 'name'), + 'adult' => fillOnUndefined($params, 'adult'), + 'child_order' => fillOnUndefined($params, 'child_order'), + 'child_age_start' => fillOnUndefined($params, 'child_age_start'), + 'child_age_end' => fillOnUndefined($params, 'child_age_end'), + 'type' => fillOnUndefined($params, 'type'), + 'value' => fillOnUndefined($params, 'value'), + 'reservation_start_date' => fillOnUndefined($params, 'reservation_start_date'), + 'reservation_end_date' => fillOnUndefined($params, 'reservation_end_date'), + 'room_rate_channel_mapping_id' => fillOnUndefined($params, 'room_rate_channel_mapping_id'), + 'user_id' => $userId, + ]; + + $createResponse = $this->propertyPersonPricingPolicyService->updatePropertyPersonPricingChildPolicy($params); + + if ($createResponse['status'] != 'success') { + throw new ApiErrorException($createResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function deletePropertyPersonPricingAdultPolicy(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $userId = $request->credentials->user_id; + $params = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'pricing_policy_id' => fillOnUndefined($params, 'pricing_policy_id'), + 'user_id' => $userId, + ]; + + $createResponse = $this->propertyPersonPricingPolicyService->deletePropertyPersonPricingAdultPolicy($params); + + if ($createResponse['status'] != 'success') { + throw new ApiErrorException($createResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function deletePropertyPersonPricingChildPolicy(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $userId = $request->credentials->user_id; + $params = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'pricing_policy_id' => fillOnUndefined($params, 'pricing_policy_id'), + 'user_id' => $userId, + ]; + + $createResponse = $this->propertyPersonPricingPolicyService->deletePropertyPersonPricingChildPolicy($params); + + if ($createResponse['status'] != 'success') { + throw new ApiErrorException($createResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updateRoomRatePricingAdultPolicy(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $checkChannelRoomPricingType = $this->propertyPersonPricingPolicyService->checkChannelRoomPricingType($params); + if ($checkChannelRoomPricingType['status'] != 'success') { + throw new ApiErrorException($checkChannelRoomPricingType['message']); + } + + $getPropertyPersonPricingList = $this->propertyPersonPricingPolicyService->updateRoomRatePricingAdultPolicy($params); + + if ($getPropertyPersonPricingList['status'] != "success") { + throw new ApiErrorException($getPropertyPersonPricingList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updateRoomRatePricingChildPolicy(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $checkChannelRoomPricingType = $this->propertyPersonPricingPolicyService->checkChannelRoomPricingType($params); + if ($checkChannelRoomPricingType['status'] != 'success') { + throw new ApiErrorException($checkChannelRoomPricingType['message']); + } + + $getPropertyPersonPricingList = $this->propertyPersonPricingPolicyService->updateRoomRatePricingChildPolicy($params); + + if ($getPropertyPersonPricingList['status'] != "success") { + throw new ApiErrorException($getPropertyPersonPricingList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getRoomRateChannelPersonPricingPolicy(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $checkChannelRoomPricingType = $this->propertyPersonPricingPolicyService->checkChannelRoomPricingType($params); + if ($checkChannelRoomPricingType['status'] != 'success') { + throw new ApiErrorException($checkChannelRoomPricingType['message']); + } + + $getPropertyPersonPricingList = $this->propertyPersonPricingPolicyService->getRoomRateChannelPersonPricingPolicy($params); + + if ($getPropertyPersonPricingList['status'] != "success") { + throw new ApiErrorException($getPropertyPersonPricingList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['rooms' => $getPropertyPersonPricingList['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + +} diff --git a/app/Http/Controllers/V1/PropertyPhotoCategoryController.php b/app/Http/Controllers/V1/PropertyPhotoCategoryController.php new file mode 100644 index 0000000..02fc3be --- /dev/null +++ b/app/Http/Controllers/V1/PropertyPhotoCategoryController.php @@ -0,0 +1,76 @@ +request = $request; + $this->propertyPhotoCategoryService = $propertyPhotoCategoryService; + + } + + public function propertyPhotoCategoryList() + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + + ]; + $propertyPhotoCategory = $this->propertyPhotoCategoryService->getPropertyPhotoCategoryList($requestParams); + + if($propertyPhotoCategory['status'] != 'success'){ + throw new Exception($propertyPhotoCategory['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyPhotoCategory['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyPhotoController.php b/app/Http/Controllers/V1/PropertyPhotoController.php new file mode 100644 index 0000000..f0ebe3c --- /dev/null +++ b/app/Http/Controllers/V1/PropertyPhotoController.php @@ -0,0 +1,381 @@ +propertyPhotoService = $propertyPhotoService; + $this->googleVisionLabelService = $googleVisionLabelService; + $this->propertyConfigService = $propertyConfigService; + $this->propertyService = $propertyService ; + } + + public function getPropertyPhotos(Request $request) + { + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + + try + { + if ( is_null( $request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(),1); + $params = current($params); + + //TODO : Validation Yapılması gerekiyor. + $response = $this->propertyPhotoService->getPropertyPhotos($params); + + }catch (ApiErrorException $e) + { + $response['message'] = $e->getMessage(); + + }catch (Exception $e) + { + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function uploadPropertyPhoto(Request $request) + { + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + try + { + if(!$request->hasFile('image')) + { + throw new ApiErrorException(lang('Photos not found')); + } + + $propertyId = $request->input('property_id'); + $photo = $request->file('image'); + + $param = + [ + "property_id" => $propertyId, + "photo" => $photo, + ]; + + $response = $this->propertyPhotoService->propertyPhotoUploadFilter($param); + + $rateParams = [ + 'property_id' => $propertyId, + 'user_id' => 1, + 'property_rate_for' => 'Property.Photo.Upload', + ]; + $this->propertyConfigService->rateProperty($rateParams); + + + }catch (ApiErrorException $e) + { + $response['message'] = $e->getMessage(); + + }catch (Exception $e) + { + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function setDefaultPhoto(Request $request) + { + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + + try + { + if ( is_null( $request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(),1); + $params = current($params); + //TODO : Validation Yapılması gerekiyor. + $response = $this->propertyPhotoService->setDefaultPhoto($params); + + + + }catch (ApiErrorException $e) + { + $response['message'] = $e->getMessage(); + + }catch (Exception $e) + { + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + $response['message'] = $e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function setPropertyPhotoOrder(Request $request) + { + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + + try + { + if ( is_null( $request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(),1); + $params = current($params); + + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'set_photo_order' => fillOnUndefined($params, 'set_photo_order'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + + //TODO : Validation Yapılması gerekiyor. + $response = $this->propertyPhotoService->setPhotoOrder($requestParams); + + }catch (ApiErrorException $e) + { + $response['message'] = $e->getMessage(); + + }catch (Exception $e) + { + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function publishPhotos(Request $request) + { + + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + + try + { + if ( is_null( $request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(),1); + $params = current($params); + + //TODO : Validation Yapılması gerekiyor. + $response = $this->propertyPhotoService->publishPhotos($params); + + }catch (ApiErrorException $e) + { + $response['message'] = $e->getMessage(); + + }catch (Exception $e) + { + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function deletePropertyPhotos(Request $request) + { + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + + try + { + if ( is_null( $request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(),1); + $params = current($params); + + //TODO : Validation Yapılması gerekiyor. + $deletePhoto = $this->propertyPhotoService->deletePhotos($params); + if ($deletePhoto['status'] == false) { + throw new ApiErrorException($deletePhoto['message']); + } + + $defaultPhotoCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_default', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + + $propertyPhotoData = $this->propertyPhotoService->select($defaultPhotoCriteria, ['id']); + if ($propertyPhotoData['status'] == false) { + throw new ApiErrorException($propertyPhotoData['message']); + } + + $defaultPhoto = isset($propertyPhotoData['data']['id']) ? $propertyPhotoData['data']['id'] : null ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['default_photo' => $defaultPhoto] ]; + + + }catch (ApiErrorException $e) + { + $response['message'] = $e->getMessage(); + + }catch (Exception $e) + { + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function setPropertyPhotoCategory(Request $request) + { + $response = ['status' => false, 'statusCode' => 500 ,'message' => '', 'data' => null]; + + try + { + if ( is_null( $request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = json_decode($request->getContent(),1); + $params = current($params); + + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + 'set_photo_category' => fillOnUndefined($params, 'set_photo_category'), + ]; + + //TODO : Validation Yapılması gerekiyor. + $response = $this->propertyPhotoService->setPhotoCategory($requestParams); + + + + }catch (ApiErrorException $e) + { + $response['message'] = $e->getMessage(); + + }catch (Exception $e) + { + $message = $e->getFile().' '.$e->getLine().' '.$e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + public function downloadPropertyPhotos(Request $request) + { + + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + try { + if (is_null($request->getContent())) { + throw new Exception('api-unknown_error'); + } + $params = json_decode($request->getContent(), 1); + $params = current($params); + $responseData = null ; + $propertyId = $params['property_id']; + $getPhotosRequest = [ + 'property_id' => $propertyId + ]; + + if(isset($params['custom']) && !empty($params['custom'])) { + $getPhotosRequest['custom'] = $params['custom']; + } + + $response = $this->propertyPhotoService->getPropertyPhotos($getPhotosRequest); + + if ($response['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + if (!$response['data']) { + throw new Exception('api-unknown_error'); + } + $photos = $response['data']; + + if ($photos) { + $public_dir = public_path(); + $zipFileStoragePath = $public_dir . '/property-zip-files'; + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $propertyId], + ], + 'firstRow' => true + ]; + $thisProperty = $this->propertyService->select($propertyRequest); + if ($thisProperty['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + $hotelName = Str::slug($thisProperty['data']['name'], '_', 'en'); + if (!File::exists($zipFileStoragePath)) { + File::makeDirectory($zipFileStoragePath, 0777, true); + } + $zipFileName = $hotelName . '_photos.zip'; + $zip = new ZipArchive; + if(file_exists($zipFileStoragePath . '/' . $zipFileName)) { + unlink($zipFileStoragePath . '/' . $zipFileName); + } + + if ($zip->open($zipFileStoragePath . '/' . $zipFileName, ZipArchive::CREATE) === TRUE) { + foreach ($photos as $photo) { + $photoUrlFilePath = Config::get('app.fileSystemDriver') . "/property-photos/{$photo['property_id']}" . "/{$photo['photo_name']}_medium.{$photo['file_ext']}"; + $fileName = "{$photo['photo_name']}_medium.{$photo['file_ext']}"; + if (file_exists($photoUrlFilePath)) { + $zip->addFile($photoUrlFilePath, $fileName); + } + } + $zip->close(); + } + $responseData = Config::get('app.zipFilesUrl').$zipFileName; + } + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['zipFile' => $responseData]]; + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + +} diff --git a/app/Http/Controllers/V1/PropertyPlaceController.php b/app/Http/Controllers/V1/PropertyPlaceController.php new file mode 100644 index 0000000..64d06b7 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyPlaceController.php @@ -0,0 +1,520 @@ +request = $request ; + $this->propertyPlaceService = $propertyPlaceService ; + $this->propertyPhotoService = $propertyPhotoService ; + + + } + + + public function getPlaceCategories(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $placeCategories = $this->propertyPlaceService->getPropertyPlaceCategories($params); + + if($placeCategories['status'] != 'success'){ + throw new Exception($placeCategories['message']); + } + + $responseData['place_categories'] = $placeCategories['data'] ; + + $placeWorkingHours = $this->propertyPlaceService->getPropertyWorkingHours($params); + if($placeWorkingHours['status'] != 'success'){ + throw new Exception($placeWorkingHours['message']); + } + $responseData['place_working_hours'] = $placeWorkingHours['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPlaceCategoriesSingle(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $placeCategories = $this->propertyPlaceService->getPropertyPlaceCategoriesSingle($params); + + if($placeCategories['status'] != 'success'){ + throw new Exception($placeCategories['message']); + } + + $responseData['place_categories'] = $placeCategories['data'] ; + + $placeWorkingHours = $this->propertyPlaceService->getPropertyWorkingHours($params); + if($placeWorkingHours['status'] != 'success'){ + throw new Exception($placeWorkingHours['message']); + } + $responseData['place_working_hours'] = $placeWorkingHours['data'] ; + + + $placeIncludes = $this->propertyPlaceService->getPlaceIncludes($params); + if($placeIncludes['status'] != 'success'){ + throw new Exception($placeIncludes['message']); + } + $responseData['place_includes'] = $placeIncludes['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPlacePlaceWorkingHours(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $placeWorkingHours = $this->propertyPlaceService->getPropertyWorkingHours($params); + + if($placeWorkingHours['status'] != 'success'){ + throw new Exception($placeWorkingHours['message']); + } + + $responseData['place_working_hours'] = $placeWorkingHours['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function createPlace(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $responseData = null ; + $placeWorkingHours = $this->propertyPlaceService->create($params); + + if($placeWorkingHours['status'] != 'success'){ + throw new Exception($placeWorkingHours['message']); + } + + $responseData['property_place'] = $placeWorkingHours['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function listPlace(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $listPlaces = $this->propertyPlaceService->listPlaces($params); + + if($listPlaces['status'] != 'success'){ + throw new Exception($listPlaces['message']); + } + + $responseData['places'] = $listPlaces['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPropertyPlacePhoto(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestFact = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'place_id' => fillOnUndefined($params, 'place_id'), + ]; + + $getPropertyPhoto = $this->propertyPhotoService->getPropertyPlacePhoto($requestFact); + if ($getPropertyPhoto['status'] != 'success') { + throw new ApiErrorException($getPropertyPhoto['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $getPropertyPhoto['data'] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyPlacePhotoMapping(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try + { + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = json_decode($this->request->getContent(),1); + $params = current($params); + $params['user_id'] = $this->request->auth->id; + + $response = $this->propertyPlaceService->updatePropertyPlacePhotoMapping($params); + + }catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePlace(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $responseData = null ; + $placeWorkingHours = $this->propertyPlaceService->update($params); + + if($placeWorkingHours['status'] != 'success'){ + throw new Exception($placeWorkingHours['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $placeWorkingHours['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePlaceStatus(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $responseData = null ; + $placeWorkingHours = $this->propertyPlaceService->updateStatus($params); + + if($placeWorkingHours['status'] != 'success'){ + throw new Exception($placeWorkingHours['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $placeWorkingHours['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function deletePlace(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $responseData = null ; + $placeWorkingHours = $this->propertyPlaceService->deletePlace($params); + + if($placeWorkingHours['status'] != 'success'){ + throw new Exception($placeWorkingHours['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $placeWorkingHours['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function editPlace(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $listPlaces = $this->propertyPlaceService->editPlaces($params); + + if($listPlaces['status'] != 'success'){ + throw new Exception($listPlaces['message']); + } + + $responseData = $listPlaces['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function listPlaceFacilities(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $listPlaces = $this->propertyPlaceService->listPlaceFacilities($params); + + if($listPlaces['status'] != 'success'){ + throw new Exception($listPlaces['message']); + } + + $responseData['place_facilities'] = $listPlaces['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePlaceFacilities(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $responseData = null ; + $placeWorkingHours = $this->propertyPlaceService->updatePlaceFacilities($params); + + if($placeWorkingHours['status'] != 'success'){ + throw new Exception($placeWorkingHours['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $placeWorkingHours['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPlaceCategoryFields(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $responseData = null ; + $placeCategoryFields = $this->propertyPlaceService->getPlaceCategoryFields($params); + if($placeCategoryFields['status'] != 'success'){ + throw new Exception($placeCategoryFields['message']); + } + $responseData['place_category_fields'] = $placeCategoryFields['data'] ; + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function insertNewFieldMapping(){ + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + $responseData = null ; + $placeCategoryFields = $this->propertyPlaceService->insertNewFieldMapping($params); + if($placeCategoryFields['status'] != 'success'){ + throw new Exception($placeCategoryFields['message']); + } + $responseData['place_category_fields'] = $placeCategoryFields['data'] ; + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + +} diff --git a/app/Http/Controllers/V1/PropertyPromotionController.php b/app/Http/Controllers/V1/PropertyPromotionController.php new file mode 100644 index 0000000..ce72974 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyPromotionController.php @@ -0,0 +1,264 @@ +request = $request; + $this->propertyPromotionService = $propertyPromotionService; + } + + + public function getPromotionTypeList(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $getPromotionTypeList = $this->propertyPromotionService->getPromotionTypeList($params); + + if ($getPromotionTypeList['status'] != "success") { + throw new ApiErrorException($getPromotionTypeList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['promotion_type' => $getPromotionTypeList['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyPromotionList(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $getPropertyPromotionList = $this->propertyPromotionService->getPropertyPromotionList($params); + + if ($getPropertyPromotionList['status'] != "success") { + throw new ApiErrorException($getPropertyPromotionList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['property_promotions' => $getPropertyPromotionList['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function createPropertyPromotion(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + $createResponse = $this->propertyPromotionService->createPropertyPromotion($params); + + if ($createResponse['status'] != 'success') { + throw new ApiErrorException($createResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $createResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getRoomRateChannelPromotion(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $getPropertyPromotionList = $this->propertyPromotionService->getRoomRateChannelPromotion($params); + + if ($getPropertyPromotionList['status'] != "success") { + throw new ApiErrorException($getPropertyPromotionList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['rooms' => $getPropertyPromotionList['data']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyPromotion(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + $updateResponse = $this->propertyPromotionService->updatePropertyPromotion($params); + + if ($updateResponse['status'] != 'success') { + throw new ApiErrorException($updateResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $updateResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updateRoomRateChannelPromotion(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + + $getPropertyPromotionList = $this->propertyPromotionService->updateRoomRateChannelPromotion($params); + + if ($getPropertyPromotionList['status'] != "success") { + throw new ApiErrorException($getPropertyPromotionList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function deletePropertyPromotion(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $params = $this->request->params; + $params['user_id'] = $request->credentials->user_id; + + $updateResponse = $this->propertyPromotionService->deletePropertyPromotion($params); + + if ($updateResponse['status'] != 'success') { + throw new ApiErrorException($updateResponse['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $updateResponse['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + + +} diff --git a/app/Http/Controllers/V1/PropertyQuickPricingController.php b/app/Http/Controllers/V1/PropertyQuickPricingController.php new file mode 100644 index 0000000..2772630 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyQuickPricingController.php @@ -0,0 +1,490 @@ +params = Input::all(); + $this->propertyQuickPricingService = $propertyQuickPricingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + } + + public function syncPropertyQuickPricing(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + //TODO Param validator + + $propertyRoomRateChannelIds = []; + + $channelRoomRateCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['params']['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + + $propertyRoomRateChannels = $this->propertyRoomRateChannelMappingService->select($channelRoomRateCriteria); + + if ($propertyRoomRateChannels['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateChannels['message']); + } + + $propertyRoomRateChannelIds = pickItemFromArray('id', $propertyRoomRateChannels['data']); + + if (empty($propertyRoomRateChannelIds)) { + throw new ApiErrorException('Not mapping room rate this channel.'); + } + + + DB::beginTransaction(); + + $quickPricingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['params']['channel_id']], + ] + ]; + + $quickPricingResult = $this->propertyQuickPricingService->selectPropertyQuickPricingMapping($quickPricingCriteria); + + if ($quickPricingResult['status'] != 'success') { + throw new ApiErrorException($quickPricingResult['message']); + } + + if (!empty($quickPricingResult['data'])) { + $deleteIds = pickItemFromArray('id', $quickPricingResult['data']); + $deletePropertyQuickPricingMapping = $this->propertyQuickPricingService->deletePropertyQuickPricingMapping($deleteIds); + if ($deletePropertyQuickPricingMapping['status'] != 'success') { + throw new ApiErrorException($deletePropertyQuickPricingMapping['message']); + } + } + + $roomRateChannelMappingIdCollect = collect($this->params['params']['room_rates'])->groupBy('room_rate_channel_mapping_id')->toArray(); + foreach ($roomRateChannelMappingIdCollect as $roomRateChannelMapping) { + if (count($roomRateChannelMapping) > 1) { + throw new ApiErrorException('The same room rate channel id can not be processed.'); + } + } + + + $requestResultData = []; + foreach ($this->params['params']['room_rates'] as $roomRate) { + + if (!in_array($roomRate['room_rate_channel_mapping_id'], $propertyRoomRateChannelIds)) { + continue; + } + + $requestParam = [ + 'property_id' => $this->params['params']['property_id'], + 'channel_id' => $this->params['params']['channel_id'], + 'room_rate_channel_mapping_id' => $roomRate['room_rate_channel_mapping_id'], + 'action_type' => fillOnUndefined($roomRate, 'action_type'), + 'price_type' => fillOnUndefined($roomRate, 'price_type'), + 'price_value' => fillOnUndefined($roomRate, 'price_value'), + 'created_by' => $request->auth->id, + 'updated_by' => $request->auth->id, + ]; + + + $requestResult = $this->propertyQuickPricingService->createPropertyQuickPricingMapping($requestParam); + + if ($requestResult['status'] != 'success') { + throw new ApiErrorException($requestResult['message']); + } + + $requestResultData[] = $requestResult['data']; + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $requestResultData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function getPropertyQuickPricing(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['params']['channel_id']], + ], + 'with' => ['propertyRoomRateChannelMapping.propertyRoomRateMapping'] + ]; + + $columns = ['id', 'property_id', 'channel_id', 'room_rate_channel_mapping_id', 'action_type', 'price_type', 'price_value']; + $requestSelectResult = $this->propertyQuickPricingService->selectPropertyQuickPricingMapping($requestSelectCriteria, $columns); + + if ($requestSelectResult['status'] != 'success') { + throw new ApiErrorException($requestSelectResult['message']); + } + + $requestSelectResultData = []; + foreach ($requestSelectResult['data'] as $key => $value) { + $requestSelectResultData[$value['room_rate_channel_mapping_id']] = [ + 'property_id' => $value['property_id'], + 'channel_id' => $value['channel_id'], + 'room_id' => $value['property_room_rate_channel_mapping']['property_room_rate_mapping']['room_id'], + 'room_rate_id' => $value['property_room_rate_channel_mapping']['property_room_rate_mapping']['room_rate_id'], + 'room_rate_mapping_id' => $value['property_room_rate_channel_mapping']['room_rate_mapping_id'], + 'room_rate_channel_mapping_id' => $value['room_rate_channel_mapping_id'], + 'action_type' => $value['action_type'], + 'price_type' => $value['price_type'], + 'price_value' => $value['price_value'], + ]; + + } + + $channelRoomRateCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['params']['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['propertyRoomRateMapping.propertyRoom', 'propertyRoomRateMapping.propertyRoomRate'] + ]; + + + $propertyRoomRateChannels = $this->propertyRoomRateChannelMappingService->select($channelRoomRateCriteria); + + if ($propertyRoomRateChannels['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateChannels['message']); + } + + $propertyRoomRateChannelQp = []; + foreach ($propertyRoomRateChannels['data'] as $propertyRoomRateChannel) { + + //Best Available Rate Manipulate + if($propertyRoomRateChannel['property_room_rate_mapping']['property_room_rate']['name'] == 'Best Available Rate') { + continue; + } + + $roomId = $propertyRoomRateChannel['property_room_rate_mapping']['room_id']; + + //dd($propertyRoomRateChannel['property_room_rate_mapping']['property_room']['name']); + + $propertyRoomRateChannelQp['rooms'][$roomId]['name'] = $propertyRoomRateChannel['property_room_rate_mapping']['property_room']['name']; + + $propertyRoomRateChannelQp['rooms'][$roomId]['property_room_rate_mapping'][$propertyRoomRateChannel['room_rate_mapping_id']] = [ + 'name' => $propertyRoomRateChannel['property_room_rate_mapping']['property_room_rate']['name'], + 'room_rate_channel_mapping_id' => $propertyRoomRateChannel['id'], + 'action_type' => null, + 'price_type' => null, + 'price_value' => null + ]; + + if (isset($requestSelectResultData[$propertyRoomRateChannel['id']])) { + $propertyRoomRateChannelQp['rooms'][$roomId]['property_room_rate_mapping'][$propertyRoomRateChannel['room_rate_mapping_id']] = [ + 'name' => $propertyRoomRateChannel['property_room_rate_mapping']['property_room_rate']['name'], + 'room_rate_channel_mapping_id' => $propertyRoomRateChannel['id'], + 'action_type' => $requestSelectResultData[$propertyRoomRateChannel['id']]['action_type'], + 'price_type' => $requestSelectResultData[$propertyRoomRateChannel['id']]['price_type'], + 'price_value' => $requestSelectResultData[$propertyRoomRateChannel['id']]['price_value'], + ]; + } + + } + + + //array_values + foreach ($propertyRoomRateChannelQp['rooms'] as $roomId => $rooms) { + $propertyRoomRateChannelQp['rooms'][$roomId]['property_room_rate_mapping'] = array_values($rooms['property_room_rate_mapping']); + } + + $propertyRoomRateChannelQp['rooms'] = array_values($propertyRoomRateChannelQp['rooms']); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateChannelQp]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function propertyQuickPricingRate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + //Burada kanal ve proeprty ile kanal maping detaylatı çekilmeli property_channel_mapping tablosundan + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['params']['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + $propertyChannelMapping = $propertyChannelMapping['data']; + + + //CONNECTED CHANNEL RATE CHECK + $propertyChannelConnectedList = []; + $propertyChannelConnectedCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $this->params['params']['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $propertyChannelConnected = $this->propertyChannelMappingService->select($propertyChannelConnectedCriteria); + if ($propertyChannelConnected['status'] == 'success') { + $propertyChannelConnectedList = $propertyChannelConnected['data']; + + } + //CONNECTED CHANNEL RATE CHECK + + /* + + "property_id" => 1 + "channel_id" => 1 + "currency_code" => "EUR" + "property_booking_type_id" => 1 + "property_availability_type_id" => 1 + + * */ + + //Property ve Kanala ait mapping yapılan room rate ler + $propertyRoomRateChannelIds = []; + $channelRoomRateCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['params']['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + + $propertyRoomRateChannels = $this->propertyRoomRateChannelMappingService->select($channelRoomRateCriteria); + + if ($propertyRoomRateChannels['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateChannels['message']); + } + + $propertyRoomRateChannelIds = pickItemFromArray('id', $propertyRoomRateChannels['data']); + + if (empty($propertyRoomRateChannelIds)) { + throw new ApiErrorException('Not mapping room rate this channel.'); + } + + $propertyQuickPricingMapping = []; + $requestSelectCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['params']['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['params']['channel_id']], + ], + 'with' => ['propertyRoomRateChannelMapping.propertyRoomRateMapping', 'propertyRoomRateChannelMapping.propertyRoomRateMapping'] + ]; + + $columns = ['id', 'property_id', 'channel_id', 'room_rate_channel_mapping_id', 'action_type', 'price_type', 'price_value']; + $requestSelectResult = $this->propertyQuickPricingService->selectPropertyQuickPricingMapping($requestSelectCriteria, $columns); + + if ($requestSelectResult['status'] != 'success') { + throw new ApiErrorException($requestSelectResult['message']); + } + + $propertyQuickPricingMapping = $requestSelectResult['data']; + + DB::beginTransaction(); + + foreach ($propertyQuickPricingMapping as $quickPricing) { + + //Eğer kanalda kapalı bir room rate var ise onun fiyatı güncellenmez + if (!in_array($quickPricing['room_rate_channel_mapping_id'], $propertyRoomRateChannelIds)) { + continue; + } + + //dd($quickPricing,$propertyChannelMapping,$this->params); + + + $requestParams = [ + 'property_id' => $quickPricing['property_id'], + 'channel_id' => $quickPricing['channel_id'], + 'user_id' => $request->auth->id, + 'availability' => [] + ]; + + $requestParams['rates'] = []; + + foreach ($this->params['params']['data'] as $date => $amount) { + + + $dateForSql = substr($date, 0, 4) . '-' . substr($date, 4, 2) . '-' . substr($date, 6, 2); + + if (!Carbon::parse($dateForSql)) { + throw new ApiErrorException('Invalid date format.'); + } + + if (Carbon::parse($dateForSql)->isBefore(Carbon::now()->toDateString())) { + throw new ApiErrorException('Date to be updated cannot be earlier than today'); + } + + $dateKeyParam = []; + $dateKeyParam[] = $propertyChannelMapping['property_availability_type_id']; + $dateKeyParam[] = $quickPricing['property_room_rate_channel_mapping']['property_room_rate_mapping']['room_id']; + $dateKeyParam[] = $quickPricing['property_room_rate_channel_mapping']['room_rate_mapping_id']; + $dateKeyParam[] = $dateForSql; + $dateKey = implode('|', $dateKeyParam); + + + $amountCalculated = $amount; + + $amountAffected = 0; + if ($quickPricing['price_type'] == 'PER') { + $amountAffected = ($amount * $quickPricing['price_value']) / 100; + } elseif ($quickPricing['price_type'] == 'FIX') { + $amountAffected = $quickPricing['price_value']; + } + + if ($quickPricing['action_type'] == 'INC') { + $amountCalculated = $amountCalculated + $amountAffected; + } + + if ($quickPricing['action_type'] == 'DEC') { + $amountCalculated = $amountCalculated - $amountAffected; + } + + if ($amountCalculated <= 0) { + throw new ApiErrorException('Calculated amount cannot be 0 or less.'); + } + + + $requestParams['rates'][$dateKey] = [ + "setup_type_id" => $propertyChannelMapping['property_availability_type_id'], + "room_id" => $quickPricing['property_room_rate_channel_mapping']['property_room_rate_mapping']['room_id'], + "room_rate_mapping_id" => $quickPricing['property_room_rate_channel_mapping']['room_rate_mapping_id'], + "date" => $dateForSql, + "amount" => $amountCalculated + ]; + + } + + $requestParams['quickPricing'] = true; + + $roomRateUpdate = $this->propertyRoomRatePriceService->roomRateUpdate($requestParams); + + if ($roomRateUpdate['status'] != 'success') { + throw new ApiErrorException($roomRateUpdate['message']); + } + + + //CONNECTED CHANNEL RATE UPDATE + foreach ($propertyChannelConnectedList as $propertyChannelConnected) { + $requestParams['channel_id'] = $propertyChannelConnected['channel_id']; + $roomRateUpdate = $this->propertyRoomRatePriceService->roomRateUpdate($requestParams); + if ($roomRateUpdate['status'] != 'success') { + throw new ApiErrorException($roomRateUpdate['message']); + } + } + //CONNECTED CHANNEL RATE UPDATE + + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} + diff --git a/app/Http/Controllers/V1/PropertyRoomBedController.php b/app/Http/Controllers/V1/PropertyRoomBedController.php new file mode 100644 index 0000000..7207de2 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomBedController.php @@ -0,0 +1,119 @@ +request = $request; + $this->propertyRoomBedService = $propertyRoomBedService; + + } + + + + public function getPropertyRoomBed(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + ]; + + $propertyRoomBedType = $this->propertyRoomBedService->getPropertyRoomBeds($requestParams); + if($propertyRoomBedType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomBedType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomBedType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomBed(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id', null), + 'room_id' => fillOnUndefined($params, 'room_id', null), + 'room_bed_group' => fillOnUndefined($params, 'room_bed_group', []), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomBedType = $this->propertyRoomBedService->addPropertyRoomBed($requestParams); + if($propertyRoomBedType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomBedType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomBedType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + + + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyRoomBedTypeController.php b/app/Http/Controllers/V1/PropertyRoomBedTypeController.php new file mode 100644 index 0000000..11e48c7 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomBedTypeController.php @@ -0,0 +1,74 @@ +request = $request; + $this->propertyRoomBedTypeService = $propertyRoomBedTypeService; + + } + + + + public function getPropertyRoomBedTypes(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + ]; + + $propertyRoomBedType = $this->propertyRoomBedTypeService->getPropertyRoomBedTypes($requestParams); + if($propertyRoomBedType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomBedType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomBedType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyRoomController.php b/app/Http/Controllers/V1/PropertyRoomController.php new file mode 100644 index 0000000..100375b --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomController.php @@ -0,0 +1,665 @@ +request = $request; + $this->propertyRoomService = $propertyRoomService; + $this->propertyRoomTypeService = $propertyRoomTypeService; + $this->propertyRoomBedTypeService = $propertyRoomBedTypeService; + $this->propertyRoomViewTypeService = $propertyRoomViewTypeService; + $this->propertyChannelMappingService = $propertyChannelMappingService ; + + } + + + + public function getPropertyRoom(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $propertyRoomType = $this->propertyRoomService->getPropertyRooms($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoom(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'room_type_id' => fillOnUndefined($params, 'room_type_id'), + 'max_occupancy' => fillOnUndefined($params, 'max_occupancy'), + 'max_adult' => fillOnUndefined($params, 'max_adult'), + 'max_child' => fillOnUndefined($params, 'max_child'), + 'occupancy_lock' => fillOnUndefined($params, 'occupancy_lock'), + 'room_size' => fillOnUndefined($params, 'room_size'), + 'room_size_type' => fillOnUndefined($params, 'room_size_type'), + 'room_type_count' => fillOnUndefined($params, 'room_type_count',1), + 'room_count' => fillOnUndefined($params, 'room_count'), + 'bathroom_count' => fillOnUndefined($params, 'bathroom_count'), + 'toilet_count' => fillOnUndefined($params, 'toilet_count'), + 'lounge_count' => fillOnUndefined($params, 'lounge_count'), + 'max_child_number' => fillOnUndefined($params, 'max_child_number'), + 'description' => fillOnUndefined($params, 'description'), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomType = $this->propertyRoomService->addPropertyRoom($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomAndBed(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => trim(fillOnUndefined($params, 'name')), + 'room_type_id' => fillOnUndefined($params, 'room_type_id'), + 'max_occupancy' => fillOnUndefined($params, 'max_occupancy'), + 'max_adult' => fillOnUndefined($params, 'max_adult'), + 'max_child' => fillOnUndefined($params, 'max_child'), + 'occupancy_lock' => fillOnUndefined($params, 'occupancy_lock'), + 'exclude_occupancy' => fillOnUndefined($params, 'exclude_occupancy'), + 'room_size' => fillOnUndefined($params, 'room_size', null), + 'room_size_type' => fillOnUndefined($params, 'room_size_type', null), + 'room_type_count' => fillOnUndefined($params, 'room_type_count', 1), + 'room_count' => fillOnUndefined($params, 'room_count', null), + 'bathroom_count' => fillOnUndefined($params, 'bathroom_count', null), + 'toilet_count' => fillOnUndefined($params, 'toilet_count', null), + 'lounge_count' => fillOnUndefined($params, 'lounge_count', null), + 'max_child_number' => fillOnUndefined($params, 'max_child_number', null), + 'description' => fillOnUndefined($params, 'description', []), + 'room_bed_group' => fillOnUndefined($params, 'room_bed_group'), + 'room_view_type' => fillOnUndefined($params, 'room_view_type'), + 'is_connected_room' => fillOnUndefined($params, 'is_connected_room', null), + 'is_connected_room_price' => fillOnUndefined($params, 'is_connected_room_price', null), + 'is_connected_room_availability' => fillOnUndefined($params, 'is_connected_room_availability', null), + 'connected_rooms' => fillOnUndefined($params, 'connected_rooms', []), + 'user_id' => $this->request->auth->id, + ]; + + + + $propertyRoomType = $this->propertyRoomService->addPropertyRoomAndBed($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyRoomAndBed(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'name' => trim(fillOnUndefined($params, 'name')), + 'room_type_id' => fillOnUndefined($params, 'room_type_id'), + 'max_occupancy' => fillOnUndefined($params, 'max_occupancy'), + 'max_adult' => fillOnUndefined($params, 'max_adult'), + 'max_child' => fillOnUndefined($params, 'max_child'), + 'occupancy_lock' => fillOnUndefined($params, 'occupancy_lock'), + 'exclude_occupancy' => fillOnUndefined($params, 'exclude_occupancy'), + 'room_size' => fillOnUndefined($params, 'room_size', null), + 'room_size_type' => fillOnUndefined($params, 'room_size_type', null), + 'room_type_count' => fillOnUndefined($params, 'room_type_count', 1), + 'room_count' => fillOnUndefined($params, 'room_count', null), + 'bathroom_count' => fillOnUndefined($params, 'bathroom_count', null), + 'toilet_count' => fillOnUndefined($params, 'toilet_count', null), + 'lounge_count' => fillOnUndefined($params, 'lounge_count', null), + 'max_child_number' => fillOnUndefined($params, 'max_child_number', null), + 'description' => fillOnUndefined($params, 'description', []), + 'room_bed_group' => fillOnUndefined($params, 'room_bed_group'), + 'room_view_type' => fillOnUndefined($params, 'room_view_type'), + 'is_connected_room' => fillOnUndefined($params, 'is_connected_room', null), + 'is_connected_room_price' => fillOnUndefined($params, 'is_connected_room_price', null), + 'is_connected_room_availability' => fillOnUndefined($params, 'is_connected_room_availability', null), + 'connected_rooms' => fillOnUndefined($params, 'connected_rooms', []), + "status" => fillOnUndefined($params, "status", 1), + 'user_id' => $this->request->auth->id, + ]; + + + + $propertyRoomType = $this->propertyRoomService->updatePropertyRoomAndBed($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyRoom(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'id' => fillOnUndefined($params, 'id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => trim(fillOnUndefined($params, 'name')), + 'room_type_id' => fillOnUndefined($params, 'room_type_id'), + 'max_occupancy' => fillOnUndefined($params, 'max_occupancy'), + 'max_adult' => fillOnUndefined($params, 'max_adult'), + 'max_child' => fillOnUndefined($params, 'max_child'), + 'occupancy_lock' => fillOnUndefined($params, 'occupancy_lock'), + 'exclude_occupancy' => fillOnUndefined($params, 'exclude_occupancy'), + 'room_size' => fillOnUndefined($params, 'room_size', null), + 'room_size_type' => fillOnUndefined($params, 'room_size_type', null), + 'room_type_count' => fillOnUndefined($params, 'room_type_count', 1), + 'room_count' => fillOnUndefined($params, 'room_count', null), + 'bathroom_count' => fillOnUndefined($params, 'bathroom_count', null), + 'toilet_count' => fillOnUndefined($params, 'toilet_count', null), + 'lounge_count' => fillOnUndefined($params, 'lounge_count', null), + 'max_child_number' => fillOnUndefined($params, 'max_child_number', null), + 'description' => fillOnUndefined($params, 'description', []), + 'user_id' => $this->request->auth->id, + ]; + + $propertyRoomType = $this->propertyRoomService->updatePropertyRoom($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + $propertyRoomType['data']['exclude_occupancy'] = json_decode($propertyRoomType['data']['exclude_occupancy'], true); + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyRoomAndBed(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'room_id' => fillOnUndefined($params, 'room_id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $this->request->auth->id, + ]; + + $propertyRoom = $this->propertyRoomService->getPropertyRoomAndBed($requestParams); + if($propertyRoom['status'] != 'success'){ + throw new ApiErrorException($propertyRoom['message']); + } + + $responseData['property_room'] = $propertyRoom['data'] ; + + + $propertyRoomType = $this->propertyRoomTypeService->getPropertyRoomTypes($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $responseData['property_room_types'] = $propertyRoomType['data'] ; + + $propertyRoomBedType = $this->propertyRoomBedTypeService->getPropertyRoomBedTypes($requestParams); + if($propertyRoomBedType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomBedType['message']); + } + + $responseData['property_room_bed_types'] = $propertyRoomBedType['data'] ; + + + $propertyRoomViewTypeCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + + $propertyRoomViewTypeList = $this->propertyRoomViewTypeService->getPropertyRoomViewTypes($propertyRoomViewTypeCriteria); + + if($propertyRoomViewTypeList['status'] != 'success'){ + throw new ApiErrorException($propertyRoomViewTypeList['message']); + } + + $responseData['property_room_view_types'] = $propertyRoomViewTypeList['data'] ; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function deletePropertyRoom(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'id' => fillOnUndefined($params, 'id'), + 'user_id' => $this->request->auth->id, + + ]; + + $propertyRoomType = $this->propertyRoomService->deletePropertyRoom($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyRoomRateChannel(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $propertyRoomType = $this->propertyRoomService->getPropertyRoomRateChannel($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyRoomInventory(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + + ]; + + $propertyRoomType = $this->propertyRoomService->getPropertyRoomInventory($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + //Best Available Rate Manipulate + foreach ($propertyRoomType['data'] as $roomKey => $room) { + foreach ($room['property_room_rate_mapping'] as $roomRateMappingKey => $roomRateMapping) { + if($roomRateMapping['name'] == 'Best Available Rate') { + unset($room['property_room_rate_mapping'][$roomRateMappingKey]); + } + } + + $propertyRoomType['data'][$roomKey]['property_room_rate_mapping'] = array_values($room['property_room_rate_mapping']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getChannelRoomRateMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + ]; + + $propertyRoomType = $this->propertyRoomService->getChannelRoomRateMapping($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getChannelBulkUpdateParams(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + ]; + + $propertyRooms = $this->propertyRoomService->getChannelRoomRateMapping($requestParams); + if($propertyRooms['status'] != 'success'){ + throw new ApiErrorException($propertyRooms['message']); + } + $propertyRooms = $propertyRooms['data'] ; + + $rooms = [] ; + foreach ($propertyRooms as $propertyRoom) { + $room = [ + 'id' => $propertyRoom['id'], + 'name' => $propertyRoom['name'], + 'room_selected' => false, + ] ; + $rates = [] ; + foreach ($propertyRoom['property_room_rate_mapping'] as $roomRate) { + + //Best Available Rate Manipulate + if($roomRate['name'] == 'Best Available Rate') { + continue; + } + + if($roomRate['is_selected'] === true){ + + $rate = [ + 'id' => $roomRate['room_rate_id'], + 'room_rate_mapping_id' => $roomRate['id'], + 'name' => $roomRate['name'], + 'rate_selected' => false, + ] ; + $rates[] = $rate ; + } + + } + if($rates){ + $room['room_rate_mapping'] = $rates; + $rooms[] = $room; + } + } + + $bulkUpdateOptionParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + 'user_id' => $this->request->auth->id, + ]; + + + $channelMapping = $this->propertyChannelMappingService->getPropertyChannelSetup($bulkUpdateOptionParams); + if($channelMapping['status'] != 'success'){ + throw new ApiErrorException($channelMapping['message']); + } + + $channelMapping = $channelMapping['data'] ; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => ['room_rates' => $rooms, 'bulk_update_options' => $channelMapping['bulk_update_options']]]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + +} diff --git a/app/Http/Controllers/V1/PropertyRoomFactMappingController.php b/app/Http/Controllers/V1/PropertyRoomFactMappingController.php new file mode 100644 index 0000000..61137ba --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomFactMappingController.php @@ -0,0 +1,135 @@ +request = $request; + $this->propertyRoomFactMappingService = $propertyRoomFactMappingService; + $this->propertyFactService = $propertyFactService; + + } + + public function updatePropertyRoomFactMapping(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try + { + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = json_decode($this->request->getContent(),1); + $params = current($params); + $params['user_id'] = $this->request->auth->id; + + $response = $this->propertyRoomFactMappingService->updatePropertyRoomFactMapping($params); + + }catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPropertyRoomFact(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestFact = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + ]; + + $getPropertyFact = $this->propertyFactService->getPropertyRoomFact($requestFact); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $getPropertyFact['data'] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyRoomFactIsFeature(){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try + { + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = json_decode($this->request->getContent(),1); + $params = current($params); + $params['user_id'] = $this->request->auth->id; + + $response = $this->propertyRoomFactMappingService->updatePropertyRoomFactIsFeature($params); + + }catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} diff --git a/app/Http/Controllers/V1/PropertyRoomPhotoMappingController.php b/app/Http/Controllers/V1/PropertyRoomPhotoMappingController.php new file mode 100644 index 0000000..f0d967e --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomPhotoMappingController.php @@ -0,0 +1,109 @@ +request = $request; + $this->propertyRoomPhotoMappingService = $propertyRoomPhotoMappingService; + $this->propertyPhotoService = $propertyPhotoService; + + } + + public function updatePropertyRoomPhotoMapping(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try + { + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + $params = json_decode($this->request->getContent(),1); + $params = current($params); + $params['user_id'] = $this->request->auth->id; + + $response = $this->propertyRoomPhotoMappingService->updatePropertyRoomPhotoMapping($params); + + }catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPropertyRoomPhoto(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestFact = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + ]; + + $getPropertyPhoto = $this->propertyPhotoService->getPropertyRoomPhoto($requestFact); + if ($getPropertyPhoto['status'] != 'success') { + throw new ApiErrorException($getPropertyPhoto['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $getPropertyPhoto['data'] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + +} diff --git a/app/Http/Controllers/V1/PropertyRoomRateChannelMappingController.php b/app/Http/Controllers/V1/PropertyRoomRateChannelMappingController.php new file mode 100644 index 0000000..4236d58 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomRateChannelMappingController.php @@ -0,0 +1,177 @@ +request = $request; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyChannelService = $propertyChannelService ; + + } + + + + public function getPropertyRoomRateChannelMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + ]; + + $propertyChannel = $this->propertyChannelService->getPropertyChannels($requestParams); + if($propertyChannel['status'] != 'success'){ + throw new ApiErrorException($propertyChannel['message']); + } + $propertyChannels = collect($propertyChannel['data'])->where('is_selected', '=', true); + if($requestParams['channel_id']){ + $propertyChannels = $propertyChannels->whereIn('id', $requestParams['channel_id']) ; + } + $propertyChannels = $propertyChannels->values()->toArray() ; + $requestParams['channels'] = $propertyChannels ; + + $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingService->getPropertyRoomRateChannelMapping($requestParams); + if($propertyRoomRateChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateChannelMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateChannelMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomRateChannelMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id'), + 'channels' => fillOnUndefined($params, 'channels'), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingService->addPropertyRoomRateChannelMapping($requestParams); + if($propertyRoomRateChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateChannelMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateChannelMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyRoomRateChannelMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_channel_mapping' => fillOnUndefined($params, 'room_rate_channel_mapping'), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingService->updatePropertyRoomRateChannelMapping($requestParams); + if($propertyRoomRateChannelMapping['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateChannelMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateChannelMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + + +} diff --git a/app/Http/Controllers/V1/PropertyRoomRateController.php b/app/Http/Controllers/V1/PropertyRoomRateController.php new file mode 100644 index 0000000..58189ca --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomRateController.php @@ -0,0 +1,527 @@ +request = $request; + $this->propertyRoomRateService = $propertyRoomRateService; + $this->propertyRoomRateMappingService = $propertyRoomRateMappingService; + $this->propertyRoomService = $propertyRoomService; + } + + + + public function getPropertyRoomRate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $propertyRoomRate = $this->propertyRoomRateService->getPropertyRoomRates($requestParams); + if($propertyRoomRate['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRate['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyRoomRateConnected(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + ]; + + $propertyRoomRateConnected = $this->propertyRoomRateMappingService->getPropertyRoomRateMappingConnected($requestParams); + if($propertyRoomRateConnected['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateConnected['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateConnected['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function syncPropertyRoomRateConnected(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'connected_room_rate' => fillOnUndefined($params, 'connected_room_rate'), + ]; + + $propertyRoomRateConnected = $this->propertyRoomRateMappingService->syncPropertyRoomRateMappingConnected($requestParams); + if($propertyRoomRateConnected['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateConnected['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateConnected['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomRate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'description' => fillOnUndefined($params, 'description'), + 'accommodation_type' => fillOnUndefined($params, 'accommodation_type'), + 'min_stay' => fillOnUndefined($params, 'min_stay'), + 'max_stay' => fillOnUndefined($params, 'max_stay'), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomRate = $this->propertyRoomRateService->addPropertyRoomRate($requestParams); + if($propertyRoomRate['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRate['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomRateWithInclusion(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'accommodation_type' => fillOnUndefined($params, 'accommodation_type'), + 'description' => fillOnUndefined($params, 'description'), + /* 'min_stay' => fillOnUndefined($params, 'min_stay'), + 'max_stay' => fillOnUndefined($params, 'max_stay'),*/ + 'facts' => fillOnUndefined($params, 'facts'), + 'user_id' => $this->request->auth->id, + ]; + + $propertyRoomRate = $this->propertyRoomRateService->addPropertyRoomRateWithInclusion($requestParams); + if($propertyRoomRate['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRate['message']); + } + + foreach ($params['rooms'] as $room){ + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => [$room['room_id']], + 'room_rate_id' => $propertyRoomRate['data']['id'], + 'rack_rate' => fillOnUndefined($params, 'rack_rate'), + 'included_occupancy' => $room['value'], + 'room_rate_mapping_setup' => [ + [ + 'setup_type' => 'EXT_ADULT', + 'value_type' => fillOnUndefined($params, 'ext_adult_type'), + 'value' => fillOnUndefined($params, 'ext_adult_rate'), + ], + + [ + 'setup_type' => 'EXT_CHILD', + 'value_type' => fillOnUndefined($params, 'ext_child_type'), + 'value' => fillOnUndefined($params, 'ext_child_rate'), + ], + + [ + 'setup_type' => 'DIS_GUEST', + 'value_type' => fillOnUndefined($params, 'dis_guest_type'), + 'value' => fillOnUndefined($params, 'dis_guest_rate'), + ], + + + ], + 'user_id' => $this->request->auth->id, + ]; + + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->addPropertyRoomRateMappingWithSetup($requestParams); + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyRoomRate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + DB::beginTransaction(); + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'id' => fillOnUndefined($params, 'id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => trim(fillOnUndefined($params, 'name')), + 'accommodation_type' => fillOnUndefined($params, 'accommodation_type'), + 'min_stay' => fillOnUndefined($params, 'min_stay'), + 'facts' => fillOnUndefined($params, 'facts', []), + 'description' => fillOnUndefined($params, 'description'), + 'user_id' => $this->request->auth->id, + ]; + + + if(isset($params['status'])){ + $requestParams['status'] = fillOnUndefined($params, "status", 0); + } + $propertyRoomRate = $this->propertyRoomRateService->updatePropertyRoomRate($requestParams); + if($propertyRoomRate['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRate['message']); + } + + if(isset($params['rooms'])){ + foreach ($params['rooms'] as $room){ + + $criteria = [ + + ['field' => 'room_id', 'condition' => '=', 'value' => $room['room_id']], + ['field' => 'room_rate_id', 'condition' => '=', 'value' => $params['id']] + + ]; + + $data = [ + 'room_id' => fillOnUndefined($room, 'room_id'), + 'room_rate_id' => fillOnUndefined($params, 'id'), + 'included_occupancy' => fillOnUndefined($room, 'value'), + 'status' => 1, + 'created_by' => $this->request->auth->id, + 'updated_by' => $this->request->auth->id, + 'created_at' => time(), + 'updated_at' => time(), + ]; + + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->updateOrCreate($criteria,$data); + if($propertyRoomRateMapping['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + } + } + + DB::commit(); + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRate['data']]; + + } catch (ApiErrorException $e) { + DB::rollBack(); + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + DB::rollBack(); + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function deletePropertyRoomRate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'id' => fillOnUndefined($params, 'id'), + 'user_id' => $this->request->auth->id, + + ]; + + $propertyRoomRate = $this->propertyRoomRateService->deletePropertyRoomRate($requestParams); + if($propertyRoomRate['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRate['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyAccommodationTypes(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $this->request->auth->id, + + ]; + $propertyRoomRate = $this->propertyRoomRateService->getPropertyAccommodationTypes($requestParams); + if($propertyRoomRate['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRate['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function editPropertyRoomRate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + $criteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $params['room_rate_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'firstRow' => 1 + ]; + + $propertyRoomRate = $this->propertyRoomRateService->select($criteria); + if($propertyRoomRate['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRate['message']); + } + + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'with' => ['propertyRoomRateMapping'] + ]; + + $propertyRoom = $this->propertyRoomService->select($criteria); + if($propertyRoom['status'] != 'success'){ + throw new ApiErrorException($propertyRoom['message']); + } + + $propertyRoomRate['data']['rooms'] = $propertyRoom['data']; + + foreach ($propertyRoomRate['data']['rooms'] as $roomKey => $roomVal){ + + $propertyRoomRate['data']['rooms'][$roomKey]['is_selected'] = false; + $propertyRoomRate['data']['rooms'][$roomKey]['is_selected_lock'] = false; + $propertyRoomRate['data']['rooms'][$roomKey]['included_occupancy'] = null; + + foreach ($roomVal['property_room_rate_mapping'] as $rate){ + if($rate['room_rate_id'] == $params['room_rate_id']){ + $propertyRoomRate['data']['rooms'][$roomKey]['is_selected'] = true; + $propertyRoomRate['data']['rooms'][$roomKey]['is_selected_lock'] = true; + $propertyRoomRate['data']['rooms'][$roomKey]['included_occupancy'] = $rate['included_occupancy']; + break; + } + } + + unset($propertyRoomRate['data']['rooms'][$roomKey]['property_room_rate_mapping']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRate['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + + +} diff --git a/app/Http/Controllers/V1/PropertyRoomRateInclusionMappingController.php b/app/Http/Controllers/V1/PropertyRoomRateInclusionMappingController.php new file mode 100644 index 0000000..c4ae946 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomRateInclusionMappingController.php @@ -0,0 +1,124 @@ +request = $request; + $this->propertyRoomRateInclusionMappingService = $propertyRoomRateInclusionMappingService; + + } + + + + public function getPropertyRoomRateInclusionMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'user_id' => $this->request->auth->id, + ]; + + $propertyRoomRateInclusionMapping = $this->propertyRoomRateInclusionMappingService->getPropertyRoomRateInclusionMapping($requestParams); + if($propertyRoomRateInclusionMapping['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateInclusionMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateInclusionMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomRateInclusionMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'facts' => fillOnUndefined($params, 'facts'), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomRateInclusionMapping = $this->propertyRoomRateInclusionMappingService->addPropertyRoomRateInclusionMapping($requestParams); + if($propertyRoomRateInclusionMapping['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateInclusionMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateInclusionMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + + + + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyRoomRateMappingController.php b/app/Http/Controllers/V1/PropertyRoomRateMappingController.php new file mode 100644 index 0000000..de10747 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomRateMappingController.php @@ -0,0 +1,1630 @@ +request = $request; + $this->propertyRoomRateMappingService = $propertyRoomRateMappingService; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService; + $this->propertyChannelGroupService = $propertyChannelGroupService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService; + $this->propertyRoomService = $propertyRoomService; + $this->mailer = $mailer; + $this->languageService = $languageService; + + $this->actionTitleByKey = [ + 'PRC' => [ + 'title' => 'Price', + 'language_key' => 'enw-action-title-price', + ], + 'AVA' => [ + 'title' => 'Availability', + 'language_key' => 'enw-action-title-availability', + ], + 'STS' => [ + 'title' => 'Stop Sell', + 'language_key' => 'enw-action-title-stop_sell', + ], + 'MNS' => [ + 'title' => 'Min Stay', + 'language_key' => 'enw-action-title-min_stay', + ] + ]; + + } + + + public function getPropertyRoomRateMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + ]; + + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->getPropertyRoomRateMapping($requestParams); + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomRateMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'description' => fillOnUndefined($params, 'description'), + 'rack_rate' => fillOnUndefined($params, 'rack_rate'), + 'included_occupancy' => fillOnUndefined($params, 'included_occupancy'), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->addPropertyRoomRateMapping($requestParams); + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomRateMappingWithSetup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'rack_rate' => fillOnUndefined($params, 'rack_rate'), + 'included_occupancy' => fillOnUndefined($params, 'included_occupancy'), + 'room_rate_mapping_setup' => [ + [ + 'setup_type' => 'EXT_ADULT', + 'value_type' => fillOnUndefined($params, 'ext_adult_type'), + 'value' => fillOnUndefined($params, 'ext_adult_rate'), + ], + + [ + 'setup_type' => 'EXT_CHILD', + 'value_type' => fillOnUndefined($params, 'ext_child_type'), + 'value' => fillOnUndefined($params, 'ext_child_rate'), + ], + + [ + 'setup_type' => 'DIS_GUEST', + 'value_type' => fillOnUndefined($params, 'dis_guest_type'), + 'value' => fillOnUndefined($params, 'dis_guest_rate'), + ], + + + ], + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->addPropertyRoomRateMappingWithSetup($requestParams); + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyRoomRateMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'locale' => fillOnUndefined($params, 'locale'), + 'id' => fillOnUndefined($params, 'id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_id' => fillOnUndefined($params, 'room_rate_id'), + 'description' => fillOnUndefined($params, 'description'), + 'rack_rate' => fillOnUndefined($params, 'rack_rate'), + 'included_occupancy' => fillOnUndefined($params, 'included_occupancy'), + + 'user_id' => $this->request->auth->id, + ]; + + + if (isset($params['status'])) { + $requestParams['status'] = fillOnUndefined($params, "status", 0); + } + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->updatePropertyRoomRateMapping($requestParams); + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function deletePropertyRoomRateMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'id' => fillOnUndefined($params, 'id'), + 'user_id' => $this->request->auth->id, + + ]; + + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->deletePropertyRoomRateMapping($requestParams); + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function channelGroupRoomRateAvailabilityMappingBulkUpdate($requestParams = []) + { + + $channelGroupChannels = []; + $channelGroupCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $requestParams['channel_group_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true, + 'with' => ['channelGroupActiveChannels'] + ]; + + $channelGroup = $this->propertyChannelGroupService->select($channelGroupCriteria); + + if ($channelGroup['status'] == 'success') { + $channelGroupChannels = $channelGroup['data']['channel_group_active_channels']; + } + + $paramsListChannel = []; + foreach ($channelGroupChannels as $channel) { + + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channel['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + if ($propertyChannelMapping['status'] != 'success') { + continue; + } + + + $propertyRoomRateChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channel['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingService->select($propertyRoomRateChannelMappingCriteria); + if ($propertyChannelMapping['status'] != 'success') { + continue; + } + + $propertyRoomRateChannelMappingCollect = collect($propertyRoomRateChannelMapping['data']); + + if ($requestParams['update_type'] == 'rate') { + + + foreach ($requestParams['room_rates'] as $roomRate) { + + $value = null; + $roomRates = []; + + //currency_code check + if ($roomRate['currency'] == $propertyChannelMapping['data']['currency_code']) { + + //room_rate_mapping_id check + if ($propertyRoomRateChannelMappingCollect->where('room_rate_mapping_id', $roomRate['room_rate_mapping_id'])->isNotEmpty()) { + + $value = $roomRate['value']; + $roomRates[] = [ + 'room_id' => $roomRate['room_id'], + 'room_rate_mapping_id' => [ + $roomRate['room_rate_mapping_id'] + ], + ]; + + } + + } + + if (is_null($value) || empty($roomRate)) { + continue; + } + + $paramsListChannel[] = [ + 'locale' => fillOnUndefined($requestParams, 'locale'), + 'property_id' => fillOnUndefined($requestParams, 'property_id'), + 'update_type' => fillOnUndefined($requestParams, 'update_type'), + 'value' => $value, + 'channel_id' => fillOnUndefined($channel, 'channel_id'), + 'availability_type_id' => fillOnUndefined($requestParams, 'availability_type_id', 1), + 'room_rates' => $roomRates, + 'start_date' => fillOnUndefined($requestParams, 'start_date'), + 'end_date' => fillOnUndefined($requestParams, 'end_date'), + 'include_days' => fillOnUndefined($requestParams, 'include_days'), + 'user_id' => $this->request->auth->id, + ]; + + + } + + } elseif (in_array($requestParams['update_type'], ['min_stay', 'rate_stop_sell'])) { + + foreach ($requestParams['room_rates'] as $roomRate) { + + $value = null; + $roomRates = []; + + //room_rate_mapping_id check + if ($propertyRoomRateChannelMappingCollect->where('room_rate_mapping_id', $roomRate['room_rate_mapping_id'])->isNotEmpty()) { + + $value = $roomRate['value']; + $roomRates[] = [ + 'room_id' => $roomRate['room_id'], + 'room_rate_mapping_id' => [ + $roomRate['room_rate_mapping_id'] + ], + ]; + + } + + + if (is_null($value) || empty($roomRate)) { + continue; + } + + $paramsListChannel[] = [ + 'locale' => fillOnUndefined($requestParams, 'locale'), + 'property_id' => fillOnUndefined($requestParams, 'property_id'), + 'update_type' => fillOnUndefined($requestParams, 'update_type'), + 'value' => $value, + 'channel_id' => fillOnUndefined($channel, 'channel_id'), + 'availability_type_id' => fillOnUndefined($requestParams, 'availability_type_id', 1), + 'room_rates' => $roomRates, + 'start_date' => fillOnUndefined($requestParams, 'start_date'), + 'end_date' => fillOnUndefined($requestParams, 'end_date'), + 'include_days' => fillOnUndefined($requestParams, 'include_days'), + 'user_id' => $this->request->auth->id, + ]; + + } + + + } + + + } + + //dd($paramsListChannel, $requestParams); + + return $paramsListChannel; + } + + public function bulkUpdate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + DB::beginTransaction(); + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + + if (!Carbon::parse($params['start_date'])->lessThanOrEqualTo(Carbon::parse($params['end_date']))) { + throw new ApiErrorException(lang('api-error-update-start_end_date_diff')); + } + + $paramsListChannel = []; + if (isset($params['channel_group_id'])) { + + if (Carbon::parse($params['start_date'])->diffInDays(Carbon::parse($params['end_date'])) > 365) { + throw new ApiErrorException(lang('api-error-update-date_month')); + } + + $paramsListChannel = $this->channelGroupRoomRateAvailabilityMappingBulkUpdate($params); + + } elseif (isset($params['channel_id'])) { + + //CONNECTED CHANNEL RATE CHECK + $currentChannelCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true, + 'with' => ['channel'] + ]; + + $currentChannel = $this->propertyChannelMappingService->select($currentChannelCriteria); + if ($currentChannel['status'] != 'success') { + throw new ApiErrorException($currentChannel['message']); + } + + $currentChannel = $currentChannel['data']; + + if (!is_null($currentChannel['connected_channel_id'])) { + throw new ApiErrorException('Channel prices linked to a channel cannot be updated.'); + } + //CONNECTED CHANNEL RATE CHECK + + $paramsListChannel[] = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'update_type' => fillOnUndefined($params, 'update_type'), + 'value' => fillOnUndefined($params, 'value'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + 'availability_type_id' => fillOnUndefined($params, 'availability_type_id', 1), + 'room_rates' => fillOnUndefined($params, 'room_rates'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + 'include_days' => fillOnUndefined($params, 'include_days'), + 'user_id' => $this->request->auth->id, + ]; + + + //CONNECTED CHANNEL RATE UPDATE + if (in_array($params['update_type'], ['rate', 'rate_by_rate', 'rate_stop_sell', 'min_stay', 'quick_pricing'])) { + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + if ($propertyChannelMapping['status'] == 'success') { + + foreach ($propertyChannelMapping['data'] as $channel) { + + if (!is_null($channel['channel']['parent_id'])) { + continue; + } + + $paramsListChannel[] = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'update_type' => fillOnUndefined($params, 'update_type'), + 'value' => fillOnUndefined($params, 'value'), + 'channel_id' => $channel['channel_id'], + 'availability_type_id' => fillOnUndefined($params, 'availability_type_id', 1), + 'room_rates' => fillOnUndefined($params, 'room_rates'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + 'include_days' => fillOnUndefined($params, 'include_days'), + 'user_id' => $this->request->auth->id, + ]; + + } + + } + } + //CONNECTED CHANNEL RATE UPDATE + + } + + + if (in_array($params['update_type'], ['rate'])) { + + + //CONNECTED RATE + $roomRates = collect($params['room_rates']); + $propertyRoomIds = $roomRates->pluck('room_id')->toArray(); + $propertyRoomIds = array_unique($propertyRoomIds); + + $propertyRoomRateMappingCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'whereIn' => [ + ['field' => 'room_id', 'value' => $propertyRoomIds] + ] + ]; + + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->select($propertyRoomRateMappingCriteria); + $propertyRoomRateMapping = $propertyRoomRateMapping['status'] == 'success' ? $propertyRoomRateMapping['data'] : []; + $propertyRoomRateMapping = collect($propertyRoomRateMapping); + + $connectedRoomRateBulk = []; + foreach ($params['room_rates'] as $roomRateKey => $roomRate) { + + $currentRoomRateIds = $propertyRoomRateMapping->whereIn('id', $roomRate['room_rate_mapping_id'])->pluck('room_rate_id')->toArray(); + + $propertyRoomRateConnectedList = $propertyRoomRateMapping->where('room_id', $roomRate['room_id'])->whereIn('connected_rate_id', $currentRoomRateIds)->toArray(); + + if (empty($propertyRoomRateConnectedList)) { + continue; + } + + foreach ($propertyRoomRateConnectedList as $propertyRoomRateConnected) { + + $roomRateConnectedRoomRates = []; + $roomRateConnectedAmount = $params['value']; + + $amountAffected = 0; + if ($propertyRoomRateConnected['is_affected_price']) { + if ($propertyRoomRateConnected['affect_price_type'] == 'PER') { + $amountAffected = ($roomRateConnectedAmount * $propertyRoomRateConnected['affect_price_value']) / 100; + } elseif ($propertyRoomRateConnected['affect_price_type'] == 'FIX') { + $amountAffected = $propertyRoomRateConnected['affect_price_value']; + } + + if ($propertyRoomRateConnected['affect_price_action_type'] == 'INC') { + $roomRateConnectedAmount = $roomRateConnectedAmount + $amountAffected; + } + + if ($propertyRoomRateConnected['affect_price_action_type'] == 'DEC') { + $roomRateConnectedAmount = $roomRateConnectedAmount - $amountAffected; + } + } + + $roomRateConnectedAmount = $roomRateConnectedAmount == 0 ? "0" : $roomRateConnectedAmount; + + $roomRateConnectedRoomRates[] = [ + 'room_id' => $propertyRoomRateConnected['room_id'], + 'room_rate_mapping_id' => [$propertyRoomRateConnected['id']], + ]; + + foreach ($paramsListChannel as $channelParam) { + $connectedRoomRateBulk[] = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'update_type' => fillOnUndefined($params, 'update_type'), + 'value' => $roomRateConnectedAmount, + 'channel_id' => $channelParam['channel_id'], + 'availability_type_id' => fillOnUndefined($params, 'availability_type_id', 1), + 'room_rates' => $roomRateConnectedRoomRates, + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + 'include_days' => fillOnUndefined($params, 'include_days'), + 'user_id' => $this->request->auth->id, + ]; + } + + } + + } + + if (!empty($connectedRoomRateBulk)) { + $paramsListChannel = array_merge($paramsListChannel, $connectedRoomRateBulk); + } + + } + + //CONNECTED RATE + + foreach ($paramsListChannel as $channelParam) { + + + $requestParams = [ + 'locale' => fillOnUndefined($channelParam, 'locale'), + 'property_id' => fillOnUndefined($channelParam, 'property_id'), + 'update_type' => fillOnUndefined($channelParam, 'update_type'), + 'value' => fillOnUndefined($channelParam, 'value'), + 'channel_id' => fillOnUndefined($channelParam, 'channel_id'), + 'availability_type_id' => fillOnUndefined($channelParam, 'availability_type_id', 1), + 'room_rates' => fillOnUndefined($channelParam, 'room_rates'), + 'start_date' => fillOnUndefined($channelParam, 'start_date'), + 'end_date' => fillOnUndefined($channelParam, 'end_date'), + 'include_days' => fillOnUndefined($channelParam, 'include_days'), + 'user_id' => $this->request->auth->id, + ]; + + $propertyRoomRateMapping = []; + if ($requestParams['update_type'] == 'availability' || $requestParams['update_type'] == 'room_stop_sell') { + $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams); + } elseif ($requestParams['update_type'] == 'rate_by_rate') { + + $roomRatesPriceGroup = []; + foreach ($requestParams['room_rates'] as $roomRateMapping) { + foreach ($roomRateMapping['room_rate_mapping_id'] as $roomRates) { + foreach ($roomRates as $roomRateMappingId => $roomRatePrice) { + $roomRatePriceHashed = md5($roomRatePrice); + $roomRatesPriceGroup[$roomRatePriceHashed]['amount'] = $roomRatePrice; + $roomRatesPriceGroup[$roomRatePriceHashed]['roomRates'][$roomRateMapping['room_id']][$roomRateMappingId] = [ + 'roomId' => $roomRateMapping['room_id'], + 'roomRateMappingId' => $roomRateMappingId + ]; + } + } + } + + foreach ($roomRatesPriceGroup as $roomRatesPrice) { + + $requestParams['room_rates'] = []; + $requestParams['value'] = $roomRatesPrice['amount']; + $requestParams['update_type'] = 'rate'; + + $roomRateKey = 0; + foreach ($roomRatesPrice['roomRates'] as $roomId => $roomRates) { + foreach ($roomRates as $roomRateMappingId => $roomRate) { + $requestParams['room_rates'][$roomRateKey]['room_id'] = $roomId; + $requestParams['room_rates'][$roomRateKey]['room_rate_mapping_id'][] = $roomRateMappingId; + } + $roomRateKey++; + } + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + + } + + } else { + + $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams); + } + + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateMapping['data']]; + + DB::commit(); + + //$roomRateAvailabilityUpdateNotification + foreach ($paramsListChannel as $channelParam) { + $roomRateAvailabilityUpdateNotification = $this->roomRateAvailabilityBulkUpdateNotification($channelParam); + } + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + DB::rollBack(); + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + public function channelGroupRoomRateAvailabilityMapping($requestParams = []) + { + + //PRC_1_1_1_20210424 + //availability_type_id, room_id, room_rate_mapping_id, date + + $channelGroupChannels = []; + $channelGroupCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $requestParams['channel_group_id']], + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true, + 'with' => ['channelGroupActiveChannels'] + ]; + + $channelGroup = $this->propertyChannelGroupService->select($channelGroupCriteria); + + if ($channelGroup['status'] == 'success') { + $channelGroupChannels = $channelGroup['data']['channel_group_active_channels']; + } + + $paramsListChannel = []; + foreach ($channelGroupChannels as $channel) { + + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channel['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + if ($propertyChannelMapping['status'] != 'success') { + continue; + } + + + $propertyRoomRateChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $channel['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingService->select($propertyRoomRateChannelMappingCriteria); + if ($propertyChannelMapping['status'] != 'success') { + continue; + } + + $propertyRoomRateChannelMappingCollect = collect($propertyRoomRateChannelMapping['data']); + + + $roomRateAvailabilityMapping = []; + foreach ($requestParams['data'] as $roomRateKey => $roomRateValue) { + + $roomRateKeyParsed = explode('_', $roomRateKey); + + $roomRateKeyArray['type'] = $roomRateKeyParsed[0]; + $roomRateKeyArray['availabilityId'] = $roomRateKeyParsed[1]; + $roomRateKeyArray['roomId'] = $roomRateKeyParsed[2]; + $roomRateKeyArray['roomRateMappingId'] = $roomRateKeyParsed[3]; + $roomRateKeyArray['date'] = $roomRateKeyParsed[4]; + $roomRateKeyArray['currencyCode'] = $roomRateKeyParsed[0] == 'PRC' && isset($roomRateKeyParsed[5]) ? $roomRateKeyParsed[5] : null; + + if ($roomRateKeyArray['type'] == 'PRC') { + + //currency_code check + if (!is_null($roomRateKeyArray['currencyCode']) && $roomRateKeyArray['currencyCode'] == $propertyChannelMapping['data']['currency_code']) { + + //room_rate_mapping_id check + if ($propertyRoomRateChannelMappingCollect->where('room_rate_mapping_id', $roomRateKeyArray['roomRateMappingId'])->isNotEmpty()) { + unset($roomRateKeyArray['currencyCode']); + $roomRateKey = implode('_', $roomRateKeyArray); + $roomRateAvailabilityMapping[$roomRateKey] = $roomRateValue; + } + + } + + } elseif (in_array($roomRateKeyArray['type'], ['STS', 'MNS'])) { + + if ($roomRateKeyArray['roomRateMappingId'] != 0) { + + //room_rate_mapping_id check + if ($propertyRoomRateChannelMappingCollect->where('room_rate_mapping_id', $roomRateKeyArray['roomRateMappingId'])->isNotEmpty()) { + $roomRateAvailabilityMapping[$roomRateKey] = $roomRateValue; + } + + } else { + $roomRateAvailabilityMapping[$roomRateKey] = $roomRateValue; + } + + } else { + $roomRateAvailabilityMapping[$roomRateKey] = $roomRateValue; + } + + } + + if (!empty($roomRateAvailabilityMapping)) { + $paramsListChannel[] = [ + 'locale' => fillOnUndefined($requestParams, 'locale'), + 'property_id' => fillOnUndefined($requestParams, 'property_id'), + 'channel_id' => fillOnUndefined($channel, 'channel_id'), + 'rates' => $roomRateAvailabilityMapping, + 'user_id' => $this->request->auth->id, + ]; + } + + } + + //dd($paramsListChannel, $requestParams); + + return $paramsListChannel; + } + + public function roomRateAvailabilityUpdate(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + DB::beginTransaction(); + + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $paramsListChannel = []; + if (isset($params['channel_group_id'])) { + + $paramsListChannel = $this->channelGroupRoomRateAvailabilityMapping($params); + + } elseif (isset($params['channel_id'])) { + + + //CONNECTED CHANNEL RATE CHECK + $currentChannelCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true, + 'with' => ['channel'] + ]; + + $currentChannel = $this->propertyChannelMappingService->select($currentChannelCriteria); + if ($currentChannel['status'] != 'success') { + throw new ApiErrorException($currentChannel['message']); + } + + $currentChannel = $currentChannel['data']; + + if (!is_null($currentChannel['connected_channel_id'])) { + throw new ApiErrorException('Channel prices linked to a channel cannot be updated.'); + } + //CONNECTED CHANNEL RATE CHECK + + $paramsListChannel[] = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + 'rates' => fillOnUndefined($params, 'data'), + 'user_id' => $this->request->auth->id, + ]; + + //CONNECTED CHANNEL RATE UPDATE + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'] + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + if ($propertyChannelMapping['status'] == 'success') { + + foreach ($propertyChannelMapping['data'] as $channel) { + + if (!is_null($channel['channel']['parent_id'])) { + continue; + } + + $paramsListChannel[] = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'channel_id' => $channel['channel_id'], + 'rates' => fillOnUndefined($params, 'data'), + 'user_id' => $this->request->auth->id, + ]; + + } + + } + //CONNECTED CHANNEL RATE UPDATE + + } + + foreach ($paramsListChannel as $channelParam) { + + $requestParams = [ + 'locale' => fillOnUndefined($channelParam, 'locale'), + 'property_id' => fillOnUndefined($channelParam, 'property_id'), + 'channel_id' => fillOnUndefined($channelParam, 'channel_id'), + 'rates' => fillOnUndefined($channelParam, 'rates'), + 'user_id' => $this->request->auth->id, + ]; + + $formMapping = $this->propertyRoomRatePriceService->formElementsToArray($requestParams); + + if (!$formMapping) { + throw new ApiErrorException('Form Error.'); + } + + $requestParams['rates'] = $formMapping['rates']; + $requestParams['availability'] = $formMapping['availability']; + + //Amount Empty to Zero Value !important + foreach ($requestParams['rates'] as $roomRateKey => $roomRate) { + if (isset($roomRate['amount']) && empty($roomRate['amount'])) { + $requestParams['rates'][$roomRateKey]['amount'] = "0"; + } + } + + //CONNECTED RATE + $roomRates = collect($requestParams['rates']); + $propertyRoomIds = $roomRates->pluck('room_id')->toArray(); + $propertyRoomIds = array_unique($propertyRoomIds); + + + $propertyRoomRateMappingCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'whereIn' => [ + ['field' => 'room_id', 'value' => $propertyRoomIds] + ] + ]; + + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->select($propertyRoomRateMappingCriteria); + $propertyRoomRateMapping = $propertyRoomRateMapping['status'] == 'success' ? $propertyRoomRateMapping['data'] : []; + $propertyRoomRateMapping = collect($propertyRoomRateMapping); + + //dd($propertyRoomRateMapping, $propertyRoomRateMappingCriteria); + + foreach ($requestParams['rates'] as $roomRateKey => $roomRate) { + + if (!isset($roomRate['amount'])) { + continue; + } + + $currentRoomRateIds = $propertyRoomRateMapping->whereIn('id', $roomRate['room_rate_mapping_id'])->pluck('room_rate_id')->toArray(); + $propertyRoomRateConnectedList = $propertyRoomRateMapping->where('room_id', $roomRate['room_id'])->whereIn('connected_rate_id', $currentRoomRateIds)->toArray(); + + + if (empty($propertyRoomRateConnectedList)) { + continue; + } + + foreach ($propertyRoomRateConnectedList as $propertyRoomRateConnected) { + + $roomRateConnected = $roomRate; + $roomRateConnectedAmount = $roomRate['amount']; + + $amountAffected = 0; + if ($propertyRoomRateConnected['is_affected_price']) { + if ($propertyRoomRateConnected['affect_price_type'] == 'PER') { + $amountAffected = ($roomRateConnectedAmount * $propertyRoomRateConnected['affect_price_value']) / 100; + } elseif ($propertyRoomRateConnected['affect_price_type'] == 'FIX') { + $amountAffected = $propertyRoomRateConnected['affect_price_value']; + } + + if ($propertyRoomRateConnected['affect_price_action_type'] == 'INC') { + $roomRateConnectedAmount = $roomRateConnectedAmount + $amountAffected; + } + + if ($propertyRoomRateConnected['affect_price_action_type'] == 'DEC') { + $roomRateConnectedAmount = $roomRateConnectedAmount - $amountAffected; + } + } + + $roomRateConnected['amount'] = $roomRateConnectedAmount == 0 ? "0" : $roomRateConnectedAmount; + $roomRateConnected['room_rate_mapping_id'] = $propertyRoomRateConnected['id']; + + + $roomRateConnectedKeyParam = []; + $roomRateConnectedKeyParam[] = $roomRateConnected['setup_type_id']; + $roomRateConnectedKeyParam[] = $roomRateConnected['room_id']; + $roomRateConnectedKeyParam[] = $roomRateConnected['room_rate_mapping_id']; + $roomRateConnectedKeyParam[] = $roomRateConnected['date']; + $roomRateConnectedKey = implode('|', $roomRateConnectedKeyParam); + + if (array_key_exists($roomRateConnectedKey, $requestParams['rates'])) { + continue; + } + + $requestParams['rates'][$roomRateConnectedKey] = $roomRateConnected; + + } + + } + //CONNECTED RATE + + + $roomRateUpdate = $this->propertyRoomRatePriceService->roomRateUpdate($requestParams); + if ($roomRateUpdate['status'] != 'success') { + throw new ApiErrorException($roomRateUpdate['message']); + } + + $availabilityUpdate = $this->propertyRoomAvailabilityService->roomAvailabilityUpdate($requestParams); + + if ($availabilityUpdate['status'] != 'success') { + throw new ApiErrorException($availabilityUpdate['message']); + } + + + } + + DB::commit(); + + //$roomRateAvailabilityUpdateNotification + foreach ($paramsListChannel as $channelParam) { + $roomRateAvailabilityUpdateNotification = $this->roomRateAvailabilityUpdateNotification($channelParam); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + DB::rollBack(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function roomRateAvailabilityUpdateNotification($requestParams = []) + { + + //dd($requestParams['locale'], $requestParams['property_id'], $requestParams['channel_id']); + + //PRC_1_1_1_20210424 - Price Room Rate + //availability_type_id, room_id, room_rate_mapping_id, date + //AVA_1_2_0_20210424 - Availability Room + //STS_1_1_0_20210426 - Stop Sell Room + //STS_1_1_1_20210425 - Stop Sell Room Rate + //MNS_1_1_1_20210421 - Min Stay Room Rate + + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $requestParams['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel', 'property', 'channelContact'], + 'firstRow' => true + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + if ($propertyChannelMapping['status'] != 'success') { + return false; + } + + if (empty($propertyChannelMapping['data']['channel_contact'])) { + return false; + } + + $channelContact = collect($propertyChannelMapping['data']['channel_contact'])->where('status', 1)->pluck('email')->toArray(); + + if (empty($channelContact)) { + return true; + } + + + //LANGUAGE + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + $availableLanguages = Collect($availableLanguages['data'])->keyBy('code')->all(); + + $mailLanguage = 'en'; + if (array_key_exists($propertyChannelMapping['data']['channel']['country_code'], $availableLanguages)) { + $mailLanguage = $propertyChannelMapping['data']['channel']['country_code']; + } + + + $propertyRoomRateChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $requestParams['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType' + ] + ]; + + $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingService->select($propertyRoomRateChannelMappingCriteria); + $propertyRoomRateChannelMappingCollect = collect($propertyRoomRateChannelMapping['data']); + + $propertyRoomsParams = [ + 'property_id' => $requestParams['property_id'], + ]; + + $getPropertyRooms = $this->propertyRoomService->getPropertyRooms($propertyRoomsParams); + $propertyRoomsCollect = collect($getPropertyRooms['data']); + + + $roomRateGrouped = []; + foreach ($requestParams['rates'] as $roomRateKey => $roomRateValue) { + + $roomRateKeyParsed = explode('_', $roomRateKey); + + $roomRateKeyArray['type'] = $roomRateKeyParsed[0]; + $roomRateKeyArray['availabilityId'] = $roomRateKeyParsed[1]; + $roomRateKeyArray['roomId'] = $roomRateKeyParsed[2]; + $roomRateKeyArray['roomRateMappingId'] = $roomRateKeyParsed[3]; + $roomRateKeyArray['date'] = $roomRateKeyParsed[4]; + + $roomRateGrouped[$roomRateKeyArray['type']] + [$roomRateKeyArray['roomId']][$roomRateKeyArray['roomRateMappingId']][$roomRateKeyArray['date']] = [ + 'value' => $roomRateValue, + 'currency' => $propertyChannelMapping['data']['currency_code'], + ]; + + ksort($roomRateGrouped[$roomRateKeyArray['type']][$roomRateKeyArray['roomId']][$roomRateKeyArray['roomRateMappingId']]); + + } + + $roomRateNotificationGroup = []; + foreach ($roomRateGrouped as $typeKey => $room) { + + foreach ($room as $roomKey => $roomRate) { + + //roomDetail + $roomDetail = $propertyRoomsCollect->where('id', $roomKey)->first(); + if (empty($roomDetail)) { + continue; + } + + foreach ($roomRate as $roomRateKey => $roomRateDate) { + + + //roomRateChannelMappingDetail + $roomRateDetail = []; + $roomRateChannelMappingDetail = $propertyRoomRateChannelMappingCollect->where('room_rate_mapping_id', $roomRateKey)->first(); + if (empty($roomRateChannelMappingDetail) && $roomRateKey != 0) { + continue; + } + + if ($roomRateKey != 0) { + $roomRateDetail = $roomRateChannelMappingDetail['property_room_rate_mapping']['property_room_rate']; + } + + + $actionByDate = []; + foreach ($roomRateDate as $dateKey => $dateValue) { + + $dateKey = substr($dateKey, 0, 4) . '-' . substr($dateKey, 4, 2) . '-' . substr($dateKey, 6, 2); + + + $lastAction = last($actionByDate); + $lastActionKey = array_key_last($actionByDate); + + + //if ($dateKey == '2021-04-27') { + // dd($actionByDate, $lastAction, $lastActionKey); + + //} + + if ($lastAction['value'] == $dateValue['value'] && Carbon::parse($lastAction['endDate'])->addDay()->toDateString() == $dateKey) { + $actionByDate[$lastActionKey]['endDate'] = $dateKey; + } else { + if ($typeKey == 'PRC') { + $actionByDate[] = [ + 'startDate' => $dateKey, + 'endDate' => $dateKey, + 'value' => $dateValue['value'], + 'currency' => $dateValue['currency'] + ]; + } else { + $actionByDate[] = [ + 'startDate' => $dateKey, + 'endDate' => $dateKey, + 'value' => $dateValue['value'] + ]; + } + + } + + } + + $roomRateNotificationGroup[$typeKey]['title'] = isset($this->actionTitleByKey[$typeKey]) ? $this->actionTitleByKey[$typeKey]['language_key'] : $typeKey; + + $roomRateNotificationGroup[$typeKey]['data'][$roomKey]['title'] = $roomDetail['name']; + $roomRateNotificationGroup[$typeKey]['data'][$roomKey]['titleRoomType'] = $roomDetail['property_room_type']['language_key']; + + if (empty($roomRateDetail)) { + $roomRateNotificationGroup[$typeKey]['data'][$roomKey]['data'][$roomRateKey]['title'] = null; + $roomRateNotificationGroup[$typeKey]['data'][$roomKey]['data'][$roomRateKey]['titleAccommodation'] = null; + } else { + $roomRateNotificationGroup[$typeKey]['data'][$roomKey]['data'][$roomRateKey]['title'] = $roomRateDetail['name']; + $roomRateNotificationGroup[$typeKey]['data'][$roomKey]['data'][$roomRateKey]['titleAccommodation'] = $roomRateDetail['property_room_rate_accommodation']['language_key']; + } + + $roomRateNotificationGroup[$typeKey]['data'][$roomKey]['data'][$roomRateKey]['actionByDate'] = $actionByDate; + + //$roomRateNotificationGroup[$typeKey][$roomKey][$roomRateKey] = $actionByDate; + + } + + } + + } + + + //inventoryActionMail + $mailParams = [ + 'locale' => $mailLanguage, + 'propertyName' => $propertyChannelMapping['data']['property']['name'], + 'channelContact' => $channelContact, + 'roomRateNotificationData' => $roomRateNotificationGroup, + ]; + + $this->mailer->onQueue('inventoryActionMail', new InventoryActionMail($mailParams)); + //inventoryActionMail + + + return true; + } + + public function roomRateAvailabilityBulkUpdateNotification($requestParams = []) + { + + //update_type, rate, min_stay, rate_stop_sell + + $typeKeyMapping = [ + 'rate' => 'PRC', + 'min_stay' => 'MNS', + 'rate_stop_sell' => 'STS', + ]; + + $typeKey = null; + if (isset($typeKeyMapping[$requestParams['update_type']])) { + $typeKey = $typeKeyMapping[$requestParams['update_type']]; + } + + if (is_null($typeKey)) { + return false; + } + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $requestParams['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel', 'property', 'channelContact'], + 'firstRow' => true + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + if ($propertyChannelMapping['status'] != 'success') { + return false; + } + + if (empty($propertyChannelMapping['data']['channel_contact'])) { + return false; + } + + $channelContact = collect($propertyChannelMapping['data']['channel_contact'])->where('status', 1)->pluck('email')->toArray(); + + if (empty($channelContact)) { + return true; + } + + //LANGUAGE + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + $availableLanguages = Collect($availableLanguages['data'])->keyBy('code')->all(); + + $mailLanguage = 'en'; + if (array_key_exists($propertyChannelMapping['data']['channel']['country_code'], $availableLanguages)) { + $mailLanguage = $propertyChannelMapping['data']['channel']['country_code']; + } + + + $propertyRoomRateChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $requestParams['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $requestParams['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation', + 'propertyRoomRateMapping.propertyRoom.propertyRoomType' + ] + ]; + + $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingService->select($propertyRoomRateChannelMappingCriteria); + $propertyRoomRateChannelMappingCollect = collect($propertyRoomRateChannelMapping['data']); + + $propertyRoomsParams = [ + 'property_id' => $requestParams['property_id'], + ]; + + $getPropertyRooms = $this->propertyRoomService->getPropertyRooms($propertyRoomsParams); + $propertyRoomsCollect = collect($getPropertyRooms['data']); + + + //Burada time lar hesaplancak, hepsinde aynı zaten mantığı + + $actionByDate = []; + + + $diffInDays = Carbon::parse($requestParams['start_date'])->diffInDays($requestParams['end_date']); + + $startDate = $requestParams['start_date']; + for ($i = 0; $i <= $diffInDays; $i++) { + + $dateKey = Carbon::parse($startDate)->addDays($i)->toDateString(); + + if (!in_array(Carbon::parse($dateKey)->shortDayName, $requestParams['include_days'])) { + continue; + } + + $lastAction = last($actionByDate); + $lastActionKey = array_key_last($actionByDate); + + if (Carbon::parse($lastAction['endDate'])->addDay()->toDateString() == $dateKey) { + $actionByDate[$lastActionKey]['endDate'] = $dateKey; + } else { + if ($typeKey == 'PRC') { + $actionByDate[] = [ + 'startDate' => $dateKey, + 'endDate' => $dateKey, + 'value' => $requestParams['value'], + 'currency' => $propertyChannelMapping['data']['currency_code'] + ]; + } else { + $actionByDate[] = [ + 'startDate' => $dateKey, + 'endDate' => $dateKey, + 'value' => $requestParams['value'] + ]; + } + + } + + } + + $roomRateNotificationGroup = []; + foreach ($requestParams['room_rates'] as $room) { + + foreach ($room['room_rate_mapping_id'] as $roomRateId) { + + //roomDetail + $roomDetail = $propertyRoomsCollect->where('id', $room['room_id'])->first(); + if (empty($roomDetail)) { + continue; + } + + //roomRateChannelMappingDetail + $roomRateDetail = []; + $roomRateChannelMappingDetail = $propertyRoomRateChannelMappingCollect->where('room_rate_mapping_id', $roomRateId)->first(); + if (empty($roomRateChannelMappingDetail) && $roomRateId != 0) { + continue; + } + + if ($roomRateId != 0) { + $roomRateDetail = $roomRateChannelMappingDetail['property_room_rate_mapping']['property_room_rate']; + } + + + $roomRateNotificationGroup[$typeKey]['title'] = isset($this->actionTitleByKey[$typeKey]) ? $this->actionTitleByKey[$typeKey]['language_key'] : $typeKey; + + $roomRateNotificationGroup[$typeKey]['data'][$room['room_id']]['title'] = $roomDetail['name']; + $roomRateNotificationGroup[$typeKey]['data'][$room['room_id']]['titleRoomType'] = $roomDetail['property_room_type']['language_key']; + + if (empty($roomRateDetail)) { + $roomRateNotificationGroup[$typeKey]['data'][$room['room_id']]['data'][$roomRateId]['title'] = null; + $roomRateNotificationGroup[$typeKey]['data'][$room['room_id']]['data'][$roomRateId]['titleAccommodation'] = null; + } else { + $roomRateNotificationGroup[$typeKey]['data'][$room['room_id']]['data'][$roomRateId]['title'] = $roomRateDetail['name']; + $roomRateNotificationGroup[$typeKey]['data'][$room['room_id']]['data'][$roomRateId]['titleAccommodation'] = $roomRateDetail['property_room_rate_accommodation']['language_key']; + } + + $roomRateNotificationGroup[$typeKey]['data'][$room['room_id']]['data'][$roomRateId]['actionByDate'] = $actionByDate; + + } + + } + + //inventoryActionMail + $mailParams = [ + 'locale' => $mailLanguage, + 'propertyName' => $propertyChannelMapping['data']['property']['name'], + 'channelContact' => $channelContact, + 'roomRateNotificationData' => $roomRateNotificationGroup, + ]; + + $this->mailer->onQueue('inventoryActionMail', new InventoryActionMail($mailParams)); + //inventoryActionMail + + + return true; + } + + public function setStatusPropertyRoomRateMapping(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $params['user_id'] = $this->request->auth->id; + + + if (isset($params['status'])) { + $requestParams['status'] = fillOnUndefined($params, "status", 0); + } + $propertyRoomRateMapping = $this->propertyRoomRateMappingService->setStatusPropertyRoomRateMapping($params); + if ($propertyRoomRateMapping['status'] != 'success') { + throw new ApiErrorException($propertyRoomRateMapping['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateMapping['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function postInventoryLink(Request $request) + { + + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $params = $this->request->params; + + $propertyChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel', 'property', 'channelContact'], + 'firstRow' => true + ]; + + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria); + if ($propertyChannelMapping['status'] != 'success') { + throw new ApiErrorException($propertyChannelMapping['message']); + } + + if (empty($propertyChannelMapping['data']['channel_contact'])) { + throw new ApiErrorException("There is not any email address founded for this channel"); + } + + $channelContact = collect($propertyChannelMapping['data']['channel_contact'])->where('status', 1)->pluck('email')->toArray(); + + if (empty($channelContact)) { + return true; + } + + + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + $availableLanguages = Collect($availableLanguages['data'])->keyBy('code')->all(); + + $mailLanguage = 'en'; + if (array_key_exists($propertyChannelMapping['data']['channel']['country_code'], $availableLanguages)) { + $mailLanguage = $propertyChannelMapping['data']['channel']['country_code']; + } + + + $mailParams = [ + 'locale' => $mailLanguage, + 'propertyName' => $propertyChannelMapping['data']['property']['name'], + 'channelContact' => $channelContact, + 'link' => Config::get('app.url') . '/app/v1/channel-pdf-inventory/' . $propertyChannelMapping['data']['token'] + + ]; + + $this->mailer->onQueue('inventoryPdfLinkMail', new InventoryPdfLinkMail($mailParams)); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $mailParams]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } +} + + diff --git a/app/Http/Controllers/V1/PropertyRoomRateMappingSetupController.php b/app/Http/Controllers/V1/PropertyRoomRateMappingSetupController.php new file mode 100644 index 0000000..d1cc953 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomRateMappingSetupController.php @@ -0,0 +1,124 @@ +request = $request; + $this->propertyRoomRateMappingSetupService = $propertyRoomRateMappingSetupService; + + } + + + + public function getPropertyRoomRateMappingSetup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id'), + 'user_id' => $this->request->auth->id, + ]; + + $propertyRoomRateMappingSetup = $this->propertyRoomRateMappingSetupService->getPropertyRoomRateMappingSetup($requestParams); + if($propertyRoomRateMappingSetup['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateMappingSetup['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateMappingSetup['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomRateMappingSetup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'room_id' => fillOnUndefined($params, 'room_id'), + 'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id'), + 'setup' => fillOnUndefined($params, 'setup'), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomRateMappingSetup = $this->propertyRoomRateMappingSetupService->addPropertyRoomRateMappingSetup($requestParams); + if($propertyRoomRateMappingSetup['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRateMappingSetup['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRateMappingSetup['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + + + + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyRoomRatePriceController.php b/app/Http/Controllers/V1/PropertyRoomRatePriceController.php new file mode 100644 index 0000000..e7a249e --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomRatePriceController.php @@ -0,0 +1,217 @@ +request = $request; + $this->propertyRoomRatePriceService = $propertyRoomRatePriceService; + + } + + + + public function getPropertyRoomRatePrice(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $propertyRoomRatePrice = $this->propertyRoomRatePriceService->getPropertyRoomRatePrices($requestParams); + if($propertyRoomRatePrice['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRatePrice['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRatePrice['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function addPropertyRoomRatePrice(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_room_id' => fillOnUndefined($params, 'property_room_id'), + 'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id'), + 'offer_id' => fillOnUndefined($params, 'offer_id'), + 'channel_id' => fillOnUndefined($params, 'channel_id'), + 'min_stay' => fillOnUndefined($params, 'min_stay'), + 'max_stay' => fillOnUndefined($params, 'max_stay'), + 'stop_sell' => fillOnUndefined($params, 'stop_sell'), + 'booking_on_request' => fillOnUndefined($params, 'booking_on_request'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + 'amount' => fillOnUndefined($params, 'amount'), + 'currency' => fillOnUndefined($params, 'currency'), + 'user_id' => $this->request->auth->id, + ]; + + + $propertyRoomRatePrice = $this->propertyRoomRatePriceService->addPropertyRoomRatePrice($requestParams); + if($propertyRoomRatePrice['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRatePrice['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRatePrice['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyRoomRatePrice(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'id' => fillOnUndefined($params, 'id'), + 'property_id' => fillOnUndefined($params, 'property_id'), + 'name' => fillOnUndefined($params, 'name'), + 'description' => fillOnUndefined($params, 'description'), + 'min_stay' => fillOnUndefined($params, 'min_stay'), + + 'user_id' => $this->request->auth->id, + ]; + + + if(isset($params['status'])){ + $requestParams['status'] = fillOnUndefined($params, "status", 0); + } + $propertyRoomRatePrice = $this->propertyRoomRatePriceService->updatePropertyRoomRatePrice($requestParams); + if($propertyRoomRatePrice['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRatePrice['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRatePrice['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function deletePropertyRoomRatePrice(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + + 'id' => fillOnUndefined($params, 'id'), + 'user_id' => $this->request->auth->id, + + ]; + + $propertyRoomRatePrice = $this->propertyRoomRatePriceService->deletePropertyRoomRatePrice($requestParams); + if($propertyRoomRatePrice['status'] != 'success'){ + throw new ApiErrorException($propertyRoomRatePrice['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomRatePrice['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + + + +} diff --git a/app/Http/Controllers/V1/PropertyRoomSizeTypeController.php b/app/Http/Controllers/V1/PropertyRoomSizeTypeController.php new file mode 100644 index 0000000..61a7f0a --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomSizeTypeController.php @@ -0,0 +1,60 @@ +propertyRoomSizeTypeService = $propertyRoomSizeTypeService; + + } + + public function getPropertyRoomSizeTypes(){ + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $propertyRoomSizeTypeCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + + $propertyRoomSizeTypes = $this->propertyRoomSizeTypeService->select($propertyRoomSizeTypeCriteria, ['id', 'name', 'code', 'language_key']); + + + if($propertyRoomSizeTypes['status'] != 'success'){ + throw new Exception($propertyRoomSizeTypes['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomSizeTypes['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + +} diff --git a/app/Http/Controllers/V1/PropertyRoomTypeController.php b/app/Http/Controllers/V1/PropertyRoomTypeController.php new file mode 100644 index 0000000..bdd1669 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomTypeController.php @@ -0,0 +1,112 @@ +request = $request; + $this->propertyRoomTypeService = $propertyRoomTypeService; + + } + + + + public function getPropertyRoomTypes(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + ]; + + $propertyRoomType = $this->propertyRoomTypeService->getPropertyRoomTypes($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyMappedRoomTypes(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + 'property_id' => fillOnUndefined($params, 'property_id'), + ]; + + $propertyRoomType = $this->propertyRoomTypeService->getPropertyMappedRoomTypes($requestParams); + if($propertyRoomType['status'] != 'success'){ + throw new ApiErrorException($propertyRoomType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyRoomViewTypeController.php b/app/Http/Controllers/V1/PropertyRoomViewTypeController.php new file mode 100644 index 0000000..e8950cb --- /dev/null +++ b/app/Http/Controllers/V1/PropertyRoomViewTypeController.php @@ -0,0 +1,66 @@ +propertyRoomViewTypeService = $propertyRoomViewTypeService; + } + + + + public function getPropertyRoomViewTypeList() + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $propertyRoomViewTypeCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + "orderBy" => [ + ["field" => "name", "value" => "ASC"] + ] + ]; + + $propertyRoomViewTypeList = $this->propertyRoomViewTypeService->getPropertyRoomViewTypes($propertyRoomViewTypeCriteria); + + if($propertyRoomViewTypeList['status'] != 'success'){ + throw new ApiErrorException($propertyRoomViewTypeList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyRoomViewTypeList['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} diff --git a/app/Http/Controllers/V1/PropertyTypeController.php b/app/Http/Controllers/V1/PropertyTypeController.php new file mode 100644 index 0000000..43635fe --- /dev/null +++ b/app/Http/Controllers/V1/PropertyTypeController.php @@ -0,0 +1,72 @@ +request = $request; + $this->propertyTypeService = $propertyTypeService; + + } + + + + public function getPropertyTypes(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'locale' => fillOnUndefined($params, 'locale'), + ]; + + $propertyType = $this->propertyTypeService->getPropertyTypes($requestParams); + if($propertyType['status'] != 'success'){ + throw new ApiErrorException($propertyType['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyType['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/V1/PropertyWebComponentController.php b/app/Http/Controllers/V1/PropertyWebComponentController.php new file mode 100644 index 0000000..aad4167 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyWebComponentController.php @@ -0,0 +1,250 @@ +params = Input::all(); + $this->params = $this->params['params']; + $this->propertyWebService = $propertyWebService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->languageService = $languageService; + } + + + public function getPropertyWebComponent(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + //TODO: Validator + + $propertyWebComponentCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $propertyWebComponent = $this->propertyWebService->selectPropertyWebComponent($propertyWebComponentCriteria); + $propertyWebComponent = $propertyWebComponent['status'] == 'success' && !empty($propertyWebComponent['data']) ? $propertyWebComponent['data'] : []; + + + $propertyWebComponentMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['property_id']], + ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $propertyWebComponentMapping = $this->propertyWebService->selectPropertyWebComponentMapping($propertyWebComponentMappingCriteria); + $propertyWebComponentMapping = $propertyWebComponentMapping['status'] == 'success' && !empty($propertyWebComponentMapping['data']) ? $propertyWebComponentMapping['data'] : []; + $propertyChannelAddonCollect = collect($propertyWebComponentMapping); + + $propertyWebComponentList = []; + foreach ($propertyWebComponent as $webComponentKey => $webComponent) { + + $webComponentMapping = $propertyChannelAddonCollect->where('component_id', $webComponent['id'])->first(); + + $webComponentDetail = null; + $isSelected = $webComponentMapping ? true : false; + + if ($webComponentMapping) { + $webComponentDetail = [ + 'component_id' => $webComponentMapping['component_id'], + 'parameterArray' => $webComponentMapping['parameterArray'], + 'language' => $webComponentMapping['language'], + 'languageArray' => $webComponentMapping['languageArray'], + ]; + } + + + $propertyWebComponentList[] = [ + //'code' => $webComponent['code'], + 'id' => $webComponent['id'], + 'name' => $webComponent['name'], + 'component' => $webComponent['component'], + 'icon' => $webComponent['icon'], + 'iconUrl' => $webComponent['iconUrl'], + 'parameterArray' => $webComponent['parameterArray'], + 'is_selected' => $isSelected, + 'componentDetail' => $webComponentDetail + ]; + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWebComponentList]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function syncPropertyWebComponent(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + + $propertyWebComponentCriteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($this->params, 'component_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $propertyWebComponent = $this->propertyWebService->selectPropertyWebComponent($propertyWebComponentCriteria); + $propertyWebComponent = $propertyWebComponent['status'] == 'success' && !empty($propertyWebComponent['data']) ? $propertyWebComponent['data'] : []; + + if(empty($propertyWebComponent)){ + throw new ApiErrorException(lang('Component data not found')); + } + + + //Parameter Check + if (!empty($this->params['componentDetail'])) { + + $parameterCheck = array_diff(array_keys($propertyWebComponent['parameterArray']),array_keys($this->params['componentDetail']['parameter'])); + if(!empty($parameterCheck)) { + throw new ApiErrorException(lang('Missing or incorrect parameter')); + } + } + + + $propertyWebComponentMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($this->params, 'property_id')], + ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($this->params, 'channel_id')], + ['field' => 'component_id', 'condition' => '=', 'value' => fillOnUndefined($this->params, 'component_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $propertyWebComponentMapping = $this->propertyWebService->selectPropertyWebComponentMapping($propertyWebComponentMappingCriteria); + $propertyWebComponentMapping = $propertyWebComponentMapping['status'] == 'success' && !empty($propertyWebComponentMapping['data']) ? $propertyWebComponentMapping['data'] : []; + + + //Parameter Check + + + DB::beginTransaction(); + + if ($propertyWebComponentMapping) { + + + //Remove + if (empty($this->params['componentDetail'])) { + + $syncPropertyWebComponent = $this->propertyWebService->deletePropertyWebComponentMapping($propertyWebComponentMapping['id']) ; + if($syncPropertyWebComponent['status'] != 'success'){ + throw new Exception('api-unknown_error'); + } + + } else { + + //Update + $updateParam = [ + 'property_id' => fillOnUndefined($this->params, 'property_id'), + 'channel_id' => fillOnUndefined($this->params, 'channel_id'), + 'component_id' => fillOnUndefined($this->params, 'component_id'), + 'parameter' => fillOnUndefined($this->params['componentDetail'], 'parameter') ? json_encode($this->params['componentDetail']['parameter']) : null, + 'language' => fillOnUndefined($this->params['componentDetail'], 'language') ? json_encode($this->params['componentDetail']['language']) : null, + 'updated_by' => $request->auth->id, + ]; + + $syncPropertyWebComponent = $this->propertyWebService->updatePropertyWebComponentMapping($propertyWebComponentMapping['id'], $updateParam); + + if ($syncPropertyWebComponent['status'] != 'success') { + throw new ApiErrorException($syncPropertyWebComponent['message']); + } + + } + + } else { + + $createParam = [ + 'property_id' => fillOnUndefined($this->params, 'property_id'), + 'channel_id' => fillOnUndefined($this->params, 'channel_id'), + 'component_id' => fillOnUndefined($this->params, 'component_id'), + 'parameter' => fillOnUndefined($this->params['componentDetail'], 'parameter') ? json_encode($this->params['componentDetail']['parameter']) : null, + 'language' => fillOnUndefined($this->params['componentDetail'], 'language') ? json_encode($this->params['componentDetail']['language']) : null, + 'status' => 1, + 'created_by' => $request->auth->id, + ]; + + $syncPropertyWebComponent = $this->propertyWebService->createPropertyWebComponentMapping($createParam); + + if ($syncPropertyWebComponent['status'] != 'success') { + throw new ApiErrorException($syncPropertyWebComponent['message']); + } + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} + diff --git a/app/Http/Controllers/V1/PropertyWebContentController.php b/app/Http/Controllers/V1/PropertyWebContentController.php new file mode 100644 index 0000000..1df4a1b --- /dev/null +++ b/app/Http/Controllers/V1/PropertyWebContentController.php @@ -0,0 +1,296 @@ +request = $request; + $this->propertyWebContentService = $propertyWebContentService; + } + + public function getPropertyWebContent(Request $request, $id = null) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $columns = ['id', 'property_id', 'content_category_id', 'title', 'slug', 'image', 'language_code', 'status', 'created_at']; + $propertyWebContentCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']] + ], + 'with' => ['ContentCategory'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ] + ]; + + + $availableFilterArray = ['title', 'content_category_id', 'language_code']; + if (!empty($params['filter'])) { + foreach ($params['filter'] as $filterKey => $filterValue) { + if (in_array($filterKey, $availableFilterArray)) { + if (in_array($filterKey, ['content_category_id', 'language_code', 'status'])) { + $propertyWebContentCriteria['criteria'][] = ['field' => $filterKey, 'condition' => '=', 'value' => $filterValue]; + } + + if (in_array($filterKey, ['title'])) { + $propertyWebContentCriteria['criteria'][] = ['field' => $filterKey, 'condition' => 'LIKE', 'value' => '%' . $filterValue . '%']; + } + + } + } + } + + if (!empty($id)) { + $propertyWebContentCriteria['criteria'][] = ['field' => 'id', 'condition' => '=', 'value' => $id]; + $propertyWebContentCriteria['firstRow'] = true; + $columns[] = 'content'; + } + + + $propertyWebContent = $this->propertyWebContentService->select($propertyWebContentCriteria, $columns); + + if ($propertyWebContent['status'] != 'success') { + throw new Exception($propertyWebContent['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWebContent['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function getPropertyWebContentCategory(Request $request, $id = null) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $columns = ['id', 'name', 'language_key']; + $propertyWebContentCategoryCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ] + ]; + + $propertyWebContentCategory = $this->propertyWebContentService->selectWebContentCategory($propertyWebContentCategoryCriteria, $columns); + if ($propertyWebContentCategory['status'] != 'success') { + throw new Exception($propertyWebContentCategory['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWebContentCategory['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function createPropertyWebContent(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'content_category_id' => fillOnUndefined($params, 'content_category_id'), + 'title' => fillOnUndefined($params, 'title'), + 'language_code' => fillOnUndefined($params, 'language_code'), + 'image' => fillOnUndefined($params, 'image'), + 'content' => fillOnUndefined($params, 'content'), + 'user_id' => $this->request->credentials->user_id + ]; + + $propertyContent = $this->propertyWebContentService->create($requestParams); + + if ($propertyContent['status'] != 'success') { + throw new ApiErrorException($propertyContent['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyContent['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyWebContent(Request $request, $id) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'content_category_id' => fillOnUndefined($params, 'content_category_id'), + 'title' => fillOnUndefined($params, 'title'), + 'language_code' => fillOnUndefined($params, 'language_code'), + 'image' => fillOnUndefined($params, 'image'), + 'content' => fillOnUndefined($params, 'content'), + 'updated_by' => $this->request->credentials->user_id + ]; + + $propertyContent = $this->propertyWebContentService->update($id, $requestParams); + + if ($propertyContent['status'] != 'success') { + throw new ApiErrorException($propertyContent['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyContent['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function deletePropertyWebContent(Request $request, $id) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'id' => $id, + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + + $propertyContent = $this->propertyWebContentService->delete($id, $requestParams); + + if ($propertyContent['status'] != 'success') { + throw new ApiErrorException($propertyContent['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyContent['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function uploadPropertyWebContentImage(Request $request) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + try { + if (!$request->hasFile('image')) { + throw new ApiErrorException(lang('Photos not found')); + } + + $param = [ + "property_id" => $request->input('property_id'), + "photo" => $request->file('image'), + ]; + + $response = $this->propertyWebContentService->uploadPhoto($param); + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + +} diff --git a/app/Http/Controllers/V1/PropertyWebController.php b/app/Http/Controllers/V1/PropertyWebController.php new file mode 100644 index 0000000..d679070 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyWebController.php @@ -0,0 +1,2418 @@ +request = $request; + $this->propertyWebService = $propertyWebService; + $this->languageService = $languageService; + $this->languageBaseService = $languageBaseService; + $this->propertyWebMenuService = $propertyWebMenuService; + $this->propertyWebMenuMappingService = $propertyWebMenuMappingService; + $this->propertyWebSetupService = $propertyWebSetupService; + $this->propertyWebLanguageMappingService = $propertyWebLanguageMappingService; + $this->propertyWebColorMappingService = $propertyWebColorMappingService; + $this->propertyBrandService = $propertyBrandService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyGroupMappingService = $propertyGroupMappingService; + $this->findCountryCodeService = $findCountryCodeService; + $this->propertyWebLogService = $propertyWebLogService; + $this->propertyWebMetaService = $propertyWebMetaService; + $this->bookingService = $bookingService; + $this->propertyWebAboutUsService = $propertyWebAboutUsService; + $this->propertyPlaceService = $propertyPlaceService; + $this->propertyAdditionalInfoService = $propertyAdditionalInfoService; + $this->propertyService = $propertyService; + $this->propertyRoomService = $propertyRoomService; + $this->propertyWebContentService = $propertyWebContentService; + $this->pdf = $pdf; + } + + + public function listPropertyWeb(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + $propertyRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ], + "orderBy" => [ + ["field" => "id", "value" => "DESC"] + ], + "with" => ['propertyWebTemplate', 'propertyWebLanguage'], + ]; + $propertyWebs = $this->propertyWebService->select($propertyRequest, ['id', 'domain', 'default_language', 'template_id', 'token', 'status', 'is_published', 'is_dns_checked']); + + if ($propertyWebs['status'] != 'success') { + throw new ApiErrorException($propertyWebs['message']); + } + + $web = []; + foreach ($propertyWebs['data'] as $key => $propertyWeb) { + + //Cache + $dashboardCountablePlaceCacheKey = md5('dashboardCountablePlace-' . $params['property_id']); + if (Cache::has($dashboardCountablePlaceCacheKey)) { + $counts = Cache::get($dashboardCountablePlaceCacheKey); + } else { + $counts = $this->dashboardCountablePlace($params, $propertyWeb['id']); + } + + + if ($counts['status'] === false) { + throw new ApiErrorException($counts['message']); + } + + $webData = [ + 'live_url' => Config::get('app.mainHostAddress') . '/live/' . $propertyWeb['token'] . '/' . $propertyWeb['default_language'], + 'preview_url' => Config::get('app.mainHostAddress') . '/preview/' . $propertyWeb['token'] . '/' . $propertyWeb['default_language'], + 'log' => $counts['data'] + ]; + $web[$key] = array_merge($propertyWeb, $webData); + } + + $web = reset($web); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $web]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPropertyWeb(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + + $propertyWebRequest = [ + 'id' => fillOnUndefined($params, 'property_web_id'), + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + $propertyWeb = $this->propertyWebService->getPropertyWeb($propertyWebRequest); + if ($propertyWeb['status'] != 'success') { + throw new ApiErrorException($propertyWeb['message']); + } + + $propertyWeb = $propertyWeb['data']; + $webData = [ + 'live_url' => Config::get('app.mainHostAddress') . '/live/' . $propertyWeb['token'] . '/' . $propertyWeb['default_language'], + 'preview_url' => Config::get('app.mainHostAddress') . '/preview/' . $propertyWeb['token'] . '/' . $propertyWeb['default_language'], + ]; + $propertyWeb = array_merge($propertyWeb, $webData); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWeb]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function createPropertyWeb(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = $params; + $requestParams['user_id'] = $this->request->auth->id; + + $storePropertyWeb = $this->propertyWebService->create($requestParams); + if ($storePropertyWeb['status'] != 'success') { + throw new ApiErrorException($storePropertyWeb['message']); + } + + $propertyWebRequest = [ + 'id' => $storePropertyWeb['data']['id'], + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $this->request->auth->id + ]; + + + $storePropertyWebPhotos = $this->propertyWebService->insertAllPropertyWebPhotosToMapping($propertyWebRequest); + if ($storePropertyWebPhotos['status'] != 'success') { + throw new ApiErrorException($storePropertyWebPhotos['message']); + } + + + $propertyWeb = $this->propertyWebService->getPropertyWeb($propertyWebRequest); + if ($propertyWeb['status'] != 'success') { + throw new ApiErrorException($propertyWeb['message']); + } + + $propertyWeb['data']['log'] = [ + 'active_user_count' => 0, + 'booking_count' => 0, + 'country_count' => [], + 'mobile_count' => 0, + 'web_count' => 0 + + ]; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWeb['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function updatePropertyWeb(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + $requestParams = $params; + $requestParams['user_id'] = $this->request->auth->id; + $propertyWebId = $params['property_web_id']; + + $storePropertyWeb = $this->propertyWebService->update($propertyWebId, $requestParams); + if ($storePropertyWeb['status'] != 'success') { + throw new ApiErrorException($storePropertyWeb['message']); + } + + $propertyWebRequest = [ + 'id' => $propertyWebId, + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + $propertyWeb = $this->propertyWebService->getPropertyWeb($propertyWebRequest); + if ($propertyWeb['status'] != 'success') { + throw new ApiErrorException($propertyWeb['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWeb['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function checkDomain(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + $params['mode'] = fillOnUndefined($params, 'mode', null); + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'domain', 'condition' => '=', 'value' => fillOnUndefined($params, 'domain')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true, + "with" => [ + 'property', 'property.propertyAwardsCertificates.awardsCertificateCategory', 'propertyWebTemplate', 'propertyBookingEngine', + 'propertyWebMenuMenuMapping', 'propertyWebLanguageMapping', + 'propertyGroupMapping.propertyGroup', 'propertyWebAbout', 'propertyWebPopup', 'propertyWebWeather', + 'propertyWebComponent', 'propertyWebRoomMapping.roomDetail', 'propertyWebColorMapping' + ] + ]; + $propertyWeb = $this->propertyWebService->select($propertyRequest, ['id', 'property_id', 'domain', 'default_language', 'template_id', 'token', 'is_ssl_active', 'weather_active', 'cover_video_id']); + $propertyGroupId = NULL; + $propertyBookingEngineGroupId = NULL; + if (isset($propertyWeb['data']['property_group_mapping'])) { + foreach ($propertyWeb['data']['property_group_mapping'] as $propertyGroupMapping) { + if (!empty($propertyGroupMapping['property_group'] && $propertyGroupMapping['property_group']['type'] === "MYW")) { + $propertyGroupId = $propertyGroupMapping['property_group']['id']; + } + if (!empty($propertyGroupMapping['property_group'] && $propertyGroupMapping['property_group']['type'] === "MYW")) { + $propertyBookingEngineGroupId = $propertyGroupMapping['property_group']['id']; + } + } + } + + if (!isset($propertyWeb['data']['property'])) { + throw new ApiErrorException('Property is not active!', 101); + //101: Passive Property + } + + if ($propertyWeb['data']['property']['status'] != 1) { + throw new ApiErrorException('Property is not active!', 101); + //101: Passive Property + } + + + $propertyGroupMappingRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_group_id', 'condition' => '=', 'value' => $propertyGroupId], + ['field' => 'property_id', 'condition' => '!=', 'value' => fillOnUndefined($propertyWeb['data'], 'property_id')] + ], + 'orderBy' => [ + ["field" => "order_number", "value" => "ASC"] + ], + "with" => ['property.propertyWeb'] + ]; + + $propertyGroupWebSiteResult = $this->propertyGroupMappingService->getPropertyGroupMapping($propertyGroupMappingRequest); + if ($propertyGroupWebSiteResult['status'] != 'success') { + throw new ApiErrorException($propertyGroupWebSiteResult['message']); + } + + $propertyGroupWebSites = $propertyGroupWebSiteResult['data']; + + $groupWebSites = []; + foreach ($propertyGroupWebSites as $propertyGroupWebSite) { + if ($propertyGroupWebSite['property'] && $propertyGroupWebSite['property']['property_web']) { + $propertyGroupWebSite['property']['property_web']['name'] = $propertyGroupWebSite['property']['name']; + $groupWebSites[] = $propertyGroupWebSite['property']['property_web']; + } + } + + $groupWebSites = collect($groupWebSites)/*->where('is_published', 1)*/ ->values()->toArray(); + + + if (!empty($propertyWeb['data'])) { + $propertyWeb['data']['property_group_web_sites'] = $groupWebSites; + unset($propertyWeb['data']['property_group_mapping']); + } + + $propertyBookingGroupMappingRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_group_id', 'condition' => '=', 'value' => $propertyBookingEngineGroupId], + ], + 'orderBy' => [ + ["field" => "order_number", "value" => "ASC"] + ], + "with" => [ + 'property.propertyBookingEngineToken', + 'property.propertyWeb.propertyWebPhotoMapping.propertyPhoto' + ] + ]; + + $propertyGroupBookingResult = $this->propertyGroupMappingService->getPropertyGroupMapping($propertyBookingGroupMappingRequest); + if ($propertyGroupBookingResult['status'] != 'success') { + throw new ApiErrorException($propertyGroupBookingResult['message']); + } + + $propertyGroupBookings = $propertyGroupBookingResult['data']; + + + $bookingGroupProperties = []; + foreach ($propertyGroupBookings as $key => $propertyGroupBooking) { + if ($propertyGroupBooking['property']) { + $bookingGroupProperties[$key]['property_id'] = $propertyGroupBooking['property']['id']; + $bookingGroupProperties[$key]['name'] = $propertyGroupBooking['property']['name']; + $bookingGroupProperties[$key]['name'] = $propertyGroupBooking['property']['name']; + if ($propertyGroupBooking['property']['property_booking_engine_token']) { + $bookingGroupProperties[$key]['token'] = $propertyGroupBooking['property']['property_booking_engine_token']['token']; + } else { + $bookingGroupProperties[$key]['token'] = null; + } + + $bookingGroupProperties[$key]['web'] = null; + if (!empty($propertyGroupBooking['property']['property_web'])) { + if ($propertyGroupBooking['property']['property_web']['status'] && $propertyGroupBooking['property']['property_web']['is_published']) { + $bookingGroupProperties[$key]['web'] = $propertyGroupBooking['property']['property_web']['webProtocolUrl']; + } + } + + $propertyWebPhotoMapping = collect($propertyGroupBooking['property']['property_web']['property_web_photo_mapping']) + ->where('status', 1) + ->where('is_cover', 1) + ->toArray(); + + $bookingGroupProperties[$key]['coverPhoto'] = []; + foreach ($propertyWebPhotoMapping as $propertyWebPhoto) { + $bookingGroupProperties[$key]['coverPhoto'][] = $propertyWebPhoto['property_photo']['photoUrl']; + } + } + } + + if (!empty($bookingGroupProperties)) { + $propertyWeb['data']['property_booking_group'] = collect($bookingGroupProperties)->where('token', '!=', NULL)->toArray(); + } + + if (!empty($propertyWeb['data']['property_booking_engine'])) { + $propertyChannelMappingRequestData = [ + 'property_id' => $propertyWeb['data']['property_booking_engine']['property_id'], + 'channel_id' => $propertyWeb['data']['property_booking_engine']['channel_id'] + ]; + $propertyChannelMappingData = $this->propertyChannelMappingService->checkPropertyChannelMapping($propertyChannelMappingRequestData); + if ($propertyChannelMappingData['data'] === "") { + $propertyWeb['data']['property_booking_engine']['token'] = NULL; + } + } + + $propertyWebPreviewOrLiveMenus = []; + if (isset($propertyWeb['data']['property_web_menu_menu_mapping'])) { + $propertyWebPreviewOrLiveMenus = collect($propertyWeb['data']['property_web_menu_menu_mapping']) + ->where('status', 1)->toArray(); // live + } + + if ($propertyWeb['status'] != 'success' || !$propertyWeb['data']) { + throw new ApiErrorException('Domain not found'); + } + + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + + $propertyWebLanguageMappings = $propertyWeb['data']['property_web_language_mapping'] ? $propertyWeb['data']['property_web_language_mapping'] : []; + $propertyWebLanguageMappings = collect($propertyWebLanguageMappings)->keyBy('language_code')->toArray(); + + if ($propertyWebLanguageMappings) { + foreach ($availableLanguages['data'] as $language) { + if (array_key_exists($language['code'], $propertyWebLanguageMappings)) { + $propertyWeb['data']['available_language_codes'][$language['code']] = $language; + } + } + } else { + foreach ($availableLanguages['data'] as $language) { + $propertyWeb['data']['available_language_codes'][$language['code']] = $language; + } + } + + $webMenu = []; + if (empty($propertyWebPreviewOrLiveMenus)) { + $myWebMenus = $this->propertyWebMenuService->getPropertyWebMenu(); + if ($myWebMenus['status'] != 'success') { + throw new ApiErrorException($myWebMenus['message']); + } + $propertyWeb['data']['web_menu']['LVL1'] = collect($myWebMenus['data']['property_web_menus'])->where('type', '=', '1')->keyBy('alias')->toArray(); + $propertyWeb['data']['web_menu']['LEGAL'] = collect($myWebMenus['data']['property_web_menus'])->where('type', '=', '3')->keyBy('alias')->toArray(); + unset($propertyWeb['data']['property_web_menu_menu_mapping']); + } else { + + $webMenuTypes = collect($propertyWeb['data']['property_web_menu_menu_mapping']) + ->where('status', 1) + ->groupBy('menu_code')->toArray(); + + $allLevelData = []; + $allTopData = []; + foreach ($webMenuTypes as $menuTypeKey => $webMenuType) { + if ($menuTypeKey === self::MENU_CODE_LVL1) { + $allLevelData = $this->getTypeMenus($webMenuType, $menuTypeKey); + + } elseif ($menuTypeKey === self::MENU_CODE_TOP) { + $allTopData = $this->getTypeMenus($webMenuType, $menuTypeKey); + + } + } + + $staticMenu = $this->propertyWebMenuService->getPropertyWebMenu(); + if ($staticMenu['status'] != 'success') { + throw new ApiErrorException($staticMenu['message']); + } + + unset($propertyWeb['data']['property_web_menu_menu_mapping']); + $propertyWeb['data']['web_menu'] = array_merge($allTopData, $allLevelData); + $propertyWeb['data']['web_menu']['LEGAL'] = collect($staticMenu['data']['property_web_menus'])->where('type', '=', '3')->keyBy('alias')->toArray(); + + } + + if ($propertyWeb['data']['property']['country'] == 'TR') { + unset($propertyWeb['data']['web_menu']['LEGAL']['Protection']); + } else { + unset($propertyWeb['data']['web_menu']['LEGAL']['Kvkk']); + } + + + //property_web_room_mapping + $propertyWebRoomMapping = []; + $propertyWebRoomMappingTemp = $propertyWeb['data']['property_web_room_mapping']; + unset($propertyWeb['data']['property_web_room_mapping']); + foreach ($propertyWebRoomMappingTemp as $propertyWebRoom) { + $propertyWebRoomMapping[] = [ + 'id' => $propertyWebRoom['room_detail']['id'], + 'name' => $propertyWebRoom['room_detail']['name'], + 'slug' => Str::slug($propertyWebRoom['room_detail']['name'], $separator = '-', $language = 'en') . '-' . $propertyWebRoom['room_detail']['id'] + ]; + } + $propertyWeb['data']['property_web_room_mapping'] = $propertyWebRoomMapping; + //property_web_room_mapping + + + $agentIp = !empty($params['agentInfo']) && $params['agentInfo']['ip'] ? $params['agentInfo']['ip'] : NULL; + + if ($agentIp !== NULL) { + // Find Country Code with IP + $ipResponse = $this->findCountryCodeService->findCountryWithIpAddress($agentIp); + if ($ipResponse['status'] !== 'success') { + //throw new ApiErrorException('IP Not Found'); + } + + // Create or Update Web Log + if (!empty($params['agentInfo']) && !empty($ipResponse['data']) && !empty($ipResponse['data']['code']) && $ipResponse['status']) { + + $agentParams = [ + 'web_id' => fillOnUndefined($propertyWeb['data'], 'id'), + 'ip' => $agentIp, + 'country_code' => $ipResponse['data'] ? $ipResponse['data']['code'] : null, + 'isRobot' => $params['agentInfo']['isRobot'] === true ? $params['agentInfo']['isRobot'] : false, + 'isDesktop' => $params['agentInfo']['isDesktop'] === true ? $params['agentInfo']['isDesktop'] : false, + 'isPhone' => $params['agentInfo']['isPhone'] === true ? $params['agentInfo']['isPhone'] : false + ]; + + + if ($agentParams['isRobot'] === false) { + $webLogResponse = $this->propertyWebLogService->createPropertyWebLog($agentParams); + if ($webLogResponse['status'] != 'success') { + throw new ApiErrorException('Property Web Log Not Created'); + } + } + + } + } + + //PropertyWebPopUp + $propertyWebPopUp = null; + if ($propertyWeb['data']['property_web_popup']) { + $propertyWebPopUp = collect($propertyWeb['data']['property_web_popup']) + ->where('start_date', '<=', Carbon::now()->toDateString()) + ->where('end_date', '>=', Carbon::now()->toDateString()) + ->toArray(); + + $propertyWebPopUp = array_values($propertyWebPopUp); + + } + + $propertyWeb['data']['property_web_popup'] = $propertyWebPopUp; + //PropertyWebPopUp + + + //PropertyWebContent + $propertyWebContent = null; + $propertyWebContentCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyWeb['data']['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + //'with' => ['ContentCategory'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ] + ]; + + $propertyWebContentCriteria['skip'] = 0; + $propertyWebContentCriteria['take'] = 100; + $propertyWebContent = $this->propertyWebContentService->select($propertyWebContentCriteria, ['id', 'title', 'property_id', 'content_category_id', 'slug', 'language_code', 'image']); + $propertyWebContent = ($propertyWebContent['status'] == 'success' && !empty($propertyWebContent['data'])) ? array_values($propertyWebContent['data']) : []; + + $propertyWebContentGrouped = null; + foreach ($propertyWebContent as $content) { + if (isset($propertyWebContentGrouped[$content['language_code']]) && count($propertyWebContentGrouped[$content['language_code']]) > 2) { + continue; + } + $propertyWebContentGrouped[$content['language_code']][] = $content; + } + + $propertyWeb['data']['property_web_content'] = $propertyWebContentGrouped; + //PropertyWebContent + + + //property_web_weather + $propertyWeb['data']['property_web_weather'] = $propertyWeb['data']['weather_active'] == 1 ? $propertyWeb['data']['property_web_weather'] : null; + + //PropertyWebComponent + if ($propertyWeb['data']['property_web_component']) { + + $propertyWebComponent = $propertyWeb['data']['property_web_component']; + unset($propertyWeb['data']['property_web_component']); + + $propertyWebComponentGoogleTagManager = collect($propertyWebComponent) + ->where('component_id', 1) + ->first(); + + if ($propertyWebComponentGoogleTagManager) { + $propertyWeb['data']['property_web_component']['google_tag_manager'] = $propertyWebComponentGoogleTagManager['parameterArray']; + } + + $propertyWebComponentGoogleAnalytics = collect($propertyWebComponent) + ->where('component_id', 4) + ->first(); + + if ($propertyWebComponentGoogleAnalytics) { + $propertyWeb['data']['property_web_component']['google_analytics'] = $propertyWebComponentGoogleAnalytics['parameterArray']; + } + + $propertyWebComponentJotform = collect($propertyWebComponent) + ->where('component_id', 5) + ->first(); + + if ($propertyWebComponentJotform) { + $propertyWeb['data']['property_web_component']['jotform'] = $propertyWebComponentJotform['parameterArray']; + } + + + $propertyWebComponentMetaPixel = collect($propertyWebComponent) + ->where('component_id', 6) + ->first(); + + if ($propertyWebComponentMetaPixel) { + $propertyWeb['data']['property_web_component']['metapixel'] = $propertyWebComponentMetaPixel['parameterArray']; + } + + + $propertyWebComponentTawkTo = collect($propertyWebComponent) + ->where('component_id', 7) + ->first(); + + if ($propertyWebComponentTawkTo) { + $propertyWeb['data']['property_web_component']['tawkto'] = $propertyWebComponentTawkTo['parameterArray']; + } + + $propertyWebComponentGoogleSiteVerification = collect($propertyWebComponent) + ->where('component_id', 8) + ->first(); + + if ($propertyWebComponentGoogleSiteVerification) { + $propertyWeb['data']['property_web_component']['googlesiteverification'] = $propertyWebComponentGoogleSiteVerification['parameterArray']; + } + + $propertyWebComponentSojernWeb = collect($propertyWebComponent) + ->where('component_id', 9) + ->first(); + + if ($propertyWebComponentSojernWeb) { + $propertyWeb['data']['property_web_component']['sojernweb'] = $propertyWebComponentSojernWeb['parameterArray']; + } + + $propertyWebComponentSojernBookingEngine = collect($propertyWebComponent) + ->where('component_id', 10) + ->first(); + + if ($propertyWebComponentSojernBookingEngine) { + $propertyWeb['data']['property_web_component']['sojernbookingengine'] = $propertyWebComponentSojernBookingEngine['parameterArray']; + } + + + $propertyWebComponentGoogleAds = collect($propertyWebComponent) + ->where('component_id', 11) + ->first(); + + if ($propertyWebComponentGoogleAds) { + $propertyWeb['data']['property_web_component']['googleads'] = $propertyWebComponentGoogleAds['parameterArray']; + } + + $propertyWebComponentMetaVerification = collect($propertyWebComponent) + ->where('component_id', 12) + ->first(); + + if ($propertyWebComponentMetaVerification) { + $propertyWeb['data']['property_web_component']['metaverification'] = $propertyWebComponentMetaVerification['parameterArray']; + } + + + $propertyWebComponentClarityweb = collect($propertyWebComponent) + ->where('component_id', 13) + ->first(); + + if ($propertyWebComponentClarityweb) { + $propertyWeb['data']['property_web_component']['clarityweb'] = $propertyWebComponentClarityweb['parameterArray']; + } + + $propertyWebComponentClarityBookingEngine = collect($propertyWebComponent) + ->where('component_id', 14) + ->first(); + + if ($propertyWebComponentClarityBookingEngine) { + $propertyWeb['data']['property_web_component']['claritybookingengine'] = $propertyWebComponentClarityBookingEngine['parameterArray']; + } + + } + //PropertyWebComponent + + //PropertyAwardsCertificates + if (!empty($propertyWeb['data']['property']['property_awards_certificates'])) { + + $staticPolicyIds = [134, 177, 211, 232]; + $staticPolicyGroup = []; + + $propertyAwardsCertificates = collect($propertyWeb['data']['property']['property_awards_certificates'])->where('status', 1)->whereIn('category_id', $staticPolicyIds)->sortByDesc('id')->toArray(); + + if ($propertyAwardsCertificates) { + + foreach ($propertyAwardsCertificates as $propertyAwardsCertificate) { + + if (isset($staticPolicyGroup[$propertyAwardsCertificate['category_id']]['language'][$propertyAwardsCertificate['language_code']])) { + continue; + } + + + $staticPolicyGroup[$propertyAwardsCertificate['category_id']]['name'] = $propertyAwardsCertificate['awards_certificate_category']['name']; + $staticPolicyGroup[$propertyAwardsCertificate['category_id']]['language_key'] = $propertyAwardsCertificate['awards_certificate_category']['language_key']; + + if (empty($propertyAwardsCertificate['language_code'])) { + $propertyAwardsCertificate['language_code'] = 'default'; + } + + $staticPolicyGroup[$propertyAwardsCertificate['category_id']]['language'][$propertyAwardsCertificate['language_code']] = [ + 'file' => $propertyAwardsCertificate['file_path'], + 'url' => Config::get('app.imageUrl') . '/property-photos/' . $propertyWeb['data']['property']['id'] . '/awards-certificates/' . $propertyAwardsCertificate['file_path'] + ]; + + } + + } + + $propertyWeb['data']['static_policy'] = !empty($staticPolicyGroup) ? $staticPolicyGroup : null; + + unset($propertyWeb['data']['property']['property_awards_certificates']); + + } + //PropertyAwardsCertificates + + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $propertyWeb['data']['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', + 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates.awardsCertificateCategory', + 'propertyPaymentMapping' + ], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $propertyDetail = $this->propertyService->select($propertyRequest, $getPropertyFields); + $propertyDetail = $propertyDetail['data']; + + + $propertyDetail['property_brand']['logo_url'] = isset($propertyDetail['property_brand']) ? Config::get('app.imageUrl') . '/property-photos/' . $propertyDetail['id'] . "/logo/" . $propertyDetail['property_brand']['logo_name'] . '_250x250.' . $propertyDetail['property_brand']['logo_file_ext'] : null; + $propertyDetail['property_contact']['social_media_addresses'] = json_decode($propertyDetail['property_contact']['social_media_addresses'], 1); + + + $colorCodes = []; + foreach ($propertyWeb['data']['property_web_color_mapping'] as $color) { + $colorCodes[] = [ + 'color_number' => $color['order_number'], + 'color_code' => $color['color_code'], + ]; + } + $colorCodes = $colorCodes ? $colorCodes : json_decode($propertyDetail['property_brand']['color_codes'], 1); + $propertyDetail['property_brand']['color_codes'] = $colorCodes; + + $propertyAdditionalInfo = null; + $propertyAdditionalInfoRequest = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy(['property_id' => $propertyDetail['id']]); + if (isset($propertyAdditionalInfoRequest['data'])) { + $propertyAdditionalInfo = $propertyAdditionalInfoRequest['data']; + } + + + //coverPhotos + $coverPhotosParam = [ + 'property_id' => $propertyWeb['data']['property']['id'], + 'property_web_id' => $propertyWeb['data']['id'], + 'mode' => $params['mode'], + ]; + $landingPhoto = $this->coverPhotos($coverPhotosParam); + //coverPhotos + + + $propertyWeb['data']['property']['name'] = $propertyDetail['name']; + $propertyWeb['data']['property']['property_type'] = $propertyDetail['property_type']['name']; + $propertyWeb['data']['property']['property_type_key'] = $propertyDetail['property_type']['language_key']; + $propertyWeb['data']['property']['property_chain'] = $propertyDetail['property_chain']['name']; + $propertyWeb['data']['property']['official_name'] = $propertyDetail['official_name']; + $propertyWeb['data']['property']['tax_office'] = $propertyDetail['tax_office']; + $propertyWeb['data']['property']['tax_number'] = $propertyDetail['tax_number']; + $propertyWeb['data']['property']['currency_type'] = $propertyDetail['currency_type']; + $propertyWeb['data']['property']['property_brand'] = $propertyDetail['property_brand']; + $propertyWeb['data']['property']['property_contact'] = $propertyDetail['property_contact']; + $propertyWeb['data']['property']['description'] = $propertyDetail['name']; + $propertyWeb['data']['property']['additional_info'] = $propertyAdditionalInfo; + $propertyWeb['data']['property']['landing_photo'] = $landingPhoto; + + $propertyWeb['data']['is_virtual_payment_active'] = collect($propertyDetail['property_payment_mapping'])->where('status', 1)->count() > 0 ? true : false; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWeb['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + $response['errorCode'] = $e->getCode(); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode'], fillOnUndefined($response, 'errorCode', null)); + } + + public function checkDomainToken(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => fillOnUndefined($params, 'token')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true, + "with" => [ + 'property', 'propertyWebTemplate', 'propertyBookingEngine', + 'propertyWebMenuMenuMapping', 'propertyWebLanguageMapping', + 'propertyGroupMapping.propertyGroup', 'propertyWebAbout', 'propertyWebPopup', 'propertyWebWeather', + 'propertyWebComponent', 'propertyWebRoomMapping.roomDetail' + ] + ]; + $propertyWeb = $this->propertyWebService->select($propertyRequest, ['id', 'property_id', 'domain', 'default_language', 'template_id', 'token', 'is_ssl_active', 'weather_active', 'cover_video_id']); + $propertyGroupId = NULL; + $propertyBookingEngineGroupId = NULL; + if (isset($propertyWeb['data']['property_group_mapping'])) { + foreach ($propertyWeb['data']['property_group_mapping'] as $propertyGroupMapping) { + if (!empty($propertyGroupMapping['property_group'] && $propertyGroupMapping['property_group']['type'] === "MYW")) { + $propertyGroupId = $propertyGroupMapping['property_group']['id']; + } + if (!empty($propertyGroupMapping['property_group'] && $propertyGroupMapping['property_group']['type'] === "BEN")) { + $propertyBookingEngineGroupId = $propertyGroupMapping['property_group']['id']; + } + } + } + + $propertyGroupMappingRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_group_id', 'condition' => '=', 'value' => $propertyGroupId], + ['field' => 'property_id', 'condition' => '!=', 'value' => fillOnUndefined($propertyWeb['data'], 'property_id')] + ], + 'orderBy' => [ + ["field" => "order_number", "value" => "ASC"] + ], + "with" => ['property.propertyWeb'] + ]; + + $propertyGroupWebSiteResult = $this->propertyGroupMappingService->getPropertyGroupMapping($propertyGroupMappingRequest); + + if ($propertyGroupWebSiteResult['status'] != 'success') { + throw new ApiErrorException($propertyGroupWebSiteResult['message']); + } + + $propertyGroupWebSites = $propertyGroupWebSiteResult['data']; + + $groupWebSites = []; + foreach ($propertyGroupWebSites as $propertyGroupWebSite) { + if ($propertyGroupWebSite['property'] && $propertyGroupWebSite['property']['property_web']) { + $propertyGroupWebSite['property']['property_web']['name'] = $propertyGroupWebSite['property']['name']; + $groupWebSites[] = $propertyGroupWebSite['property']['property_web']; + } + } + + $groupWebSites = collect($groupWebSites)/*->where('is_published', 1)*/ ->values()->toArray(); + + if (!empty($propertyWeb['data'])) { + $propertyWeb['data']['property_group_web_sites'] = $groupWebSites; + unset($propertyWeb['data']['property_group_mapping']); + } + + $propertyBookingGroupMappingRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_group_id', 'condition' => '=', 'value' => $propertyBookingEngineGroupId], + ], + 'orderBy' => [ + ["field" => "order_number", "value" => "ASC"] + ], + "with" => ['property.propertyBookingEngineToken', 'property.propertyWeb'] + ]; + + $propertyGroupBookingResult = $this->propertyGroupMappingService->getPropertyGroupMapping($propertyBookingGroupMappingRequest); + if ($propertyGroupBookingResult['status'] != 'success') { + throw new ApiErrorException($propertyGroupBookingResult['message']); + } + + $propertyGroupBookings = $propertyGroupBookingResult['data']; + + $bookingGroupProperties = []; + foreach ($propertyGroupBookings as $key => $propertyGroupBooking) { + if ($propertyGroupBooking['property']) { + $bookingGroupProperties[$key]['property_id'] = $propertyGroupBooking['property']['id']; + $bookingGroupProperties[$key]['name'] = $propertyGroupBooking['property']['name']; + if ($propertyGroupBooking['property']['property_booking_engine_token']) { + $bookingGroupProperties[$key]['token'] = $propertyGroupBooking['property']['property_booking_engine_token']['token']; + } else { + $bookingGroupProperties[$key]['token'] = NULL; + } + + $bookingGroupProperties[$key]['web'] = null; + if (!empty($propertyGroupBooking['property']['property_web'])) { + if ($propertyGroupBooking['property']['property_web']['status'] && $propertyGroupBooking['property']['property_web']['is_published']) { + $bookingGroupProperties[$key]['web'] = $propertyGroupBooking['property']['property_web']['webProtocolUrl']; + } + } + } + } + + if (!empty($bookingGroupProperties)) { + $propertyWeb['data']['property_booking_group'] = collect($bookingGroupProperties)->where('token', '!=', NULL)->toArray(); + } + + $mywebServiceMode = fillOnUndefined($params, 'serviceMode', 'live'); // preview or live + $mywebServiceModeStatus = $mywebServiceMode === "live" ? 1 : 2; + + if (!empty($propertyWeb['data']['property_booking_engine'])) { + $propertyChannelMappingRequestData = [ + 'property_id' => $propertyWeb['data']['property_booking_engine']['property_id'], + 'channel_id' => $propertyWeb['data']['property_booking_engine']['channel_id'] + ]; + $propertyChannelMappingData = $this->propertyChannelMappingService->checkPropertyChannelMapping($propertyChannelMappingRequestData); + if ($propertyChannelMappingData['data'] === "") { + $propertyWeb['data']['property_booking_engine']['token'] = NULL; + } + } + + $propertyWebPreviewOrLiveMenus = []; + if (isset($propertyWeb['data']['property_web_menu_menu_mapping'])) { + $propertyWebPreviewOrLiveMenus = collect($propertyWeb['data']['property_web_menu_menu_mapping']) + ->where('status', $mywebServiceModeStatus)->toArray(); + } + + if ($propertyWeb['status'] != 'success' || !$propertyWeb['data']) { + throw new ApiErrorException('Domain not found'); + } + + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + + $propertyWebLanguageMappings = $propertyWeb['data']['property_web_language_mapping'] ? $propertyWeb['data']['property_web_language_mapping'] : []; + $propertyWebLanguageMappings = collect($propertyWebLanguageMappings) + ->keyBy('language_code') + ->where('status', $mywebServiceModeStatus) + ->toArray(); + + if ($propertyWebLanguageMappings) { + foreach ($availableLanguages['data'] as $language) { + if (array_key_exists($language['code'], $propertyWebLanguageMappings)) { + $propertyWeb['data']['available_language_codes'][$language['code']] = $language; + } + } + } else { + foreach ($availableLanguages['data'] as $language) { + $propertyWeb['data']['available_language_codes'][$language['code']] = $language; + } + } + $webMenu = []; + if (empty($propertyWebPreviewOrLiveMenus)) { + $myWebMenus = $this->propertyWebMenuService->getPropertyWebMenu(); + if ($myWebMenus['status'] != 'success') { + throw new ApiErrorException($myWebMenus['message']); + } + $propertyWeb['data']['web_menu']['LVL1'] = collect($myWebMenus['data']['property_web_menus'])->where('type', '=', '1')->keyBy('alias')->toArray(); + $propertyWeb['data']['web_menu']['LEGAL'] = collect($myWebMenus['data']['property_web_menus'])->where('type', '=', '3')->keyBy('alias')->toArray(); + unset($propertyWeb['data']['property_web_menu_menu_mapping']); + } else { + + $webMenuTypes = collect($propertyWeb['data']['property_web_menu_menu_mapping']) + ->where('status', $mywebServiceModeStatus) + ->groupBy('menu_code')->toArray(); + + $allLevelData = []; + $allTopData = []; + foreach ($webMenuTypes as $menuTypeKey => $webMenuType) { + if ($menuTypeKey === self::MENU_CODE_LVL1) { + $allLevelData = $this->getTypeMenus($webMenuType, $menuTypeKey); + } elseif ($menuTypeKey === self::MENU_CODE_TOP) { + $allTopData = $this->getTypeMenus($webMenuType, $menuTypeKey); + } + } + + + unset($propertyWeb['data']['property_web_menu_menu_mapping']); + $propertyWeb['data']['web_menu'] = array_merge($allTopData, $allLevelData); + } + + //property_web_room_mapping + $propertyWebRoomMapping = []; + $propertyWebRoomMappingTemp = $propertyWeb['data']['property_web_room_mapping']; + unset($propertyWeb['data']['property_web_room_mapping']); + foreach ($propertyWebRoomMappingTemp as $propertyWebRoom) { + $propertyWebRoomMapping[] = [ + 'id' => $propertyWebRoom['room_detail']['id'], + 'name' => $propertyWebRoom['room_detail']['name'], + 'slug' => Str::slug($propertyWebRoom['room_detail']['name'], $separator = '-', $language = 'en') . '-' . $propertyWebRoom['room_detail']['id'] + ]; + } + $propertyWeb['data']['property_web_room_mapping'] = $propertyWebRoomMapping; + //property_web_room_mapping + + //PropertyWebPopUp + $propertyWebPopUp = null; + if ($propertyWeb['data']['property_web_popup']) { + $propertyWebPopUp = collect($propertyWeb['data']['property_web_popup']) + ->where('start_date', '<=', Carbon::now()->toDateString()) + ->where('end_date', '>=', Carbon::now()->toDateString()) + ->toArray(); + + $propertyWebPopUp = array_values($propertyWebPopUp); + + } + + $propertyWeb['data']['property_web_popup'] = $propertyWebPopUp; + //PropertyWebPopUp + + //PropertyWebContent + $propertyWebContent = null; + $propertyWebContentCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyWeb['data']['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + //'with' => ['ContentCategory'], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ] + ]; + + $propertyWebContentCriteria['skip'] = 0; + $propertyWebContentCriteria['take'] = 100; + $propertyWebContent = $this->propertyWebContentService->select($propertyWebContentCriteria, ['id', 'title', 'property_id', 'slug', 'content_category_id', 'language_code', 'image']); + $propertyWebContent = ($propertyWebContent['status'] == 'success' && !empty($propertyWebContent['data'])) ? array_values($propertyWebContent['data']) : []; + + $propertyWebContentGrouped = null; + foreach ($propertyWebContent as $content) { + if (isset($propertyWebContentGrouped[$content['language_code']]) && count($propertyWebContentGrouped[$content['language_code']]) > 2) { + continue; + } + $propertyWebContentGrouped[$content['language_code']][] = $content; + } + + $propertyWeb['data']['property_web_content'] = $propertyWebContentGrouped; + //PropertyWebContent + + + //property_web_weather + $propertyWeb['data']['property_web_weather'] = $propertyWeb['data']['weather_active'] == 1 ? $propertyWeb['data']['property_web_weather'] : null; + + + //PropertyWebComponent + if ($propertyWeb['data']['property_web_component']) { + + $propertyWebComponent = $propertyWeb['data']['property_web_component']; + unset($propertyWeb['data']['property_web_component']); + + $propertyWebComponentGoogleTagManager = collect($propertyWebComponent) + ->where('component_id', 1) + ->first(); + + if ($propertyWebComponentGoogleTagManager) { + $propertyWeb['data']['property_web_component']['google_tag_manager'] = $propertyWebComponentGoogleTagManager['parameterArray']; + } + + $propertyWebComponentGoogleAnalytics = collect($propertyWebComponent) + ->where('component_id', 4) + ->first(); + + if ($propertyWebComponentGoogleAnalytics) { + $propertyWeb['data']['property_web_component']['google_analytics'] = $propertyWebComponentGoogleAnalytics['parameterArray']; + } + + $propertyWebComponentJotform = collect($propertyWebComponent) + ->where('component_id', 5) + ->first(); + + if ($propertyWebComponentJotform) { + $propertyWeb['data']['property_web_component']['jotform'] = $propertyWebComponentJotform['parameterArray']; + } + + + $propertyWebComponentMetaPixel = collect($propertyWebComponent) + ->where('component_id', 6) + ->first(); + + if ($propertyWebComponentMetaPixel) { + $propertyWeb['data']['property_web_component']['metapixel'] = $propertyWebComponentMetaPixel['parameterArray']; + } + + + $propertyWebComponentTawkTo = collect($propertyWebComponent) + ->where('component_id', 7) + ->first(); + + if ($propertyWebComponentTawkTo) { + $propertyWeb['data']['property_web_component']['tawkto'] = $propertyWebComponentTawkTo['parameterArray']; + } + + $propertyWebComponentGoogleSiteVerification = collect($propertyWebComponent) + ->where('component_id', 8) + ->first(); + + if ($propertyWebComponentGoogleSiteVerification) { + $propertyWeb['data']['property_web_component']['googlesiteverification'] = $propertyWebComponentGoogleSiteVerification['parameterArray']; + } + + $propertyWebComponentMetaVerification = collect($propertyWebComponent) + ->where('component_id', 12) + ->first(); + + if ($propertyWebComponentMetaVerification) { + $propertyWeb['data']['property_web_component']['metaverification'] = $propertyWebComponentMetaVerification['parameterArray']; + } + + } + //PropertyWebComponent + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $propertyWeb['data']['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyBrand', 'propertyContact', 'propertyPhotos', 'propertyType', 'propertyChain', + 'propertyRooms', 'propertyWeb', 'propertyAwardsCertificates.awardsCertificateCategory', + 'propertyPaymentMapping' + ], + 'firstRow' => true + ]; + + $getPropertyFields = ['id', 'name', 'property_type_id', 'chain_id', 'rating', 'official_name', 'tax_office', 'tax_number', 'currency_type', 'country']; + $propertyDetail = $this->propertyService->select($propertyRequest, $getPropertyFields); + $propertyDetail = $propertyDetail['data']; + + + $propertyDetail['property_brand']['logo_url'] = $propertyDetail['property_brand'] ? Config::get('app.imageUrl') . '/property-photos/' . $propertyDetail['id'] . "/logo/" . $propertyDetail['property_brand']['logo_name'] . '_250x250.' . $propertyDetail['property_brand']['logo_file_ext'] : null; + $propertyDetail['property_contact']['social_media_addresses'] = json_decode($propertyDetail['property_contact']['social_media_addresses'], 1); + + $colorCodes = json_decode($propertyDetail['property_brand']['color_codes'], 1); + $propertyDetail['property_brand']['color_codes'] = $colorCodes; + + $propertyAdditionalInfo = null; + $propertyAdditionalInfoRequest = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy(['property_id' => $propertyDetail['id']]); + if (isset($propertyAdditionalInfoRequest['data'])) { + $propertyAdditionalInfo = $propertyAdditionalInfoRequest['data']; + } + + + //coverPhotos + $coverPhotosParam = [ + 'property_id' => $propertyWeb['data']['property']['id'], + 'property_web_id' => $propertyWeb['data']['id'], + 'mode' => $mywebServiceModeStatus, + ]; + $landingPhoto = $this->coverPhotos($coverPhotosParam); + //coverPhotos + + + $propertyWeb['data']['property']['name'] = $propertyDetail['name']; + $propertyWeb['data']['property']['property_type'] = $propertyDetail['property_type']['name']; + $propertyWeb['data']['property']['property_type_key'] = $propertyDetail['property_type']['language_key']; + $propertyWeb['data']['property']['property_chain'] = $propertyDetail['property_chain']['name']; + $propertyWeb['data']['property']['official_name'] = $propertyDetail['official_name']; + $propertyWeb['data']['property']['tax_office'] = $propertyDetail['tax_office']; + $propertyWeb['data']['property']['tax_number'] = $propertyDetail['tax_number']; + $propertyWeb['data']['property']['currency_type'] = $propertyDetail['currency_type']; + $propertyWeb['data']['property']['property_brand'] = $propertyDetail['property_brand']; + $propertyWeb['data']['property']['property_contact'] = $propertyDetail['property_contact']; + $propertyWeb['data']['property']['description'] = $propertyDetail['name']; + $propertyWeb['data']['property']['additional_info'] = $propertyAdditionalInfo; + $propertyWeb['data']['property']['landing_photo'] = $landingPhoto; + + $propertyWeb['data']['is_virtual_payment_active'] = collect($propertyDetail['property_payment_mapping'])->where('status', 1)->count() > 0 ? true : false; + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWeb['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function checkDomainWebGroup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'domain', 'condition' => '=', 'value' => fillOnUndefined($params, 'domain')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyGroupMapping.property.propertyWeb.propertyWebPhotoMapping.propertyPhoto', + 'propertyGroupMapping.property.propertyBrand', 'propertyGroupMapping.property.propertyContact', + 'propertyGroupMapping.property.propertyBookingEngineToken' + ], + 'firstRow' => true, + ]; + + $propertyWebGroup = $this->propertyWebService->selectWebGroup($propertyRequest, + [ + 'id', 'title', 'property_group_id', 'domain', 'default_language', + 'logo', 'is_ssl_active', 'address', 'email', 'phone', 'template', 'slogan' + ]); + + if ($propertyWebGroup['status'] != 'success') { + throw new ApiErrorException(lang('Domain not found.')); + } + + $propertyWebGroup = !empty($propertyWebGroup['data']) ? $propertyWebGroup['data'] : null; + + if (empty($propertyWebGroup)) { + throw new ApiErrorException(lang('Domain not found.')); + } + + + $propertyWebGroupData = []; + $propertyWebGroupData['title'] = $propertyWebGroup['title']; + $propertyWebGroupData['domain'] = $propertyWebGroup['domain']; + $propertyWebGroupData['template'] = $propertyWebGroup['template']; + $propertyWebGroupData['default_language'] = $propertyWebGroup['default_language']; + $propertyWebGroupData['logo'] = $propertyWebGroup['logo']; + $propertyWebGroupData['logoUrl'] = $propertyWebGroup['logoUrl']; + $propertyWebGroupData['address'] = $propertyWebGroup['address']; + $propertyWebGroupData['email'] = $propertyWebGroup['email']; + $propertyWebGroupData['phone'] = $propertyWebGroup['phone']; + $propertyWebGroupData['is_ssl_active'] = $propertyWebGroup['is_ssl_active']; + $propertyWebGroupData['slogan'] = $propertyWebGroup['slogan']; + + if (!empty($propertyWebGroup['property_group_mapping'])) { + $propertyWebGroup['property_group_mapping'] = collect($propertyWebGroup['property_group_mapping'])->sortBy('order_number')->toArray(); + } + + $propertyGroupMapping = []; + foreach ($propertyWebGroup['property_group_mapping'] as $property) { + + if (empty($property['property']['property_web'])) { + continue; + } + + if ($property['property']['property_web']['status'] != 1 || $property['property']['property_web']['is_published'] != 1) { + continue; + } + + $propertyGroupMapping[$property['property_id']]['id'] = $property['property']['id']; + $propertyGroupMapping[$property['property_id']]['title'] = $property['property']['name']; + $propertyGroupMapping[$property['property_id']]['domain'] = $property['property']['property_web']['domain']; + $propertyGroupMapping[$property['property_id']]['webProtocolUrl'] = $property['property']['property_web']['webProtocolUrl']; + $propertyGroupMapping[$property['property_id']]['logoUrl'] = $property['property']['property_brand']['logoUrl']; + $propertyGroupMapping[$property['property_id']]['address'] = $property['property']['property_contact']['address']; + $propertyGroupMapping[$property['property_id']]['email'] = $property['property']['property_contact']['email']; + $propertyGroupMapping[$property['property_id']]['phone'] = $property['property']['property_contact']['view_full_phone']; + $propertyGroupMapping[$property['property_id']]['content_code'] = $property['property']['content_code']; + $propertyGroupMapping[$property['property_id']]['token'] = $property['property']['property_booking_engine_token']['token']; + + + $colorCodes = json_decode($property['property']['property_brand']['color_codes'], 1); + $propertyGroupMapping[$property['property_id']]['colorCodes'] = $colorCodes; + + + $propertyWebPhotoMapping = collect($property['property']['property_web']['property_web_photo_mapping']) + ->where('status', 1) + ->where('is_cover', 1) + ->toArray(); + + $propertyGroupMapping[$property['property_id']]['coverPhoto'] = []; + foreach ($propertyWebPhotoMapping as $propertyWebPhoto) { + $propertyGroupMapping[$property['property_id']]['coverPhoto'][] = $propertyWebPhoto['property_photo']['photoUrl']; + } + + + } + + $propertyWebGroupData['propertyGroupMapping'] = array_values($propertyGroupMapping); + + + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + + foreach ($availableLanguages['data'] as $language) { + $propertyWebGroupData['available_language_codes'][$language['code']] = $language; + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWebGroupData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function contentDomainWebGroup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + + $propertyRequest = [ + 'criteria' => [ + ['field' => 'domain', 'condition' => '=', 'value' => fillOnUndefined($params, 'domain')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1], + ], + 'with' => [ + 'propertyGroupMapping.property.propertyWeb.propertyWebPhotoMapping.propertyPhoto', + 'propertyGroupMapping.property.propertyWeb.propertyWebContentSummary.ContentCategory', + 'propertyGroupMapping.property.propertyBrand', 'propertyGroupMapping.property.propertyContact', + 'propertyGroupMapping.property.propertyBookingEngineToken', + 'propertyGroupMapping.property.propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlaceCategory', + 'propertyGroupMapping.property.propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlaceWorkingHour', + 'propertyGroupMapping.property.propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlacePhotoMapping.propertyPlacePhoto', + 'propertyGroupMapping.property.propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFact', + 'propertyGroupMapping.property.propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlaceFactMapping.propertyPlaceFactTitleFactMapping.placeFactTitle', + 'propertyGroupMapping.property.propertyWeb.propertyWebPlaceMapping.placeDetail.propertyPlacePhotoMapping', + ], + 'firstRow' => true, + ]; + + $propertyWebGroup = $this->propertyWebService->selectWebGroup($propertyRequest, + [ + 'id', 'title', 'property_group_id', 'domain', 'default_language', + 'logo', 'is_ssl_active', 'address', 'email', 'phone', 'template', 'slogan' + ]); + + if ($propertyWebGroup['status'] != 'success') { + throw new ApiErrorException(lang('Domain not found.')); + } + + $propertyWebGroup = !empty($propertyWebGroup['data']) ? $propertyWebGroup['data'] : null; + + if (empty($propertyWebGroup)) { + throw new ApiErrorException(lang('Domain not found.')); + } + + + $propertyWebGroupData = []; + $propertyWebGroupData['title'] = $propertyWebGroup['title']; + $propertyWebGroupData['domain'] = $propertyWebGroup['domain']; + $propertyWebGroupData['template'] = $propertyWebGroup['template']; + $propertyWebGroupData['default_language'] = $propertyWebGroup['default_language']; + $propertyWebGroupData['logo'] = $propertyWebGroup['logo']; + $propertyWebGroupData['logoUrl'] = $propertyWebGroup['logoUrl']; + $propertyWebGroupData['address'] = $propertyWebGroup['address']; + $propertyWebGroupData['email'] = $propertyWebGroup['email']; + $propertyWebGroupData['phone'] = $propertyWebGroup['phone']; + $propertyWebGroupData['is_ssl_active'] = $propertyWebGroup['is_ssl_active']; + $propertyWebGroupData['slogan'] = $propertyWebGroup['slogan']; + + if (!empty($propertyWebGroup['property_group_mapping'])) { + $propertyWebGroup['property_group_mapping'] = collect($propertyWebGroup['property_group_mapping'])->sortBy('order_number')->toArray(); + } + + $propertyGroupMapping = []; + foreach ($propertyWebGroup['property_group_mapping'] as $property) { + + //dd($property); + + if (empty($property['property']['property_web'])) { + continue; + } + + if ($property['property']['property_web']['status'] != 1 || $property['property']['property_web']['is_published'] != 1) { + continue; + } + + $propertyGroupMapping[$property['property_id']]['id'] = $property['property']['id']; + $propertyGroupMapping[$property['property_id']]['title'] = $property['property']['name']; + $propertyGroupMapping[$property['property_id']]['domain'] = $property['property']['property_web']['domain']; + $propertyGroupMapping[$property['property_id']]['webProtocolUrl'] = $property['property']['property_web']['webProtocolUrl']; + $propertyGroupMapping[$property['property_id']]['logoUrl'] = $property['property']['property_brand']['logoUrl']; + $propertyGroupMapping[$property['property_id']]['address'] = $property['property']['property_contact']['address']; + $propertyGroupMapping[$property['property_id']]['email'] = $property['property']['property_contact']['email']; + $propertyGroupMapping[$property['property_id']]['phone'] = $property['property']['property_contact']['view_full_phone']; + $propertyGroupMapping[$property['property_id']]['content_code'] = $property['property']['content_code']; + $propertyGroupMapping[$property['property_id']]['token'] = $property['property']['property_booking_engine_token']['token']; + + + $propertyGroupMapping[$property['property_id']]['property_place'] = $property['property']['property_web']['property_web_place_mapping']; + $propertyGroupMapping[$property['property_id']]['property_web_content'] = $property['property']['property_web']['property_web_content_summary']; + + + $colorCodes = json_decode($property['property']['property_brand']['color_codes'], 1); + $propertyGroupMapping[$property['property_id']]['colorCodes'] = $colorCodes; + + $propertyWebPhotoMapping = collect($property['property']['property_web']['property_web_photo_mapping']) + ->where('status', 1) + ->toArray(); + + $propertyGroupMapping[$property['property_id']]['propertyWebPhoto'] = []; + foreach ($propertyWebPhotoMapping as $propertyWebPhoto) { + $propertyGroupMapping[$property['property_id']]['propertyWebPhoto'][] = $propertyWebPhoto['property_photo']['photoUrl']; + } + + + $propertyWebPhotoMapping = collect($property['property']['property_web']['property_web_photo_mapping']) + ->where('status', 1) + ->where('is_cover', 1) + ->toArray(); + + $propertyGroupMapping[$property['property_id']]['coverPhoto'] = []; + foreach ($propertyWebPhotoMapping as $propertyWebPhoto) { + $propertyGroupMapping[$property['property_id']]['coverPhoto'][] = $propertyWebPhoto['property_photo']['photoUrl']; + } + + + } + + $propertyWebGroupData['propertyGroupMapping'] = array_values($propertyGroupMapping); + + + $availableLanguageRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'is_application', 'condition' => '=', 'value' => 1], + ['field' => 'is_published', 'condition' => '=', 'value' => 1] + ], + ]; + + $availableLanguages = $this->languageService->select($availableLanguageRequest, ['code', 'name', 'language_key']); + + foreach ($availableLanguages['data'] as $language) { + $propertyWebGroupData['available_language_codes'][$language['code']] = $language; + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWebGroupData]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + public function test(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $params]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getLanguages(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + + $languageData = $this->languageBaseService->createApplicationLanguageData(); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $languageData['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPropertyWebEditContent(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + + $propertyWebRequest = [ + 'id' => fillOnUndefined($params, 'property_web_id'), + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + $propertyWeb = $this->propertyWebService->getPropertyWeb($propertyWebRequest); + if ($propertyWeb['status'] != 'success') { + throw new ApiErrorException($propertyWeb['message']); + } + + $propertyWebPhotos = $this->propertyWebService->getPropertyWebPhotos($propertyWebRequest); + if ($propertyWeb['status'] != 'success') { + throw new ApiErrorException($propertyWeb['message']); + } + $languages = $this->languageService->getApplicationLanguages(); + if ($languages['status'] != 'success') { + throw new ApiErrorException($languages['message']); + } + + $myWebMenus = $this->propertyWebMenuService->getPropertyWebMenu(); + if ($myWebMenus['status'] != 'success') { + throw new ApiErrorException($myWebMenus['message']); + } + + $myWebMenus = collect($myWebMenus['data']['property_web_menus'])->where('type', '!=', '3')->toArray(); + $placeRequest = [ + 'property_id' => $params['property_id'] + ]; + $myPlaces = $this->propertyPlaceService->webMenuPlaceCategoriesAndPlaces($placeRequest); + if ($myPlaces['status'] != 'success') { + throw new ApiErrorException($myPlaces['message']); + } + + $myPlaces = $myPlaces['data']; + $myWebMenus = array_merge($myWebMenus, $myPlaces); + + $myWebMenuMappingRequest = [ + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + $myWebMenuMapping = $this->propertyWebMenuMappingService->getPropertyWebMenuMapping($myWebMenuMappingRequest); + + if ($myWebMenuMapping['status'] != 'success') { + throw new ApiErrorException($myWebMenuMapping['message']); + } + + $menuIndex = 1; + $responseMenus = []; + + + $myWebMenuMappingCollection = collect($myWebMenuMapping['data']['property_web_menu_mapping']); + foreach ($myWebMenus as $myWebMenu) { + $checkMenuMapping = $myWebMenuMappingCollection->where('type', '=', $myWebMenu['menu_type']) + ->where('property_web_menu_id', '=', $myWebMenu['id'])->first(); + + if ($checkMenuMapping) { + $myWebMenu['is_selected'] = true; + $myWebMenu['order_number_mapping'] = $checkMenuMapping['order_number']; + $myWebMenu['order_number'] = $menuIndex; + $myWebMenu['menu_code'] = $checkMenuMapping['menu_code']; + } else { + $myWebMenu['is_selected'] = false; + $myWebMenu['order_number_mapping'] = null; + $myWebMenu['order_number'] = $menuIndex; + + } + $menuIndex++; + $responseMenus[] = $myWebMenu; + } + $myWebMenus = $responseMenus; + + $myWebLanguageMapping = $this->propertyWebLanguageMappingService->getPropertyWebLanguageMapping($myWebMenuMappingRequest); + + if ($myWebLanguageMapping['status'] != 'success') { + throw new ApiErrorException($myWebLanguageMapping['message']); + } + + $myWebLanguageMapping = collect($myWebLanguageMapping['data']['property_web_language_mapping'])->keyBy('language_code')->toArray(); + + $myWebColorMapping = $this->propertyWebColorMappingService->getPropertyWebColorMapping($myWebMenuMappingRequest); + + if ($myWebColorMapping['status'] != 'success') { + throw new ApiErrorException($myWebColorMapping['message']); + } + + $myWebColors = []; + if ($myWebColorMapping['data']['property_web_color_mapping']) { + $myWebColorMapping = $myWebColorMapping['data']['property_web_color_mapping']; + + foreach ($myWebColorMapping as $key => $myWebColorMappingData) { + $myWebColors[$key]['color_number'] = $myWebColorMappingData['order_number']; + $myWebColors[$key]['color_code'] = $myWebColorMappingData['color_code']; + } + } else { + + $brandParam = [ + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + $propertyBrand = $this->propertyBrandService->getPropertyBrand($brandParam); + if ($propertyBrand['status'] != 'success') { + throw new ApiErrorException($propertyBrand['message']); + } + $myWebColors = collect($propertyBrand['data']['color_codes'])->take(3)->toArray(); + } + + $propertyWebAboutUsParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'property_web_id' => fillOnUndefined($params, 'property_web_id'), + 'mode' => 1, // live + ]; + $propertyWebAboutUs = $this->propertyWebAboutUsService->getPropertyWebAboutUs($propertyWebAboutUsParams); + + if ($propertyWebAboutUs['status'] != 'success') { + throw new ApiErrorException($propertyWebAboutUs['message']); + } + + $languages = $languages['data']; + + $propertyWebColors = $myWebColors ? $myWebColors : []; + + + foreach ($languages as &$language) { + if (array_key_exists($language['code'], $myWebLanguageMapping)) { + $language['is_selected'] = true; + } else { + $language['is_selected'] = false; + } + } + + $propertyWeb = $propertyWeb['data']; + $webData = [ + 'live_url' => Config::get('app.mainHostAddress') . '/live/' . $propertyWeb['token'] . '/' . $propertyWeb['default_language'], + 'preview_url' => Config::get('app.mainHostAddress') . '/preview/' . $propertyWeb['token'] . '/' . $propertyWeb['default_language'], + ]; + $propertyWeb = array_merge($propertyWeb, $webData); + + $propertyAdditionalInfo = $this->propertyAdditionalInfoService->getPropertyAdditionalInfo2KeyBy($params); + if ($propertyAdditionalInfo['status'] != 'success') { + throw new ApiErrorException('api-unknown-error'); + } + + $propertyRequest = [ + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + $property = $this->propertyService->getProperty($propertyRequest); + + if ($property['status'] != 'success') { + throw new ApiErrorException('api-unknown-error'); + } + + $propertyData = $property['data'] ? $property['data'] : []; + + $propertyCountryCode = $propertyData['get_property']['property_info']['country']; + + + $myWebContactEmail = isset($propertyAdditionalInfo['data']['myweb_contact_email']) ? $propertyAdditionalInfo['data']['myweb_contact_email'] : null; + $kvkkMersisNo = isset($propertyAdditionalInfo['data']['kvkk_mersis_no']) ? $propertyAdditionalInfo['data']['kvkk_mersis_no'] : null; + $kvkkDataController = isset($propertyAdditionalInfo['data']['kvkk_data_controller']) ? $propertyAdditionalInfo['data']['kvkk_data_controller'] : null; + $kvkkContactPerson = isset($propertyAdditionalInfo['data']['kvkk_contact_person']) ? $propertyAdditionalInfo['data']['kvkk_contact_person'] : null; + $kvkkAddress = isset($propertyAdditionalInfo['data']['kvkk_address']) ? $propertyAdditionalInfo['data']['kvkk_address'] : null; + $kvkkPhone = isset($propertyAdditionalInfo['data']['kvkk_phone']) ? $propertyAdditionalInfo['data']['kvkk_phone'] : null; + $kvkkTaxOffice = isset($propertyAdditionalInfo['data']['kvkk_tax_office']) ? $propertyAdditionalInfo['data']['kvkk_tax_office'] : null; + $kvkkTaxNumber = isset($propertyAdditionalInfo['data']['kvkk_tax_number']) ? $propertyAdditionalInfo['data']['kvkk_tax_number'] : null; + $kvkkEmail = isset($propertyAdditionalInfo['data']['kvkk_email']) ? $propertyAdditionalInfo['data']['kvkk_email'] : null; + $kvkkWebSite = isset($propertyAdditionalInfo['data']['kvkk_web_site']) ? $propertyAdditionalInfo['data']['kvkk_web_site'] : null; + $kvkkContactEmail = isset($propertyAdditionalInfo['data']['kvkk_contact_email']) ? $propertyAdditionalInfo['data']['kvkk_contact_email'] : null; + $kvkkContactAddress = isset($propertyAdditionalInfo['data']['kvkk_contact_address']) ? $propertyAdditionalInfo['data']['kvkk_contact_address'] : null; + + $propertyWeb['languages'] = $languages; + + $propertyWeb['colors'] = $propertyWebColors; + + $propertyWeb['property_country_code'] = $propertyCountryCode; + + $propertyWeb['menus'] = $myWebMenus; + + $propertyWeb['property_web_photos'] = $propertyWebPhotos['data']; + + $propertyWeb['property_web_about_us'] = $propertyWebAboutUs['data']; + $propertyWeb['additional_info']['myweb_contact_email'] = $myWebContactEmail; + $propertyWeb['additional_info']['kvkk_mersis_no'] = $kvkkMersisNo; + $propertyWeb['additional_info']['kvkk_data_controller'] = $kvkkDataController; + $propertyWeb['additional_info']['kvkk_contact_person'] = $kvkkContactPerson; + $propertyWeb['additional_info']['kvkk_address'] = $kvkkAddress; + $propertyWeb['additional_info']['kvkk_phone'] = $kvkkPhone; + $propertyWeb['additional_info']['kvkk_tax_office'] = $kvkkTaxOffice; + $propertyWeb['additional_info']['kvkk_tax_number'] = $kvkkTaxNumber; + $propertyWeb['additional_info']['kvkk_email'] = $kvkkEmail; + $propertyWeb['additional_info']['kvkk_web_site'] = $kvkkWebSite; + $propertyWeb['additional_info']['kvkk_contact_email'] = $kvkkContactEmail; + $propertyWeb['additional_info']['kvkk_contact_address'] = $kvkkContactAddress; + + + $propertyRooms = []; + $getPropertyRoomCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + 'with' => ['propertyRoomType', 'propertyWebRoomMapping'], + ]; + + $getPropertyRoom = $this->propertyRoomService->select($getPropertyRoomCriteria); + + if ($getPropertyRoom['status'] == 'success') { + $getPropertyRoom = $getPropertyRoom['data']; + foreach ($getPropertyRoom as $propertyRoom) { + $propertyRooms[] = [ + 'id' => $propertyRoom['id'], + 'name' => $propertyRoom['name'], + 'type' => $propertyRoom['property_room_type']['language_key'], + 'is_selected' => !empty($propertyRoom['property_web_room_mapping']) ? true : false + + ]; + } + } + + $propertyWeb['rooms'] = $propertyRooms; + + + $propertyPlaces = []; + $getPropertyPlaceCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'ASC'] + ], + 'with' => ['propertyPlaceCategory', 'propertyWebPlaceMapping'], + ]; + + $getPropertyPlace = $this->propertyPlaceService->select($getPropertyPlaceCriteria); + + if ($getPropertyPlace['status'] == 'success') { + $getPropertyPlace = $getPropertyPlace['data']; + foreach ($getPropertyPlace as $propertyPlace) { + $propertyPlaces[] = [ + 'id' => $propertyPlace['id'], + 'name' => $propertyPlace['name'], + 'type' => $propertyPlace['property_place_category']['language_key'], + 'is_selected' => !empty($propertyPlace['property_web_place_mapping']) ? true : false + + ]; + } + } + + $propertyWeb['places'] = $propertyPlaces; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWeb]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function updatePropertyWebContent(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + $params['user_id'] = $this->request->auth->id; + $updateWeb = $this->propertyWebService->updatePropertyWebContent($params); + if ($updateWeb['status'] != 'success') { + throw new ApiErrorException($updateWeb['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $updateWeb['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function setPublishWeb(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $request->params; + $params['user_id'] = $this->request->auth->id; + $updateWeb = $this->propertyWebService->setPublishWeb($params); + if ($updateWeb['status'] != 'success') { + throw new ApiErrorException($updateWeb['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $updateWeb['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPropertyWebMeta(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $params = $request->params ?? []; + $result = $this->propertyWebMetaService->select($params, ['id', 'name', 'status']); + + if ($result['status'] != 'success') { + throw new ApiErrorException($result['message']); + } + + foreach ($result['data'] as $key => $datum) { + + $dataArray = null; + + if ($datum['name'] == 'ContentDetail') { + + $propertyWebContentCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ] + ]; + + $propertyWebContent = $this->propertyWebContentService->select($propertyWebContentCriteria, ['id', 'title']); + $propertyWebContent = ($propertyWebContent['status'] == 'success' && !empty($propertyWebContent['data'])) ? array_values($propertyWebContent['data']) : []; + foreach ($propertyWebContent as $propertyWebContentData) { + $dataArray[] = [ + 'code' => $propertyWebContentData['id'], + 'name' => $propertyWebContentData['title'], + ]; + } + + } + + if ($datum['name'] == 'PlaceDetail') { + + $propertyPlacetCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ] + ]; + $propertyPlaces = $this->propertyPlaceService->select($propertyPlacetCriteria, ['id', 'name', 'language_name']); + $propertyPlaces = ($propertyPlaces['status'] == 'success' && !empty($propertyPlaces['data'])) ? array_values($propertyPlaces['data']) : []; + + foreach ($propertyPlaces as $propertyPlacesData) { + $dataArray[] = [ + 'code' => $propertyPlacesData['id'], + 'name' => $propertyPlacesData['name'], + ]; + } + + } + + if ($datum['name'] == 'RoomDetail') { + + $propertyRoomtCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1] + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ] + ]; + $propertyRooms = $this->propertyRoomService->select($propertyRoomtCriteria, ['id', 'name']); + $propertyRooms = ($propertyRooms['status'] == 'success' && !empty($propertyRooms['data'])) ? array_values($propertyRooms['data']) : []; + + foreach ($propertyRooms as $propertyRoomsData) { + $dataArray[] = [ + 'code' => $propertyRoomsData['id'], + 'name' => $propertyRoomsData['name'], + ]; + } + + } + + $result['data'][$key]['detail'] = $dataArray; + + } + + + //dd($result); + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $result['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function getPropertyWebMetaTag(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $params = $request->params ?? []; + $result = $this->propertyWebMetaService->selectTag($params, ['id', 'name', 'status']); + + if ($result['status'] != 'success') { + throw new ApiErrorException($result['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $result['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function syncPropertyWebMetaTag(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $params = $request->params ?? []; + $params['user_id'] = $this->request->auth->id; + $result = $this->propertyWebMetaService->syncTag($params); + + if ($result['status'] != 'success') { + throw new ApiErrorException($result['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => $result['message'], 'data' => $result['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + public function listPropertyWebMetaTagMapping(Request $request) + { + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + $params = $request->params ?? []; + $result = $this->propertyWebMetaService->selectMapping($params); + + if ($result['status'] != 'success') { + throw new ApiErrorException($result['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $result['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + + public function dashboardCountablePlace($params, $propertyWebId) + { + + $response = ["status" => false, "data" => null, "message" => ""]; + + try { + + $propertyWebLog = DB::select(" + SELECT property_web_log.country_code 'country_code', ip_nation_countries.country 'country', property_web_log.device_type 'device_type', count(property_web_log.id) 'count' + FROM property_web_log + INNER JOIN ip_nation_countries ON ip_nation_countries.code = property_web_log.country_code + WHERE property_web_log.web_id = $propertyWebId AND property_web_log.status = 1 GROUP BY property_web_log.country_code,ip_nation_countries.country, property_web_log.device_type; + "); + + $propertyWebLog = json_decode(json_encode($propertyWebLog), true); + + + $bookingCount = $this->bookingService->getBookingListCount(['property_id' => fillOnUndefined($params, 'property_id'), 'channel_id' => 1]); + + if ($bookingCount['status'] != 'success') { + throw new ApiErrorException($bookingCount['message']); + } + + $countryGroups = collect($propertyWebLog)->groupBy('country_code')->toArray(); + + $countyGroupCount = []; + foreach ($countryGroups as $countryGroupKey => $countryGroup) { + foreach ($countryGroup as $key => $groupData) { + + if (!isset($countyGroupCount[$countryGroupKey]['value'])) { + $countyGroupCount[$countryGroupKey]['value'] = 0; + } + + $countyGroupCount[$countryGroupKey]['value'] += $groupData['count']; + $countyGroupCount[$countryGroupKey]['country'] = $groupData['country_code']; + $countyGroupCount[$countryGroupKey]['country_name'] = $groupData['country']; + } + } + + array_multisort(array_column($countyGroupCount, 'value'), SORT_DESC, + $countyGroupCount + ); + + $activeUser = DB::select("SELECT count(id) 'count' FROM property_web_log + WHERE web_id = $propertyWebId AND created_at > " . Carbon::now()->subMinutes(self::WEB_ACTIVE_USER_MINUTE)->timestamp); + + $activeUser = json_decode(json_encode($activeUser), true); + $activeUser = reset($activeUser); + + + $response['data'] = [ + 'web_count' => collect($propertyWebLog)->where('device_type', 'web')->sum('count'), + 'mobile_count' => collect($propertyWebLog)->where('device_type', 'mobile')->sum('count'), + 'active_user_count' => $activeUser['count'], + 'booking_count' => $bookingCount['data'], + 'country_count' => collect($countyGroupCount)->values()->toArray(), + ]; + + $response['status'] = true; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['status'] = false; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = "Raw Query Error"; + $response['status'] = false; + } + + return $response; + } + + private function getTypeMenus($menuType, $menuTypeKey) + { + + $webMenuTypes = collect($menuType)->groupBy('type')->toArray(); + + $allData = []; + $allData[$menuTypeKey] = []; + foreach ($webMenuTypes as $webMenuTypeKey => $webMenuType) { + + if ($webMenuTypeKey === self::PLACE) { + + $placesIds = collect($webMenuTypes[$webMenuTypeKey])->pluck('property_web_menu_id')->toArray(); + + $checkPropertyMappingRequest = [ + 'criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]], + "whereIn" => [["field" => "id", "value" => $placesIds]] + ]; + $propertyPlaces = $this->propertyPlaceService->select($checkPropertyMappingRequest, ['id', 'name', 'order_number', 'language_name']); + + if ($propertyPlaces['status'] != 'success') { + throw new ApiErrorException('Have a problem in Get Property Place '); + } + + $myWebMenuMapping = collect($webMenuTypes[$webMenuTypeKey])->keyBy('property_web_menu_id')->toArray(); + + foreach ($propertyPlaces['data'] as $propertyPlace) { + + $propertyPlace['order_number'] = $myWebMenuMapping[$propertyPlace['id']]['order_number']; + $propertyPlace['type'] = $myWebMenuMapping[$propertyPlace['id']]['type']; + $propertyPlace['menu_code'] = $myWebMenuMapping[$propertyPlace['id']]['menu_code']; + $propertyPlace['route'] = '/place/' . Str::slug($propertyPlace['name'], $separator = '-', $language = 'en') . '-' . $propertyPlace['id']; + $alias = $myWebMenuMapping[$propertyPlace['id']]['type'] . '-' . $propertyPlace['id']; + $propertyPlace['alias'] = $alias; + $propertyPlace['name'] = $this->appearanceTitle($menuTypeKey, $propertyPlace['name']); + $allData[$menuTypeKey][] = $propertyPlace; + } + + } elseif ($webMenuTypeKey === self::MAIN) { + + $myWebMenus = $this->propertyWebMenuService->getPropertyWebMenu(); + $webMenus = $myWebMenus['data']['property_web_menus']; + $webMenuLevelIds = collect($webMenuTypes[$webMenuTypeKey])->keyBy('property_web_menu_id')->toArray(); + foreach ($webMenus as $webMenu) { + if (isset($webMenuLevelIds[$webMenu['id']])) { + $webMenu['order_number'] = $webMenuLevelIds[$webMenu['id']]['order_number']; + $webMenu['name'] = $this->appearanceTitle($menuTypeKey, $webMenu['name']); + $allData[$menuTypeKey][] = $webMenu; + } + } + + } elseif ($webMenuTypeKey === self::PLACE_CATEGORY) { + + $placesIds = collect($webMenuTypes[$webMenuTypeKey])->pluck('property_web_menu_id')->toArray(); + $checkPropertyMappingRequest = [ + 'criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]], + "whereIn" => [["field" => "id", "value" => $placesIds]] + ]; + $propertyPlaceCategories = $this->propertyPlaceService->propertyPlaceCategories($checkPropertyMappingRequest, ['id', 'name', 'language_key', 'order_number']); + + if ($propertyPlaceCategories['status'] != 'success') { + throw new ApiErrorException('Have a problem in Get Property Place Category '); + } + + $myWebMenuMapping = collect($webMenuTypes[$webMenuTypeKey])->keyBy('property_web_menu_id')->toArray(); + + foreach ($propertyPlaceCategories['data'] as $propertyPlaceCategory) { + + $propertyPlaceCategory['order_number'] = $myWebMenuMapping[$propertyPlaceCategory['id']]['order_number']; + $propertyPlaceCategory['type'] = $myWebMenuMapping[$propertyPlaceCategory['id']]['type']; + $propertyPlaceCategory['menu_code'] = $myWebMenuMapping[$propertyPlaceCategory['id']]['menu_code']; + $propertyPlaceCategory['route'] = '/place-category/' . Str::slug($propertyPlaceCategory['name'], $separator = '-', $language = 'en') . '-' . $propertyPlaceCategory['id']; + $alias = $myWebMenuMapping[$propertyPlaceCategory['id']]['type'] . '-' . $propertyPlaceCategory['id']; + $propertyPlaceCategory['alias'] = $alias; + $propertyPlaceCategory['name'] = $this->appearanceTitle($menuTypeKey, $propertyPlaceCategory['name']); + $allData[$menuTypeKey][] = $propertyPlaceCategory; + } + } + + $allData[$menuTypeKey] = collect($allData[$menuTypeKey])->sortBy('order_number')->keyBy('alias')->toArray(); + } + + return $allData; + } + + private function appearanceTitle($type, $title) + { + if ($type === self::MENU_CODE_LVL1) { + $title = Str::upper($title); + } elseif ($type === self::MENU_CODE_TOP) { + $title = Str::title($title); + } + return $title; + } + + public function coverPhotos($params = []) + { + + $propertyWebPhotoMappingRequest = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')], + ['field' => 'property_web_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_web_id')], + ['field' => 'is_cover', 'condition' => '=', 'value' => 1], + ['field' => 'status', 'condition' => '=', 'value' => $params['mode'] == 'preview' ? 2 : 1], + ], + 'with' => ['propertyPhoto'] + ]; + $propertyWebMappingPhotos = $this->propertyWebService->selectPropertyWebPhotos($propertyWebPhotoMappingRequest); + $propertyWebMappingPhotos = $propertyWebMappingPhotos['status'] == 'success' && !empty($propertyWebMappingPhotos['status']) ? $propertyWebMappingPhotos['data'] : []; + + $coverPhotos = []; + + foreach ($propertyWebMappingPhotos as $key => $photo) { + $photoUrlFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_1024x768.' . $photo['property_photo']['file_ext']; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_200x200.' . $photo['property_photo']['file_ext']; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_1024x768.' . $photo['property_photo']['file_ext']; + } else { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_medium.' . $photo['property_photo']['file_ext']; + } + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_200x200.' . $photo['property_photo']['file_ext']; + } else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '_thumbnail.' . $photo['property_photo']['file_ext']; + } + $coverPhotos[$key]['default'] = Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' . $photo['property_photo']['photo_name'] . '.' . $photo['property_photo']['file_ext']; + $coverPhotos[$key]['fixed'] = $photoUrlFilePath; + $coverPhotos[$key]['thumb'] = $photoUrlThumbFilePath; + } + + return $coverPhotos; + + } + + public function propertyComparison(Request $request, $weekKey, $userId = null) + { + + $daysCount = [1, 15, 45, 90]; + $comparisonChannels = ['BookingEngine', 'Agoda.com', 'etstur.com', 'Booking.com']; + + $response = ['status' => false, 'message' => '', 'data' => null]; + + try { + + $userDetail = null; + $propertyPriceComparison = PropertyPriceComparison::where('week_key', $weekKey)->with('propertyDetail.propertyContractUser')->orderBy('property_id')->get(); + $propertyPriceComparison = $propertyPriceComparison ? $propertyPriceComparison->toArray() : []; + + + if (!empty($userId)) { + $propertyPriceComparison = collect($propertyPriceComparison)->where('property_detail.property_contract_user.id', $userId)->toArray(); + $userDetail = isset(reset($propertyPriceComparison)['property_detail']['property_contract_user']) ? reset($propertyPriceComparison)['property_detail']['property_contract_user'] : null; + } + + $response = [ + 'status' => true + ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + } + + if (!is_null($userId)) { + $pdf = $this->pdf + ->setOptions(['isRemoteEnabled' => false, 'isHtml5ParserEnabled' => true, 'isFontSubsettingEnabled' => true]) + //->setPaper('A4', 'landscape') + ->loadView('pdf.priceComparison', + compact('response', 'daysCount', 'comparisonChannels', 'propertyPriceComparison', 'userDetail'), + [], 'UTF8'); + + return $pdf->stream(); + + } + + + return view('pdf.priceComparison', compact('response', 'daysCount', 'comparisonChannels', 'propertyPriceComparison', 'userDetail')); + + } + +} diff --git a/app/Http/Controllers/V1/PropertyWebPopupController.php b/app/Http/Controllers/V1/PropertyWebPopupController.php new file mode 100644 index 0000000..f3ec515 --- /dev/null +++ b/app/Http/Controllers/V1/PropertyWebPopupController.php @@ -0,0 +1,255 @@ +request = $request; + $this->propertyWebPopupService = $propertyWebPopupService; + } + + public function getPropertyWebPopup(Request $request, $id = null) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $columns = ['id', 'property_id', 'title', 'language_code', 'start_date', 'end_date', 'url', 'image', 'status', 'created_at']; + $criteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']], + ], + 'orderBy' => [ + ['field' => 'id', 'value' => 'DESC'] + ] + ]; + + $availableFilterArray = ['title', 'language_code']; + if (!empty($params['filter'])) { + foreach ($params['filter'] as $filterKey => $filterValue) { + if (in_array($filterKey, $availableFilterArray)) { + if (in_array($filterKey, ['language_code', 'status'])) { + $criteria['criteria'][] = ['field' => $filterKey, 'condition' => '=', 'value' => $filterValue]; + } + + if (in_array($filterKey, ['title'])) { + $criteria['criteria'][] = ['field' => $filterKey, 'condition' => 'LIKE', 'value' => '%' . $filterValue . '%']; + } + + } + } + } + + if (!empty($id)) { + $criteria['criteria'][] = ['field' => 'id', 'condition' => '=', 'value' => $id]; + $criteria['firstRow'] = true; + } + + + $propertyWebPopup = $this->propertyWebPopupService->select($criteria, $columns); + + if ($propertyWebPopup['status'] != 'success') { + throw new Exception($propertyWebPopup['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWebPopup['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function createPropertyWebPopup(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'title' => fillOnUndefined($params, 'title'), + 'language_code' => fillOnUndefined($params, 'language_code'), + 'image' => fillOnUndefined($params, 'image'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + 'url' => fillOnUndefined($params, 'url'), + 'user_id' => $this->request->credentials->user_id + ]; + + + $propertyWebPopup = $this->propertyWebPopupService->create($requestParams); + + if ($propertyWebPopup['status'] != 'success') { + throw new ApiErrorException($propertyWebPopup['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWebPopup['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function updatePropertyWebPopup(Request $request, $id) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'title' => fillOnUndefined($params, 'title'), + 'language_code' => fillOnUndefined($params, 'language_code'), + 'image' => fillOnUndefined($params, 'image'), + 'start_date' => fillOnUndefined($params, 'start_date'), + 'end_date' => fillOnUndefined($params, 'end_date'), + 'url' => fillOnUndefined($params, 'url'), + 'updated_by' => $this->request->credentials->user_id + ]; + + $propertyWebPopup = $this->propertyWebPopupService->update($id, $requestParams); + + if ($propertyWebPopup['status'] != 'success') { + throw new ApiErrorException($propertyWebPopup['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWebPopup['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function deletePropertyWebPopup(Request $request, $id) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + try { + + if (is_null($this->request->getContent())) { + throw new ApiErrorException(lang('Parameter Error.')); + } + + $params = $this->request->params; + + $requestParams = [ + 'id' => $id, + 'property_id' => fillOnUndefined($params, 'property_id') + ]; + + $propertyWebPopup = $this->propertyWebPopupService->delete($id, $requestParams); + + if ($propertyWebPopup['status'] != 'success') { + throw new ApiErrorException($propertyWebPopup['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyWebPopup['data']]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + + } + + public function uploadPropertyWebPopupImage(Request $request) + { + $response = ['status' => false, 'statusCode' => 500, 'message' => '', 'data' => null]; + try { + if (!$request->hasFile('image')) { + throw new ApiErrorException(lang('Photos not found')); + } + + $param = [ + "property_id" => $request->input('property_id'), + "photo" => $request->file('image'), + ]; + + $response = $this->propertyWebPopupService->uploadPhoto($param); + + } catch (ApiErrorException $e) { + $response['message'] = $e->getMessage(); + + } catch (Exception $e) { + $message = $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage(); + Log::error($message); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + } + + +} diff --git a/app/Http/Controllers/V1/ReputationManagementController.php b/app/Http/Controllers/V1/ReputationManagementController.php new file mode 100644 index 0000000..723015f --- /dev/null +++ b/app/Http/Controllers/V1/ReputationManagementController.php @@ -0,0 +1,570 @@ +params = Input::all(); + $this->params = $this->params['params']; + + $this->reputationManagementService = $reputationManagementService; + } + + + public function getChannel(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + $criteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $reputationManagementChannel = $this->reputationManagementService->selectChannel($criteria, ['id', 'name', 'logo', 'parameter', 'logo']); + $reputationManagementChannel = $reputationManagementChannel['status'] == 'success' && !empty($reputationManagementChannel['data']) ? $reputationManagementChannel['data'] : []; + + + $reputationManagementMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['fetchStatus'], + ]; + + $reputationManagementMapping = $this->reputationManagementService->selectChannelMapping($reputationManagementMappingCriteria); + $reputationManagementMapping = $reputationManagementMapping['status'] == 'success' && !empty($reputationManagementMapping['data']) ? $reputationManagementMapping['data'] : []; + $reputationManagementMappingCollect = collect($reputationManagementMapping); + + + $reputationManagementList = []; + foreach ($reputationManagementChannel as $reputationManagementChannelKey => $reputationManagementChannel) { + + $reputationManagementMapping = $reputationManagementMappingCollect->where('channel_id', $reputationManagementChannel['id'])->first(); + + + $reputationManagementDetail = null; + $isSelected = $reputationManagementMapping ? true : false; + + if ($reputationManagementMapping) { + $reputationManagementDetail = [ + 'fetchStatus' => $reputationManagementMapping['fetch_status'], + 'parameterArray' => $reputationManagementMapping['parameterArray'] + ]; + } + + $reputationManagementList[] = [ + //'code' => $webComponent['code'], + 'id' => $reputationManagementChannel['id'], + 'name' => $reputationManagementChannel['name'], + 'logo' => $reputationManagementChannel['logo'], + 'logoUrl' => $reputationManagementChannel['logoUrl'], + 'parameterArray' => $reputationManagementChannel['parameterArray'], + 'is_selected' => $isSelected, + 'channelDetail' => $reputationManagementDetail + ]; + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $reputationManagementList]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function syncChannel(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + + $criteria = [ + 'criteria' => [ + ['field' => 'id', 'condition' => '=', 'value' => $this->params['channel_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $reputationManagementChannel = $this->reputationManagementService->selectChannel($criteria, ['id', 'name', 'logo', 'parameter', 'logo']); + $reputationManagementChannel = $reputationManagementChannel['status'] == 'success' && !empty($reputationManagementChannel['data']) ? $reputationManagementChannel['data'] : []; + + if (empty($reputationManagementChannel)) { + throw new ApiErrorException(lang('Channel data not found')); + } + + //Parameter Check + if (!empty($this->params['channelDetail'])) { + $parameterCheck = array_diff(array_keys($reputationManagementChannel['parameterArray']), array_keys($this->params['channelDetail']['parameter'])); + if (!empty($parameterCheck)) { + throw new ApiErrorException(lang('Missing or incorrect parameter')); + } + } + + + $reputationManagementChannelMappingCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($this->params, 'property_id')], + ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($this->params, 'channel_id')], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => true + ]; + + $reputationManagementChannelMapping = $this->reputationManagementService->selectChannelMapping($reputationManagementChannelMappingCriteria); + $reputationManagementChannelMapping = $reputationManagementChannelMapping['status'] == 'success' && !empty($reputationManagementChannelMapping['data']) ? $reputationManagementChannelMapping['data'] : []; + + //Parameter Check + + + DB::beginTransaction(); + + if ($reputationManagementChannelMapping) { + + + //Remove + if (empty($this->params['channelDetail'])) { + + $syncReputationManagementChannelMapping = $this->reputationManagementService->deleteChannelMapping($reputationManagementChannelMapping['id']); + if ($syncReputationManagementChannelMapping['status'] != 'success') { + throw new Exception('api-unknown_error'); + } + + } else { + + //Update + $updateParam = [ + 'property_id' => fillOnUndefined($this->params, 'property_id'), + 'channel_id' => fillOnUndefined($this->params, 'channel_id'), + 'parameter' => fillOnUndefined($this->params['channelDetail'], 'parameter') ? json_encode($this->params['channelDetail']['parameter']) : null, + 'fetch_status' => fillOnUndefined($reputationManagementChannelMapping, 'fetch_status'), + 'updated_by' => $request->auth->id, + ]; + + $syncReputationManagementChannelMapping = $this->reputationManagementService->updateChannelMapping($reputationManagementChannelMapping['id'], $updateParam); + + if ($syncReputationManagementChannelMapping['status'] != 'success') { + throw new ApiErrorException($syncReputationManagementChannelMapping['message']); + } + + } + + } else { + + $createParam = [ + 'property_id' => fillOnUndefined($this->params, 'property_id'), + 'channel_id' => fillOnUndefined($this->params, 'channel_id'), + 'parameter' => fillOnUndefined($this->params['channelDetail'], 'parameter') ? json_encode($this->params['channelDetail']['parameter']) : null, + 'fetch_status' => 3, + 'status' => 1, + 'created_by' => $request->auth->id, + ]; + + $syncReputationManagementChannelMapping = $this->reputationManagementService->createChannelMapping($createParam); + + if ($syncReputationManagementChannelMapping['status'] != 'success') { + throw new ApiErrorException($syncReputationManagementChannelMapping['message']); + } + + //First Fetch Review + dispatch(new PropertyReviewServiceJob($createParam['property_id'], $createParam['channel_id'])); + + } + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + if ($response['status']) { + DB::commit(); + } else { + DB::rollBack(); + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function getReview(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + + $propertyReviewCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel', 'channel', 'categoryMapping.category'], + 'orderBy' => [ + ['field' => 'review_date', 'value' => 'DESC'] + ], + ]; + + + if (isset($this->params['filter']) && !empty(isset($this->params['filter']))) { + + if (isset($this->params['filter']['channel_id'])) { + $propertyReviewCriteria['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['filter']['channel_id']]; + } + + if (isset($this->params['filter']['start_date'])) { + $propertyReviewCriteria['criteria'][] = ['field' => 'review_date', 'condition' => '>', 'value' => $this->params['filter']['start_date']]; + } + + if (isset($this->params['filter']['end_date'])) { + $propertyReviewCriteria['criteria'][] = ['field' => 'review_date', 'condition' => '<=', 'value' => $this->params['filter']['end_date']]; + } + + if (isset($this->params['filter']['sentiment'])) { + $propertyReviewCriteria['criteria'][] = ['field' => 'sentiment', 'condition' => '=', 'value' => $this->params['filter']['sentiment']]; + } + + + if (isset($this->params['filter']['keyword'])) { + $propertyReviewCriteria['whereOr'][] = ['field' => 'author', 'condition' => 'LIKE', 'value' => '%' . $this->params['filter']['keyword'] . '%']; + $propertyReviewCriteria['whereOr'][] = ['field' => 'title', 'condition' => 'LIKE', 'value' => '%' . $this->params['filter']['keyword'] . '%']; + $propertyReviewCriteria['whereOr'][] = ['field' => 'review', 'condition' => 'LIKE', 'value' => '%' . $this->params['filter']['keyword'] . '%']; + } + + + } + + //Just Last 1 Year + $propertyReviewCriteria['criteria'][] = ['field' => 'review_date', 'condition' => '>', 'value' => Carbon::now()->subYear()->toDateString()]; + + + $propertyReviewCriteriaCount = $propertyReviewCriteria; + $propertyReviewCriteriaCount['count'] = true; + $propertyReviewCount = $this->reputationManagementService->select($propertyReviewCriteriaCount, ['id']); + + + $propertyReviewCriteria['skip'] = fillOnUndefined($this->params, 'page', 0) ? ($this->params['page'] - 1) : 0; + $propertyReviewCriteria['take'] = fillOnUndefined($this->params, 'per_page', 20); + $propertyReviewCriteria['skip'] = $propertyReviewCriteria['skip'] * $propertyReviewCriteria['take']; + + $column = ['id', 'property_id', 'channel_id', 'author', 'title', 'review', 'review_date', 'rating', 'top_rating', 'score', 'sentiment', 'language']; + $propertyReview = $this->reputationManagementService->select($propertyReviewCriteria, $column); + $propertyReview = $propertyReview['status'] == 'success' && !empty($propertyReview['data']) ? $propertyReview['data'] : []; + + + $propertyReviewList = [ + 'total' => $propertyReviewCount['data'], + 'page' => $this->params['page'], + 'count' => count($propertyReview), + 'review' => $propertyReview + ]; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyReviewList]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + public function getReviewStatistics(Request $request) + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500]; + + try { + + + $propertyReviewCriteria = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $this->params['property_id']], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel', 'channel', 'categoryMapping.category', 'keywordMapping'], + 'orderBy' => [ + ['field' => 'review_date', 'value' => 'DESC'] + ], + ]; + + if (isset($this->params['filter']) && !empty(isset($this->params['filter']))) { + + if (isset($this->params['filter']['channel_id'])) { + $propertyReviewCriteria['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => $this->params['filter']['channel_id']]; + } + + if (isset($this->params['filter']['start_date'])) { + $propertyReviewCriteria['criteria'][] = ['field' => 'review_date', 'condition' => '>', 'value' => $this->params['filter']['start_date']]; + } + + if (isset($this->params['filter']['end_date'])) { + $propertyReviewCriteria['criteria'][] = ['field' => 'review_date', 'condition' => '<=', 'value' => $this->params['filter']['end_date']]; + } + + } + + //Just Last 1 Year + $propertyReviewCriteria['criteria'][] = ['field' => 'review_date', 'condition' => '>', 'value' => Carbon::now()->subYear()->toDateString()]; + + + $column = ['id', 'property_id', 'channel_id', 'review_date', 'rating', 'top_rating', 'score', 'sentiment', 'language']; + $propertyReview = $this->reputationManagementService->select($propertyReviewCriteria, $column); + $propertyReview = $propertyReview['status'] == 'success' && !empty($propertyReview['data']) ? $propertyReview['data'] : []; + + + $propertyReviewStatistics['reviewScore'] = collect($propertyReview)->average('score'); + $propertyReviewStatistics['reviewScore'] = (float)number_format($propertyReviewStatistics['reviewScore'], 2, '.', ''); + + + $reviewSentimentGroup = collect($propertyReview)->groupBy('sentiment')->map->count(); + $reviewSentimentGroup = $reviewSentimentGroup ? $reviewSentimentGroup->toArray() : null; + + $reviewSentiment['positive'] = isset($reviewSentimentGroup[1]) ? $reviewSentimentGroup[1] : 0; + $reviewSentiment['negative'] = isset($reviewSentimentGroup[0]) ? $reviewSentimentGroup[0] : 0; + + $propertyReviewStatistics['reviewScoreBySystem'] = null; + if (($reviewSentiment['positive'] + $reviewSentiment['negative']) > 0) { + $propertyReviewStatistics['reviewScoreBySystem'] = $reviewSentiment['positive'] / ($reviewSentiment['positive'] + $reviewSentiment['negative']) * 100; + $propertyReviewStatistics['reviewScoreBySystem'] = (float)number_format($propertyReviewStatistics['reviewScoreBySystem'], 2, '.', ''); + } + + + //$reviewCategoryScoreBySystem + $reviewCategoryMapping = []; + $reviewCategoryScoreBySystem = []; + $reviewKeywordScoreBySystem = []; + foreach ($propertyReview as $review) { + + foreach ($review['category_mapping'] as $reviewCategory) { + if (!isset($reviewCategoryScoreBySystem[$reviewCategory['category_id']])) { + $reviewCategoryScoreBySystem[$reviewCategory['category_id']][0] = 0; + $reviewCategoryScoreBySystem[$reviewCategory['category_id']][1] = 0; + } + $reviewCategoryScoreBySystem[$reviewCategory['category_id']][$reviewCategory['sentiment']]++; + + $reviewCategoryMapping[$reviewCategory['category_id']]['name'] = $reviewCategory['category']['name']; + $reviewCategoryMapping[$reviewCategory['category_id']]['score'][$review['review_date']] = $review['score']; + + if (!isset($reviewCategoryMapping[$reviewCategory['category_id']]['sentimentScore'][Carbon::parse($review['review_date'])->format('Y-m')])) { + $reviewCategoryMapping[$reviewCategory['category_id']]['sentimentScore'][Carbon::parse($review['review_date'])->format('Y-m')][0] = 0; + $reviewCategoryMapping[$reviewCategory['category_id']]['sentimentScore'][Carbon::parse($review['review_date'])->format('Y-m')][1] = 0; + } + + $reviewCategoryMapping[$reviewCategory['category_id']]['sentimentScore'][Carbon::parse($review['review_date'])->format('Y-m')][$reviewCategory['sentiment']]++; + + } + + foreach ($review['keyword_mapping'] as $reviewKeyword) { + if (!isset($reviewKeywordScoreBySystem[md5($reviewKeyword['keyword'])])) { + $reviewKeywordScoreBySystem[md5($reviewKeyword['keyword'])]['positive'] = 0; + $reviewKeywordScoreBySystem[md5($reviewKeyword['keyword'])]['negative'] = 0; + $reviewKeywordScoreBySystem[md5($reviewKeyword['keyword'])]['keyword'] = $reviewKeyword['keyword']; + } + + if ($reviewKeyword['sentiment'] == 1) { + $reviewKeywordScoreBySystem[md5($reviewKeyword['keyword'])]['positive']++; + } elseif ($reviewKeyword['sentiment'] == 0) { + $reviewKeywordScoreBySystem[md5($reviewKeyword['keyword'])]['negative']++; + } + + } + } + + + $propertyReviewCategoryCriteria = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ] + ]; + + $propertyReviewCategory = $this->reputationManagementService->selectCategory($propertyReviewCategoryCriteria); + $propertyReviewCategory = $propertyReviewCategory['status'] == 'success' ? $propertyReviewCategory['data'] : []; + + $propertyReviewCategoryScore = []; + foreach ($propertyReviewCategory as $reviewCategory) { + if (empty($reviewCategoryScoreBySystem)) { + continue; + } + if (isset($reviewCategoryScoreBySystem[$reviewCategory['id']]) && array_sum($reviewCategoryScoreBySystem[$reviewCategory['id']]) < 5) { + continue; + } + $propertyReviewCategoryScore[$reviewCategory['id']] = [ + 'name' => $reviewCategory['name'], + 'language_key' => $reviewCategory['language_key'], + ]; + + $propertyReviewCategoryScore[$reviewCategory['id']]['score'] = null; + if (isset($reviewCategoryScoreBySystem[$reviewCategory['id']])) { + + $propertyReviewCategoryScore[$reviewCategory['id']]['score'] = (float)number_format($reviewCategoryScoreBySystem[$reviewCategory['id']][1] / array_sum($reviewCategoryScoreBySystem[$reviewCategory['id']]) * 100, 2, '.', ''); + $propertyReviewCategoryScore[$reviewCategory['id']]['positive'] = $reviewCategoryScoreBySystem[$reviewCategory['id']][1]; + $propertyReviewCategoryScore[$reviewCategory['id']]['negative'] = $reviewCategoryScoreBySystem[$reviewCategory['id']][0]; + $propertyReviewCategoryScore[$reviewCategory['id']]['total'] = array_sum($reviewCategoryScoreBySystem[$reviewCategory['id']]); + + + krsort($reviewCategoryMapping[$reviewCategory['id']]['score']); + $propertyReviewCategoryScore[$reviewCategory['id']]['scoreLastNumber'] = array_slice($reviewCategoryMapping[$reviewCategory['id']]['score'], 0, 10); + + //Group By Month + /*$propertyReviewCategoryGroupScore = []; + foreach ($reviewCategoryMapping[$reviewCategory['id']]['score'] as $reviewDateTime => $reviewScore) { + $propertyReviewCategoryGroupScore[Carbon::parse($reviewDateTime)->format('m-Y')][] = $reviewScore; + } + $propertyReviewCategoryGroupScoreAverage = []; + foreach ($propertyReviewCategoryGroupScore as $reviewPeriod => $reviewPeriodScore) { + $propertyReviewCategoryGroupScoreAverage[$reviewPeriod] = round(collect($reviewPeriodScore)->avg()); + }*/ + //Group By Month + + + //Group By Month + ksort($reviewCategoryMapping[$reviewCategory['id']]['sentimentScore']); + $propertyReviewCategoryGroupScoreAverage = []; + foreach ($reviewCategoryMapping[$reviewCategory['id']]['sentimentScore'] as $reviewPeriod => $reviewPeriodScore) { + $categorySentimentScore = (float)number_format($reviewPeriodScore[1] / array_sum($reviewPeriodScore) * 100, 2, '.', ''); + if ($categorySentimentScore <= 0) { + continue; + } + $propertyReviewCategoryGroupScoreAverage[$reviewPeriod] = round($categorySentimentScore); + } + //Group By Month + + $propertyReviewCategoryScore[$reviewCategory['id']]['scoreMonthlyGroup'] = $propertyReviewCategoryGroupScoreAverage; + $propertyReviewCategoryScore[$reviewCategory['id']]['scoreMonthlyGroupAverage'] = (float)number_format(collect($propertyReviewCategoryGroupScoreAverage)->average(), 2, '.', ''); + + } + + } + $propertyReviewStatistics['reviewCategoryScoreBySystem'] = array_values($propertyReviewCategoryScore); + //$reviewCategoryScoreBySystem + + + foreach ($reviewKeywordScoreBySystem as $reviewKeywordKey => $reviewKeyword) { + if (($reviewKeyword['positive'] + $reviewKeyword['negative']) < 3) { + unset($reviewKeywordScoreBySystem[$reviewKeywordKey]); + continue; + } + if (isset($reviewKeywordScoreBySystem[$reviewKeywordKey])) { + $reviewKeywordScoreBySystem[$reviewKeywordKey]['score'] = (float)number_format($reviewKeywordScoreBySystem[$reviewKeywordKey]['positive'] / array_sum($reviewKeywordScoreBySystem[$reviewKeywordKey]) * 100, 2, '.', ''); + } + } + + $propertyReviewStatistics['reviewKeywordScoreBySystem'] = array_values($reviewKeywordScoreBySystem); + + + //Dashboard Summary + $propertyReviewStatistics['dashboard'] = []; + $currentWeekStart = Carbon::now()->startOfWeek(Carbon::MONDAY)->toDateString(); + $currentWeekFinish = Carbon::now()->addWeek()->startOfWeek(Carbon::MONDAY)->toDateString(); + + //TOTAL + $totalReview = count($propertyReview); + $extraReviewCount = collect($propertyReview)->where('review_date', '>', $currentWeekStart)->count(); + $propertyReviewStatistics['dashboard'] ['total'] = [ + 'count' => $totalReview, + 'extraReviewCount' => $extraReviewCount + ]; + + //POSITIVE + $totalReviewPositive = collect($propertyReview)->where('sentiment', 1)->count(); + $extraPositiveReviewCount = collect($propertyReview)->where('review_date', '>', $currentWeekStart)->where('sentiment', 1)->count(); + $propertyReviewStatistics['dashboard'] ['positive'] = [ + 'count' => $totalReviewPositive, + 'extraReviewCount' => $extraPositiveReviewCount + ]; + + //NEGATIVE + $totalReviewNegative = collect($propertyReview)->where('sentiment', '!=', 1)->count(); + $extraNegativeReviewCount = collect($propertyReview)->where('review_date', '>', $currentWeekStart)->where('sentiment', '!=', 1)->count(); + $propertyReviewStatistics['dashboard'] ['negative'] = [ + 'count' => $totalReviewNegative, + 'extraReviewCount' => $extraNegativeReviewCount + ]; + + //BEST + $bestReview = collect($propertyReviewCategoryScore)->sortByDesc('positive')->first(); + $propertyReviewStatistics['dashboard'] ['best'] = [ + 'name' => $bestReview['name'], + 'language_key' => $bestReview['language_key'], + 'score' => $bestReview['score'], + 'positive' => $bestReview['positive'], + 'negative' => $bestReview['negative'], + 'total' => $bestReview['total'], + ]; + + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $propertyReviewStatistics]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']); + + } + + +} + diff --git a/app/Http/Controllers/V1/TempPropertyController.php b/app/Http/Controllers/V1/TempPropertyController.php new file mode 100644 index 0000000..2230efa --- /dev/null +++ b/app/Http/Controllers/V1/TempPropertyController.php @@ -0,0 +1,67 @@ +request = $request; + $this->tempPropertyService = $tempPropertyService ; + + } + + + public function getTempPropertyList() + { + + $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 400 ]; + try { + + if (is_null($this->request->getContent())) { + throw new Exception(lang('Parameter Error.')) ; + } + $tempPropertyListCriteria = $this->request->getContent(); + $tempPropertyListCriteria = json_decode($tempPropertyListCriteria,1); + $tempPropertyListCriteria = $tempPropertyListCriteria['params']; + + $temPropertyList = $this->tempPropertyService->search($tempPropertyListCriteria, fillOnUndefined($tempPropertyListCriteria, 'select', ['*'])); + if ($temPropertyList['status'] != 'success') { + throw new Exception($temPropertyList['message']); + } + + $response = ['status' => 1, 'statusCode' => 200, 'message' => null,'data' => $temPropertyList['data'] ]; + + } catch (ApiErrorException $e) { + $response['message'] = implode(', ', $e->getMessageArr()); + $response['statusCode'] = 400; + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error($message); + $response['message'] = $e->getMessage(); + $response['statusCode'] = 500; + } + return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);; + + } +} diff --git a/app/Http/Controllers/bulutController.backup.php b/app/Http/Controllers/bulutController.backup.php new file mode 100644 index 0000000..93e5c0f --- /dev/null +++ b/app/Http/Controllers/bulutController.backup.php @@ -0,0 +1,364 @@ + 15, + 'headers' => array( + 'x-rapidapi-host' => env('BULUT_RAPIDAPI_HOST'), + 'x-rapidapi-key' => env('BULUT_RAPIDAPI_KEY'), + ), + )); + } + + /** + * ADIM 1: Otel adına göre ara → hotel_id al + * GET /bulut/search?name=Grand Yavuz Hotel Istanbul + */ + public function search(Request $request) + { + $name = $request->query('name', ''); + + if (!$name) { + return response()->json(array('status' => false, 'message' => 'name parametresi gerekli'), 422); + } + + try { + $response = $this->client()->get( + 'https://' . env('BULUT_RAPIDAPI_HOST') . '/locations/auto-complete', + array('query' => array('text' => $name, 'languagecode' => 'en-us')) + ); + + $data = json_decode($response->getBody()->getContents(), true); + + return response()->json(array('status' => true, 'raw' => $data)); + } catch (Exception $e) { + Log::error('search error: ' . $e->getMessage()); + return response()->json(array('status' => false, 'message' => $e->getMessage()), 500); + } + } + + /** + * ADIM 2: Otel detayları + * GET /bulut/hotel-data?hotel_id=1234567&checkin=2026-06-01&checkout=2026-06-02 + */ + public function hotelData(Request $request) + { + $hotelId = $request->query('hotel_id'); + $checkin = $request->query('checkin'); + $checkout = $request->query('checkout'); + + if (!$hotelId) { + return response()->json(array('status' => false, 'message' => 'hotel_id gerekli'), 422); + } + + try { + $response = $this->client()->get( + 'https://' . env('BULUT_RAPIDAPI_HOST') . '/properties/detail', + array('query' => array( + 'hotel_id' => $hotelId, + 'arrival_date' => $checkin, + 'departure_date' => $checkout, + 'languagecode' => 'en-us', + 'currency_code' => 'USD', + )) + ); + + $data = json_decode($response->getBody()->getContents(), true); + return response()->json(array('status' => true, 'data' => $data)); + } catch (Exception $e) { + Log::error('hotelData error: ' . $e->getMessage()); + return response()->json(array('status' => false, 'message' => $e->getMessage()), 500); + } + } + + public function hotelPhotos(Request $request) + { + $hotelIds = $request->query('hotel_ids'); + $hotelName = $request->query('hotel_name', 'hotel'); + $propertyId = $request->query('property_id', $hotelIds); + + if (!$hotelIds) { + return response()->json(array( + 'status' => false, + 'message' => 'hotel_ids gerekli' + ), 422); + } + + try { + $response = $this->client()->get( + 'https://' . env('BULUT_RAPIDAPI_HOST') . '/properties/get-hotel-photos', + array( + 'query' => array( + 'hotel_ids' => $hotelIds, + 'languagecode' => 'en-us', + ) + ) + ); + + $data = json_decode($response->getBody()->getContents(), true); + + $savedImages = $this->saveHotelPhotosFromResponse($data, $hotelName, $propertyId); + Log::info('BULUT RAW RESPONSE KEYS', array( + 'main_keys' => array_keys($data), + 'data_keys' => isset($data['data']) && is_array($data['data']) ? array_keys($data['data']) : array(), + + )); + + return response()->json(array( + 'status' => true, + 'message' => 'Otel görselleri başarıyla indirildi ve gruplandı.', + 'debug' => array( + 'main_keys' => array_keys($data), + 'data_keys' => isset($data['data']) && is_array($data['data']) ? array_keys($data['data']) : array(), + 'data_data_keys' => isset($data['data']['data']) && is_array($data['data']['data']) ? array_keys($data['data']['data']) : array(), + ), + 'saved_images' => $savedImages + )); + } catch (Exception $e) { + Log::error('hotelPhotos error: ' . $e->getMessage()); + + return response()->json(array( + 'status' => false, + 'message' => $e->getMessage() + ), 500); + } + } + + private function saveHotelPhotosFromResponse(array $responseData, string $hotelName, $propertyId) + { + $result = array(); + + $urlPrefix = 'https://cf.bstatic.com'; + + if (isset($responseData['url_prefix'])) { + $urlPrefix = $responseData['url_prefix']; + } + + if (isset($responseData['data']['url_prefix'])) { + $urlPrefix = $responseData['data']['url_prefix']; + } + + $hotels = $this->extractHotelsFromPhotoResponse($responseData); + + Log::info('BULUT PHOTO HOTELS EXTRACTED', array( + 'hotel_count' => count($hotels), + 'hotel_keys' => array_keys($hotels), + 'url_prefix' => $urlPrefix + )); + + $uploadRootPath = rtrim(Config::get('app.fileSystemDriver'), '/'); + + $propertyBasePath = $uploadRootPath . '/property-photos/' . $propertyId; + + if (!File::exists($propertyBasePath)) { + File::makeDirectory($propertyBasePath, 0777, true); + } + + foreach ($hotels as $hotelId => $photos) { + $hotelFolderName = Str::slug($hotelName) . '-' . $hotelId; + + $basePath = $propertyBasePath . '/bulut/' . $hotelFolderName; + + if (!File::exists($basePath)) { + File::makeDirectory($basePath, 0777, true); + } + + $result[$hotelId] = array( + 'hotel_name' => $hotelName, + 'property_id' => $propertyId, + 'folder' => '/property-photos/' . $propertyId . '/bulut/' . $hotelFolderName, + 'physical_path' => $basePath, + 'groups' => array() + ); + + foreach ($photos as $photo) { + $photoId = isset($photo[2]) ? $photo[2] : uniqid(); + $tags = isset($photo[3]) ? $photo[3] : array(); + $imagePath = isset($photo[4]) ? $photo[4] : null; + + if (!$imagePath) { + continue; + } + + $groupName = $this->detectPhotoGroup($tags); + + $groupPath = $basePath . '/' . $groupName; + + if (!File::exists($groupPath)) { + File::makeDirectory($groupPath, 0777, true); + } + + $imageUrl = $urlPrefix . $imagePath; + + $fileName = $photoId . '.jpg'; + $savePath = $groupPath . '/' . $fileName; + + try { + $this->downloadImage($imageUrl, $savePath); + + $relativePath = '/property-photos/' . $propertyId . '/bulut/' . $hotelFolderName . '/' . $groupName . '/' . $fileName; + + if (!isset($result[$hotelId]['groups'][$groupName])) { + $result[$hotelId]['groups'][$groupName] = array(); + } + + $result[$hotelId]['groups'][$groupName][] = array( + 'photo_id' => $photoId, + 'file_name' => $fileName, + 'photo_path' => '/property-photos/' . $propertyId . '/bulut/' . $hotelFolderName . '/' . $groupName . '/', + 'relative_path' => $relativePath, + 'physical_path' => $savePath, + 'source_url' => $imageUrl, + 'tags' => $tags + ); + } catch (Exception $e) { + Log::error('BULUT IMAGE DOWNLOAD ERROR: ' . $e->getMessage(), array( + 'image_url' => $imageUrl, + 'save_path' => $savePath + )); + } + } + } + + return $result; + } + + private function detectPhotoGroup(array $tags) + { + $tagNames = array(); + + foreach ($tags as $tag) { + if (isset($tag['tag'])) { + $tagNames[] = strtolower($tag['tag']); + } + } + + $tagText = implode(' ', $tagNames); + + if ( + strpos($tagText, 'room') !== false || + strpos($tagText, 'bedroom') !== false || + strpos($tagText, 'bed') !== false || + strpos($tagText, 'living room') !== false + ) { + return 'rooms'; + } + + if ( + strpos($tagText, 'bathroom') !== false || + strpos($tagText, 'shower') !== false || + strpos($tagText, 'toilet') !== false + ) { + return 'bathroom'; + } + + if ( + strpos($tagText, 'breakfast') !== false || + strpos($tagText, 'buffet') !== false + ) { + return 'breakfast'; + } + + if ( + strpos($tagText, 'restaurant') !== false || + strpos($tagText, 'food') !== false || + strpos($tagText, 'drinks') !== false || + strpos($tagText, 'lunch') !== false || + strpos($tagText, 'dinner') !== false + ) { + return 'restaurant'; + } + + if ( + strpos($tagText, 'lobby') !== false || + strpos($tagText, 'reception') !== false || + strpos($tagText, 'lounge') !== false || + strpos($tagText, 'seating area') !== false + ) { + return 'lobby'; + } + + if ( + strpos($tagText, 'spa') !== false || + strpos($tagText, 'wellness') !== false || + strpos($tagText, 'public bath') !== false + ) { + return 'spa'; + } + + if ( + strpos($tagText, 'balcony') !== false || + strpos($tagText, 'terrace') !== false || + strpos($tagText, 'garden') !== false || + strpos($tagText, 'sea view') !== false || + strpos($tagText, 'city view') !== false + ) { + return 'view-and-outdoor'; + } + + if ( + strpos($tagText, 'property building') !== false || + strpos($tagText, 'property') !== false + ) { + return 'property'; + } + + return 'other'; + } + + private function downloadImage(string $imageUrl, string $savePath) + { + $client = new Client(array( + 'timeout' => 30, + 'verify' => false, + 'headers' => array( + 'User-Agent' => 'Mozilla/5.0' + ) + )); + + $response = $client->get($imageUrl); + + if ($response->getStatusCode() !== 200) { + throw new Exception('Image download failed: ' . $imageUrl); + } + + File::put($savePath, $response->getBody()->getContents()); + + return true; + } + + private function extractHotelsFromPhotoResponse(array $responseData) + { + if (isset($responseData['data']) && is_array($responseData['data'])) { + foreach ($responseData['data'] as $key => $value) { + if (is_numeric($key) && is_array($value)) { + return $responseData['data']; + } + } + } + + if (isset($responseData['data']['data']) && is_array($responseData['data']['data'])) { + return $responseData['data']['data']; + } + + if (isset($responseData['data']['data']['data']) && is_array($responseData['data']['data']['data'])) { + return $responseData['data']['data']['data']; + } + + return array(); + } +} diff --git a/app/Http/Controllers/bulutController.php b/app/Http/Controllers/bulutController.php new file mode 100644 index 0000000..71d4b5b --- /dev/null +++ b/app/Http/Controllers/bulutController.php @@ -0,0 +1,1079 @@ + 30, + 'headers' => array( + 'x-rapidapi-host' => env('BULUT_RAPIDAPI_HOST', self::API_HOST), + 'x-rapidapi-key' => env('BULUT_RAPIDAPI_KEY'), + 'Content-Type' => 'application/json', + ), + )); + } + + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 1: Otel adına göre ara → hotel_id bul + // GET /bulut/search?name=Grand+Yavuz+Hotel + // ───────────────────────────────────────────────────────────────────────────── + public function search(Request $request) + { + $name = $request->query('name', ''); + + if (!$name) { + return response()->json(array('status' => false, 'message' => 'name parametresi gerekli'), 422); + } + + try { + $response = $this->client()->get( + self::API_BASE . '/api/v1/hotels/searchDestination', + array('query' => array('query' => $name)) + ); + + $data = json_decode($response->getBody()->getContents(), true); + + return response()->json(array('status' => true, 'data' => $data)); + } catch (Exception $e) { + Log::error('Bulut.search error: ' . $e->getMessage()); + return response()->json(array('status' => false, 'message' => $e->getMessage()), 500); + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 2: Otel detayları + // GET /bulut/hotel-data?hotel_id=89675&checkin=2026-06-01&checkout=2026-06-02 + // ───────────────────────────────────────────────────────────────────────────── + public function hotelData(Request $request) + { + $hotelId = $request->query('hotel_id'); + $checkin = $request->query('checkin', date('Y-m-d', strtotime('+30 days'))); + $checkout = $request->query('checkout', date('Y-m-d', strtotime('+31 days'))); + + if (!$hotelId) { + return response()->json(array('status' => false, 'message' => 'hotel_id gerekli'), 422); + } + + try { + $response = $this->client()->get( + self::API_BASE . '/api/v1/hotels/getHotelDetails', + array('query' => array( + 'hotel_id' => $hotelId, + 'arrival_date' => $checkin, + 'departure_date' => $checkout, + 'adults' => 1, + 'room_qty' => 1, + 'languagecode' => 'en-us', + 'currency_code' => 'USD', + 'units' => 'metric', + 'temperature_unit' => 'c', + )) + ); + + $data = json_decode($response->getBody()->getContents(), true); + return response()->json(array('status' => true, 'data' => $data)); + } catch (Exception $e) { + Log::error('Bulut.hotelData error: ' . $e->getMessage()); + return response()->json(array('status' => false, 'message' => $e->getMessage()), 500); + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 3: Otel fotoğraflarını indir → property_photo tablosuna kaydet + // GET /bulut/hotel-photos?hotel_id=89675&property_id=1 + // ───────────────────────────────────────────────────────────────────────────── + public function hotelPhotos(Request $request) + { + $hotelId = $request->query('hotel_id'); + $propertyId = $request->query('property_id', 1); + $limit = (int) $request->query('limit', 20); + + if (!$hotelId) { + return response()->json(array('status' => false, 'message' => 'hotel_id gerekli'), 422); + } + + try { + $response = $this->client()->get( + self::API_BASE . '/api/v1/hotels/getHotelPhotos', + array('query' => array( + 'hotel_id' => $hotelId, + 'page_number' => 1, + )) + ); + + $data = json_decode($response->getBody()->getContents(), true); + + $savedPhotos = $this->processAndSavePhotos($data, $hotelId, $propertyId, $limit); + + return response()->json(array( + 'status' => true, + 'hotel_id' => $hotelId, + 'property_id' => $propertyId, + 'photos_saved' => $savedPhotos, + )); + } catch (Exception $e) { + Log::error('Bulut.hotelPhotos error: ' . $e->getMessage()); + return response()->json(array('status' => false, 'message' => $e->getMessage()), 500); + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // TAM PIPELINE: otel adı gönder → ara → detay + fotoğrafları kaydet + // GET /bulut/test-api?name=Grand+Yavuz+Hotel&property_id=1&limit=5 + // ───────────────────────────────────────────────────────────────────────────── + public function testApi(Request $request) + { + $name = $request->query('name', ''); + $propertyId = $request->query('property_id', 1); + $checkin = $request->query('checkin', date('Y-m-d', strtotime('+30 days'))); + $checkout = $request->query('checkout', date('Y-m-d', strtotime('+31 days'))); + $limit = (int) $request->query('limit', 5); + + if (!$name) { + return response()->json(array('status' => false, 'message' => 'name parametresi gerekli'), 422); + } + + try { + // ── 1. Ara ─────────────────────────────────────────────────────────── + $searchResponse = $this->client()->get( + self::API_BASE . '/api/v1/hotels/searchDestination', + array('query' => array('query' => $name)) + ); + $searchData = json_decode($searchResponse->getBody()->getContents(), true); + + if (empty($searchData['data'])) { + return response()->json(array( + 'status' => false, + 'message' => 'Otel bulunamadı', + 'raw' => $searchData + ), 404); + } + + // Önce dest_type=hotel olanı bul, yoksa ilk sonucu al + $hotelResult = null; + foreach ($searchData['data'] as $item) { + if (isset($item['dest_type']) && $item['dest_type'] === 'hotel') { + $hotelResult = $item; + break; + } + } + if (!$hotelResult) { + $hotelResult = $searchData['data'][0]; + } + + $hotelId = isset($hotelResult['dest_id']) ? $hotelResult['dest_id'] : null; + $hotelName = isset($hotelResult['label']) ? $hotelResult['label'] : $name; + + if (!$hotelId) { + return response()->json(array( + 'status' => false, + 'message' => 'hotel_id bulunamadı', + 'search_result' => $hotelResult, + ), 404); + } + + // ── 2. Otel detayı ─────────────────────────────────────────────────── + $detailResponse = $this->client()->get( + self::API_BASE . '/api/v1/hotels/getHotelDetails', + array('query' => array( + 'hotel_id' => $hotelId, + 'arrival_date' => $checkin, + 'departure_date' => $checkout, + 'adults' => 1, + 'room_qty' => 1, + 'languagecode' => 'en-us', + 'currency_code' => 'USD', + 'units' => 'metric', + 'temperature_unit' => 'c', + )) + ); + $detailData = json_decode($detailResponse->getBody()->getContents(), true); + + // ── 3. Fotoğrafları çek ve kaydet ──────────────────────────────────── + $photoResponse = $this->client()->get( + self::API_BASE . '/api/v1/hotels/getHotelPhotos', + array('query' => array( + 'hotel_id' => $hotelId, + 'page_number' => 1, + )) + ); + $photoData = json_decode($photoResponse->getBody()->getContents(), true); + $savedPhotos = $this->processAndSavePhotos($photoData, $hotelId, $propertyId, $limit); + + // ── 4. Facility matching ────────────────────────────────────────────── + $detail = isset($detailData['data']) ? $detailData['data'] : $detailData; + $facilityResult = $this->matchAndLogFacilities($detail, $hotelId, $propertyId); + + return response()->json(array( + 'status' => true, + 'hotel_id' => $hotelId, + 'hotel_name' => $hotelName, + 'hotel_details' => $detail, + 'photos_saved' => $savedPhotos, + 'facility_matching' => $facilityResult, + )); + } catch (Exception $e) { + Log::error('Bulut.testApi error: ' . $e->getMessage()); + return response()->json(array('status' => false, 'message' => $e->getMessage()), 500); + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // Private: API'den gelen foto listesinden URL çıkar, indir, boyutlandır, DB'ye kaydet + // ───────────────────────────────────────────────────────────────────────────── + private function processAndSavePhotos(array $responseData, $hotelId, $propertyId, $limit = 20) + { + $photoUrls = $this->extractPhotoUrls($responseData); + + if (empty($photoUrls)) { + return array( + 'count' => 0, + 'message' => 'API yanıtında fotoğraf URL bulunamadı', + 'raw_keys' => array_keys($responseData), + ); + } + + $uploadRoot = rtrim(Config::get('app.fileSystemDriver'), '/'); + $urlPath = '/property-photos/' . $propertyId . '/'; + $filePath = $uploadRoot . $urlPath; + + if (!File::exists($filePath)) { + File::makeDirectory($filePath, 0777, true); + } + + // Bu property için zaten default foto var mı? + $hasDefault = PropertyPhoto::where('property_id', $propertyId) + ->where('status', 1) + ->where('is_default', 1) + ->exists(); + + $saved = array(); + $isFirstPhoto = true; + $total = min(count($photoUrls), $limit); + + for ($i = 0; $i < $total; $i++) { + $photoUrl = $photoUrls[$i]; + + try { + $fileName = time() . '_bk' . $hotelId . '_' . $i; + $fileExt = 'jpg'; + $tempPath = sys_get_temp_dir() . '/' . $fileName . '_orig.jpg'; + + // ── İndir ───────────────────────────────────────────────────────── + $this->downloadRawImage($photoUrl, $tempPath); + + // ── Intervention Image ile işle ─────────────────────────────────── + $image = Image::make($tempPath); + $imageWidth = $image->width(); + $imageHeight = $image->height(); + $fileSize = (int) ceil(filesize($tempPath) / 1024); + $quality = 80; + + // Orijinal (2000px üzeriyse küçült) + if ($imageHeight > 2000 || $imageWidth > 2000) { + if ($imageHeight > $imageWidth) { + $r = $imageHeight / 2000; + $image->fit((int)($imageWidth / $r), 2000); + } else { + $r = $imageWidth / 2000; + $image->fit(2000, (int)($imageHeight / $r)); + } + } + $image->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt); + + // _medium (max 1600x768) + $mediumImage = Image::make($tempPath); + $ratio = $imageHeight > 0 ? ($imageHeight / 768) : 1; + $resizeWidth = (int)($imageWidth / max($ratio, 1)); + if ($resizeWidth > 1599) { + $mediumImage->fit(1600, 768); + } else { + $mediumImage->fit(max($resizeWidth, 1), 768); + } + $mediumImage->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '_medium.' . $fileExt); + + // _thumbnail (300x300) + Image::make($tempPath) + ->fit(300, 300) + ->encode($fileExt, $quality) + ->orientate() + ->save($filePath . $fileName . '_thumbnail.' . $fileExt); + + // Temp dosyayı sil + if (File::exists($tempPath)) { + File::delete($tempPath); + } + + $isCompatible = ($imageWidth >= 1150 && $imageHeight >= 600) ? 1 : 0; + $isDefault = (!$hasDefault && $isFirstPhoto) ? 1 : 0; + $isFirstPhoto = false; + + // ── DB'ye kaydet ────────────────────────────────────────────────── + $photoRecord = PropertyPhoto::create(array( + 'property_id' => $propertyId, + 'property_photo_category_id' => 1, + 'photo_path' => $urlPath, + 'photo_name' => $fileName, + 'file_size' => $fileSize, + 'file_ext' => $fileExt, + 'photo_resolution' => $imageWidth . 'x' . $imageHeight, + 'is_default' => $isDefault, + 'is_compatible_with_myweb_slider' => $isCompatible, + 'photo_order' => 0, + 'photo_rank' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + )); + + $saved[] = array( + 'db_id' => $photoRecord->id, + 'file_name' => $fileName . '.' . $fileExt, + 'resolution' => $imageWidth . 'x' . $imageHeight, + 'source_url' => $photoUrl, + ); + } catch (Exception $e) { + Log::error('Bulut.photo.save [' . $i . ']: ' . $e->getMessage() . ' | ' . $photoUrl); + $saved[] = array('error' => $e->getMessage(), 'source_url' => $photoUrl); + } + } + + return array( + 'saved_count' => count(array_filter($saved, function ($s) { + return !isset($s['error']); + })), + 'total_in_api' => count($photoUrls), + 'limit_applied' => $total, + 'photos' => $saved, + ); + } + + // ───────────────────────────────────────────────────────────────────────────── + // booking-com15 getHotelPhotos yanıtından URL listesi çıkar + // ───────────────────────────────────────────────────────────────────────────── + private function extractPhotoUrls(array $data) + { + $urls = array(); + + // Format 1: data[] dizisi – her elemanın url_original / url_max / url alanı + if (isset($data['data']) && is_array($data['data']) && !empty($data['data'])) { + foreach ($data['data'] as $item) { + if (!is_array($item)) { + continue; + } + if (!empty($item['url_original'])) { + $urls[] = $item['url_original']; + } elseif (!empty($item['url_max'])) { + $urls[] = $item['url_max']; + } elseif (!empty($item['url'])) { + $urls[] = $item['url']; + } elseif (!empty($item['photo']['url_original'])) { + $urls[] = $item['photo']['url_original']; + } + } + } + + // Format 2: data.photos[] + if (empty($urls) && isset($data['data']['photos']) && is_array($data['data']['photos'])) { + foreach ($data['data']['photos'] as $item) { + if (!empty($item['url_original'])) { + $urls[] = $item['url_original']; + } elseif (!empty($item['url'])) { + $urls[] = $item['url']; + } + } + } + + // Format 3: photos[] direkt root'ta + if (empty($urls) && isset($data['photos']) && is_array($data['photos'])) { + foreach ($data['photos'] as $item) { + if (!empty($item['url_original'])) { + $urls[] = $item['url_original']; + } elseif (!empty($item['url'])) { + $urls[] = $item['url']; + } + } + } + + return $urls; + } + + // ───────────────────────────────────────────────────────────────────────────── + // Görseli disk'e indir (GuzzleHttp sink kullan) + // ───────────────────────────────────────────────────────────────────────────── + private function downloadRawImage($url, $savePath) + { + $client = new Client(array( + 'timeout' => 30, + 'verify' => false, + 'headers' => array('User-Agent' => 'Mozilla/5.0'), + )); + + $response = $client->get($url, array('sink' => $savePath)); + + if ($response->getStatusCode() !== 200) { + throw new Exception('Image download failed HTTP ' . $response->getStatusCode() . ' | ' . $url); + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // Public: GET /bulut/match-facilities?hotel_id=89675&property_id=1 + // ───────────────────────────────────────────────────────────────────────────── + public function matchFacilities(Request $request) + { + $hotelId = $request->query('hotel_id', ''); + $propertyId = $request->query('property_id', 1); + + if (!$hotelId) { + return response()->json(array('status' => false, 'message' => 'hotel_id gerekli'), 422); + } + + try { + $response = $this->client()->get( + self::API_BASE . '/api/v1/hotels/getHotelFacilities', + array('query' => array('hotel_id' => $hotelId)) + ); + + $data = json_decode($response->getBody()->getContents(), true); + $result = $this->matchAndLogFacilities( + isset($data['data']) ? $data['data'] : array(), + $hotelId, + $propertyId + ); + + return response()->json(array( + 'status' => true, + 'hotel_id' => $hotelId, + 'result' => $result, + )); + } catch (Exception $e) { + Log::error('Bulut.matchFacilities error: ' . $e->getMessage()); + return response()->json(array('status' => false, 'message' => $e->getMessage()), 500); + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // Private: API yanıtındaki facilitylerle property_fact tablosunu eşleştir + logla + // ───────────────────────────────────────────────────────────────────────────── + private function matchAndLogFacilities(array $detail, $hotelId, $propertyId) + { + // ── 1. API'den facility isimlerini topla ────────────────────────────────── + // getHotelFacilities yapısı: facilities[], accommodationHighlights[], highlights[] + // getHotelDetails yapısı (eski): facilities_block.facilities[], top_ufi_benefits[], property_highlight_strip[] + $apiNames = array(); + + $addUniq = function ($name) use (&$apiNames) { + $name = trim($name); + if ($name !== '' && !in_array($name, $apiNames)) { + $apiNames[] = $name; + } + }; + + // getHotelFacilities → facilities[].instances[].title (96 adet zengin veri) + if (isset($detail['facilities']) && is_array($detail['facilities'])) { + foreach ($detail['facilities'] as $fac) { + if (!empty($fac['instances']) && is_array($fac['instances'])) { + foreach ($fac['instances'] as $inst) { + if (!empty($inst['title'])) { + $addUniq($inst['title']); + } + } + } + } + } + + // getHotelFacilities → accommodationHighlights[].title + if (isset($detail['accommodationHighlights']) && is_array($detail['accommodationHighlights'])) { + foreach ($detail['accommodationHighlights'] as $h) { + if (!empty($h['title'])) { + $addUniq($h['title']); + } + } + } + + // getHotelFacilities → highlights[].instances[].title + if (isset($detail['highlights']) && is_array($detail['highlights'])) { + foreach ($detail['highlights'] as $h) { + if (!empty($h['instances']) && is_array($h['instances'])) { + foreach ($h['instances'] as $inst) { + if (!empty($inst['title'])) { + $addUniq($inst['title']); + } + } + } + } + } + + // getHotelDetails uyumluluğu — eski endpoint hâlâ çalışsın + if (isset($detail['facilities_block']['facilities']) && is_array($detail['facilities_block']['facilities'])) { + foreach ($detail['facilities_block']['facilities'] as $fac) { + if (!empty($fac['name'])) { + $addUniq($fac['name']); + } + } + } + if (isset($detail['top_ufi_benefits']) && is_array($detail['top_ufi_benefits'])) { + foreach ($detail['top_ufi_benefits'] as $ben) { + if (!empty($ben['translated_name'])) { + $addUniq($ben['translated_name']); + } + } + } + if (isset($detail['property_highlight_strip']) && is_array($detail['property_highlight_strip'])) { + foreach ($detail['property_highlight_strip'] as $strip) { + if (!empty($strip['name'])) { + $addUniq($strip['name']); + } + } + } + + $apiNames = array_values($apiNames); + + if (empty($apiNames)) { + return array( + 'matched' => array(), + 'unmatched' => array(), + 'message' => 'API yanıtında facility bulunamadı', + ); + } + + // ── 2. property_fact tablosundaki TÜM kayıtları çek ────────────────────── + $allFacts = PropertyFact::select(array('id', 'name', 'icon', 'parent_id', 'type'))->get(); + $exactIndex = array(); + foreach ($allFacts as $fact) { + $key = mb_strtolower(trim($fact->name)); + if (!isset($exactIndex[$key]) || $fact->type == 1) { + $exactIndex[$key] = $fact; + } + } + + // ── 3. Alias map — Booking.com adı ≠ DB adı olan terimler ─────────────── + $aliasMap = array( + // Internet + 'internet services' => 'wi-fi', + 'free wifi' => 'wi-fi', + 'wifi' => 'wi-fi', + 'wi-fi' => 'wi-fi', + 'wifi in all areas' => 'wi-fi', + 'free wi-fi' => 'wi-fi', + 'internet' => 'wi-fi', + 'internet access' => 'wi-fi', + // Ulaşım + 'airport shuttle' => 'airport transfer', + 'airport pick up' => 'airport to hotel transfer', + 'airport drop off' => 'hotel to airport transfer', + 'car hire' => 'rent a car', + 'car rental' => 'rent a car', + 'shuttle service' => 'airport transfer', + // Servis + '24-hour front desk' => 'reception', + 'concierge service' => 'concierge', + 'luggage storage' => 'luggage storage', + 'laundry' => 'cleaning service', + 'daily housekeeping' => 'housekeeping', + 'tour desk' => 'tour desk', + 'private check-in/check-out' => 'check-in service', + 'express check-in/check-out' => 'check-in service', + 'wake-up service' => 'wake up service', + 'dry cleaning' => 'dry cleaning', + 'currency exchange' => 'currency exchange', + // Engelli + 'facilities for disabled guests' => 'disabled', + 'wheelchair accessible' => 'wheelchair', + 'upper floors accessible by elevator' => 'elevator', + 'lower bathroom sink' => 'lower bathroom sink', + 'toilet with grab rails' => 'toilet with grab rails', + // Yiyecek + 'breakfast' => 'bed and breakfast', + 'snack bar' => 'snack bar', + 'meeting/banquet facilities' => 'meeting room', + 'business centre' => 'working area', + 'business center' => 'working area', + 'fax/photocopying' => 'photocopy', + // Oda + 'air conditioning' => 'air conditioner', + 'flat-screen tv' => 'tv', + 'flat screen tv' => 'tv', + 'bath or shower' => 'shower', + 'free toiletries' => 'shower', + 'single-room air conditioning for guest accommodation' => 'air conditioner', + 'minibar' => 'mini bar', + 'mini bar' => 'mini bar', + 'hairdryer' => 'hair dryer', + 'hair dryer' => 'hair dryer', + 'wardrobe or closet' => 'clothes cabinet', + 'socket near the bed' => 'planning electrical sockets', + 'family rooms' => 'family room', + 'clothes rack' => 'clotheshorse', + // Güvenlik + 'cctv in common areas' => 'security camera', + 'cctv outside property' => 'security camera', + 'smoke alarms' => 'smoke alarm', + 'safety deposit box' => 'safety deposit box', + '24-hour security' => 'security service', + 'key card access' => 'security service', + // Hijyen + 'hand sanitizer in guest accommodation and key areas' => 'hand sanitiser', + 'hand sanitiser' => 'hand sanitiser', + 'face masks for guests available' => 'protective masks for guests', + 'physical distancing rules followed' => 'social distancing regulations', + 'screens or physical barriers placed between staff and guests in appropriate areas' => 'protective hygiene screens', + 'guest accommodation is disinfected between stays' => 'disinfection in all rooms', + // Aktivite + 'tennis court' => 'tennis', + 'golf course' => 'golf', + 'table tennis' => 'ping pong', + 'jogging track' => 'jogging', + // Çevre + 'lift' => 'elevator', + 'elevator' => 'elevator', + ); + + // ── 4. Eşleştir ─────────────────────────────────────────────────────────── + $matched = array(); + $unmatched = array(); + + foreach ($apiNames as $apiName) { + $lower = mb_strtolower(trim($apiName)); + $searchKey = isset($aliasMap[$lower]) ? $aliasMap[$lower] : $lower; + + // ADIM 1: Alias → exact + if (isset($exactIndex[$searchKey])) { + $fact = $exactIndex[$searchKey]; + $tag = ($searchKey !== $lower) ? 'alias' : 'exact'; + $matched[] = array( + 'api_name' => $apiName, + 'fact_id' => $fact->id, + 'fact_name' => $fact->name, + 'match_type' => $tag, + ); + continue; + } + + // ADIM 2: DB LIKE — fact adı içinde api ismi geçiyor mu? + $likeResult = PropertyFact::where('type', 1) + ->whereRaw('LOWER(name) LIKE ?', array('%' . $lower . '%')) + ->orderByRaw('LENGTH(name) ASC') + ->first(); + + if ($likeResult) { + $matched[] = array( + 'api_name' => $apiName, + 'fact_id' => $likeResult->id, + 'fact_name' => $likeResult->name, + 'match_type' => 'like', + ); + continue; + } + + // ADIM 3: Ters LIKE — fact adı api isminin içinde mi? + $reverseResult = PropertyFact::where('type', 1) + ->whereRaw('LOWER(?) LIKE CONCAT(\'%\', LOWER(name), \'%\')', array($lower)) + ->whereRaw('LENGTH(name) >= 5') + ->orderByRaw('LENGTH(name) DESC') + ->first(); + + if ($reverseResult) { + $matched[] = array( + 'api_name' => $apiName, + 'fact_id' => $reverseResult->id, + 'fact_name' => $reverseResult->name, + 'match_type' => 'reverse_like', + ); + continue; + } + + $unmatched[] = $apiName; + } + + // ── 5. storage/fact.log — okunabilir format ─────────────────────────────── + $logPath = storage_path('fact.log'); + $ts = date('Y-m-d H:i:s'); + $divider = str_repeat('═', 80); + $thin = str_repeat('─', 80); + + $lines = array(); + $lines[] = $divider; + $lines[] = sprintf(' [%s] hotel_id=%-12s property_id=%s', $ts, $hotelId, $propertyId); + $lines[] = sprintf( + ' Toplam API facility: %d | Eşleşen: %d | Eşleşmeyen: %d', + count($apiNames), + count($matched), + count($unmatched) + ); + $lines[] = $divider; + $lines[] = ''; + + $lines[] = sprintf(' EŞLEŞENLER (%d)', count($matched)); + $lines[] = ' ' . $thin; + if (count($matched)) { + foreach ($matched as $m) { + $lines[] = sprintf( + ' [%-12s] %-45s → #%-5d %s', + $m['match_type'], + '"' . $m['api_name'] . '"', + $m['fact_id'], + $m['fact_name'] + ); + } + } else { + $lines[] = ' (eşleşen yok)'; + } + + $lines[] = ''; + $lines[] = sprintf(' EŞLEŞMEYENLEr (%d)', count($unmatched)); + $lines[] = ' ' . $thin; + if (count($unmatched)) { + foreach ($unmatched as $u) { + $lines[] = ' [?] ' . $u; + } + } else { + $lines[] = ' (tümü eşleşti — property_fact\'e eklenebilir)'; + } + + $lines[] = ''; + $lines[] = ''; + + file_put_contents($logPath, implode(PHP_EOL, $lines), FILE_APPEND | LOCK_EX); + + return array( + 'api_facilities_count' => count($apiNames), + 'matched_count' => count($matched), + 'unmatched_count' => count($unmatched), + 'matched' => $matched, + 'unmatched' => $unmatched, + 'log_file' => $logPath, + ); + } + + // ───────────────────────────────────────────────────────────────────────────── + // AI Semantic Match: getHotelFacilities → string match → kalan unmatched'ları + // Gemini'ye gönder → "aynı kavram farklı isim mi?" veya "gerçekten eksik mi?" + // GET /bulut/ai-match?hotel_id=...&property_id=... + // ───────────────────────────────────────────────────────────────────────────── + public function aiMatch(Request $request) + { + $hotelId = $request->query('hotel_id', ''); + $propertyId = $request->query('property_id', 1); + + if (!$hotelId) { + return response()->json(array('status' => false, 'message' => 'hotel_id gerekli'), 422); + } + + $geminiKey = env('GEMINI_API_KEY', ''); + if (!$geminiKey) { + return response()->json(array('status' => false, 'message' => 'GEMINI_API_KEY .env\'de tanımlı değil'), 500); + } + + try { + // ── ADIM 1: Booking.com'dan tüm faciliteleri çek ───────────────────── + $response = $this->client()->get( + self::API_BASE . '/api/v1/hotels/getHotelFacilities', + array('query' => array('hotel_id' => $hotelId)) + ); + $data = json_decode($response->getBody()->getContents(), true); + $facilData = isset($data['data']) ? $data['data'] : array(); + + // Tüm API isimlerini topla + $apiNames = array(); + $addUniq = function ($name) use (&$apiNames) { + $name = trim($name); + if ($name !== '' && !in_array($name, $apiNames)) { + $apiNames[] = $name; + } + }; + + if (isset($facilData['facilities']) && is_array($facilData['facilities'])) { + foreach ($facilData['facilities'] as $fac) { + if (!empty($fac['instances']) && is_array($fac['instances'])) { + foreach ($fac['instances'] as $inst) { + if (!empty($inst['title'])) { + $addUniq($inst['title']); + } + } + } + } + } + if (isset($facilData['accommodationHighlights']) && is_array($facilData['accommodationHighlights'])) { + foreach ($facilData['accommodationHighlights'] as $h) { + if (!empty($h['title'])) { + $addUniq($h['title']); + } + } + } + if (isset($facilData['highlights']) && is_array($facilData['highlights'])) { + foreach ($facilData['highlights'] as $h) { + if (!empty($h['instances']) && is_array($h['instances'])) { + foreach ($h['instances'] as $inst) { + if (!empty($inst['title'])) { + $addUniq($inst['title']); + } + } + } + } + } + + if (empty($apiNames)) { + return response()->json(array('status' => false, 'message' => 'API\'den facility alınamadı'), 500); + } + + // ── ADIM 2: String tabanlı match (mevcut mantık) ───────────────────── + $stringResult = $this->matchAndLogFacilities($facilData, $hotelId, $propertyId); + $stringMatched = $stringResult['matched']; + $unmatched = $stringResult['unmatched']; + + if (empty($unmatched)) { + return response()->json(array( + 'status' => true, + 'hotel_id' => $hotelId, + 'string_matched' => $stringMatched, + 'ai_matched' => array(), + 'truly_missing' => array(), + 'message' => 'Tüm facilityler string eşleştirme ile bulundu, AI çağrısı gerekmedi', + )); + } + + // ── ADIM 3: DB'deki tüm fact isimlerini çek ────────────────────────── + $allFactNames = PropertyFact::where('type', 1) + ->orderBy('name') + ->pluck('name', 'id') + ->toArray(); + + // Gemini'ye göndermek için isim → id lookup + $nameToId = array(); + foreach ($allFactNames as $id => $name) { + $nameToId[mb_strtolower(trim($name))] = array('id' => $id, 'name' => $name); + } + + // ── ADIM 4: Gemini prompt ───────────────────────────────────────────── + $unmatchedList = implode("\n", array_map(function ($n) { + return '- ' . $n; + }, $unmatched)); + $dbList = implode("\n", array_map(function ($n) { + return '- ' . $n; + }, $allFactNames)); + + $prompt = << 60, 'http_errors' => false)); + + // Sırayla farklı modeller dene (free tier kota sorunu için) + $geminiModels = array( + 'gemini-2.0-flash-lite', + 'gemini-2.0-flash', + 'gemini-flash-lite-latest', + 'gemini-flash-latest', + ); + + $rawText = null; + $lastStatus = 0; + + foreach ($geminiModels as $model) { + $geminiResponse = $geminiClient->post( + 'https://generativelanguage.googleapis.com/v1beta/models/' . $model . ':generateContent?key=' . $geminiKey, + array( + 'json' => array( + 'contents' => array( + array('parts' => array(array('text' => $prompt))) + ), + 'generationConfig' => array( + 'responseMimeType' => 'application/json', + 'temperature' => 0.1, + ), + ), + ) + ); + + $lastStatus = $geminiResponse->getStatusCode(); + + if ($lastStatus === 200) { + $geminiBody = json_decode($geminiResponse->getBody()->getContents(), true); + $rawText = isset($geminiBody['candidates'][0]['content']['parts'][0]['text']) + ? $geminiBody['candidates'][0]['content']['parts'][0]['text'] + : null; + break; + } + + // 429 = quota exceeded → bir sonraki modeli dene + if ($lastStatus !== 429) { + $errBody = $geminiResponse->getBody()->getContents(); + throw new Exception('Gemini API hatası (' . $model . '): HTTP ' . $lastStatus . ' → ' . substr($errBody, 0, 200)); + } + } + + if ($rawText === null) { + return response()->json(array( + 'status' => true, + 'hotel_id' => $hotelId, + 'api_total' => count($apiNames), + 'string_matched' => array('count' => count($stringMatched), 'matches' => $stringMatched), + 'ai_matched' => array('count' => 0, 'matches' => array()), + 'truly_missing' => array('count' => count($unmatched), 'items' => $unmatched), + 'total_matched' => count($stringMatched), + 'warning' => 'Tüm Gemini modelleri 429 quota hatası verdi. String match sonuçları döndürüldü. Birkaç dakika bekleyip tekrar deneyin.', + )); + } + + $aiResult = json_decode($rawText, true); + if (!$aiResult) { + $aiResult = array('ai_matches' => array(), 'truly_missing' => $unmatched); + } + + $aiMatches = isset($aiResult['ai_matches']) ? $aiResult['ai_matches'] : array(); + $trulyMissing = isset($aiResult['truly_missing']) ? $aiResult['truly_missing'] : array(); + + // AI eşleşmelerine DB id'si ekle + foreach ($aiMatches as $idx => $m) { + $key = mb_strtolower(trim($m['db_name'])); + if (isset($nameToId[$key])) { + $aiMatches[$idx]['fact_id'] = $nameToId[$key]['id']; + $aiMatches[$idx]['fact_name'] = $nameToId[$key]['name']; + $aiMatches[$idx]['match_type'] = 'ai_semantic'; + } + } + + // ── ADIM 6: off_fact.log — TÜM liste: var olanlar + olmayanlar ────────── + $logPath = storage_path('off_fact.log'); + $ts = date('Y-m-d H:i:s'); + $eq = str_repeat('═', 90); + $dash = str_repeat('─', 90); + + // Tüm eşleşmeleri birleştir (string + AI) + $allMatched = $stringMatched; + foreach ($aiMatches as $am) { + $allMatched[] = array( + 'api_name' => $am['api_name'], + 'fact_id' => isset($am['fact_id']) ? $am['fact_id'] : '???', + 'fact_name' => isset($am['fact_name']) ? $am['fact_name'] : $am['db_name'], + 'match_type' => 'ai_semantic', + 'reason' => isset($am['reason']) ? $am['reason'] : '', + ); + } + + $logLines = array(); + $logLines[] = $eq; + $logLines[] = sprintf( + ' [%s] hotel_id: %-12s property_id: %s', + $ts, + $hotelId, + $propertyId + ); + $logLines[] = sprintf( + ' Booking.com\'dan gelen: %d | DB\'de VAR: %d | DB\'de YOK: %d', + count($apiNames), + count($allMatched), + count($trulyMissing) + ); + $logLines[] = $eq; + $logLines[] = ''; + + // ── VAR OLANLAR ─────────────────────────────────────────────────────── + $logLines[] = sprintf(' ✔ DB\'DE VAR (%d / %d)', count($allMatched), count($apiNames)); + $logLines[] = ' ' . $dash; + foreach ($allMatched as $m) { + $type = isset($m['match_type']) ? $m['match_type'] : 'exact'; + $line = sprintf( + ' [%-12s] %-50s → #%-5s %s', + $type, + '"' . $m['api_name'] . '"', + $m['fact_id'], + $m['fact_name'] + ); + $logLines[] = $line; + if (!empty($m['reason'])) { + $logLines[] = sprintf(' AI notu: %s', $m['reason']); + } + } + + $logLines[] = ''; + + // ── YOK OLANLAR ─────────────────────────────────────────────────────── + $logLines[] = sprintf(' ✘ DB\'DE YOK (%d / %d) — property_fact\'e eklenmesi değerlendirilebilir', count($trulyMissing), count($apiNames)); + $logLines[] = ' ' . $dash; + if (count($trulyMissing)) { + foreach ($trulyMissing as $missing) { + $logLines[] = ' [MISSING] "' . $missing . '"'; + } + } else { + $logLines[] = ' (tüm facilityler DB\'de mevcut)'; + } + + $logLines[] = ''; + $logLines[] = ''; + + file_put_contents($logPath, implode(PHP_EOL, $logLines), FILE_APPEND | LOCK_EX); + + return response()->json(array( + 'status' => true, + 'hotel_id' => $hotelId, + 'api_total' => count($apiNames), + 'string_matched' => array( + 'count' => count($stringMatched), + 'matches' => $stringMatched, + ), + 'ai_matched' => array( + 'count' => count($aiMatches), + 'matches' => $aiMatches, + ), + 'truly_missing' => array( + 'count' => count($trulyMissing), + 'items' => $trulyMissing, + ), + 'total_matched' => count($stringMatched) + count($aiMatches), + )); + } catch (Exception $e) { + Log::error('Bulut.aiMatch error: ' . $e->getMessage()); + return response()->json(array('status' => false, 'message' => $e->getMessage()), 500); + } + } +} diff --git a/app/Http/Controllers/propertyInsertController.php b/app/Http/Controllers/propertyInsertController.php new file mode 100644 index 0000000..757fb64 --- /dev/null +++ b/app/Http/Controllers/propertyInsertController.php @@ -0,0 +1,1786 @@ + 'wi-fi', + 'free wifi' => 'wi-fi', + 'wifi' => 'wi-fi', + 'wi-fi' => 'wi-fi', + 'wifi in all areas' => 'wi-fi', + 'free wi-fi' => 'wi-fi', + 'internet' => 'wi-fi', + 'internet access' => 'wi-fi', + 'airport shuttle' => 'airport transfer', + 'airport pick up' => 'airport to hotel transfer', + 'airport drop off' => 'hotel to airport transfer', + 'car hire' => 'rent a car', + 'car rental' => 'rent a car', + 'shuttle service' => 'airport transfer', + '24-hour front desk' => 'reception', + 'concierge service' => 'concierge', + 'luggage storage' => 'luggage storage', + 'laundry' => 'cleaning service', + 'daily housekeeping' => 'housekeeping', + 'tour desk' => 'tour desk', + 'private check-in/check-out' => 'check-in service', + 'express check-in/check-out' => 'check-in service', + 'wake-up service' => 'wake up service', + 'dry cleaning' => 'dry cleaning', + 'currency exchange' => 'currency exchange', + 'facilities for disabled guests' => 'disabled', + 'wheelchair accessible' => 'wheelchair', + 'upper floors accessible by elevator' => 'elevator', + 'lower bathroom sink' => 'lower bathroom sink', + 'toilet with grab rails' => 'toilet with grab rails', + 'breakfast' => 'bed and breakfast', + 'snack bar' => 'snack bar', + 'meeting/banquet facilities' => 'meeting room', + 'business centre' => 'working area', + 'business center' => 'working area', + 'fax/photocopying' => 'photocopy', + 'air conditioning' => 'air conditioner', + 'flat-screen tv' => 'tv', + 'flat screen tv' => 'tv', + 'bath or shower' => 'shower', + 'free toiletries' => 'shower', + 'single-room air conditioning for guest accommodation' => 'air conditioner', + 'single-room ac for guest accommodation' => 'air conditioner', + 'minibar' => 'mini bar', + 'hairdryer' => 'hair dryer', + 'hair dryer' => 'hair dryer', + 'wardrobe or closet' => 'clothes cabinet', + 'socket near the bed' => 'planning electrical sockets', + 'family rooms' => 'family room', + 'clothes rack' => 'clotheshorse', + 'cctv in common areas' => 'security camera', + 'cctv outside property' => 'security camera', + 'smoke alarms' => 'smoke alarm', + 'safety deposit box' => 'safety deposit box', + '24-hour security' => 'security service', + 'key card access' => 'security service', + 'hand sanitizer in guest accommodation and key areas' => 'hand sanitiser', + 'hand sanitiser' => 'hand sanitiser', + 'hand sanitizer' => 'hand sanitiser', + 'face masks for guests available' => 'protective masks for guests', + 'physical distancing rules followed' => 'social distancing regulations', + 'screens or physical barriers placed between staff and guests in appropriate areas' => 'protective hygiene screens', + 'guest accommodation is disinfected between stays' => 'disinfection in all rooms', + 'cashless payment available' => 'contactless payment', + 'food can be delivered to guest accommodation' => 'food delivery', + 'tennis court' => 'tennis', + 'golf course' => 'golf', + 'table tennis' => 'ping pong', + 'jogging track' => 'jogging', + 'lift' => 'elevator', + 'elevator' => 'elevator', + ); + + // ───────────────────────────────────────────────────────────────────────────── + // Oda olanaklarına özel alias: Booking.com oda facility adı → property_fact.name + // ───────────────────────────────────────────────────────────────────────────── + private $roomAliasMap = array( + 'safe' => 'safe box', + 'towels' => 'towel', + 'linens' => 'towel', + 'tea/coffee maker' => 'coffee maker', + 'coffee/tea maker' => 'coffee maker', + 'electric kettle' => 'kettle', + 'private bathroom' => 'bathroom', + 'telephone' => 'telephone', + 'toilet paper' => 'toilet', + 'free toiletries' => 'hair dryer', + 'slippers' => 'bathrobe', + 'bath or shower' => 'shower', + 'bathtub or shower' => 'shower', + 'shower only' => 'shower', + ); + + // ───────────────────────────────────────────────────────────────────────────── + // GuzzleHttp → Booking.com API + // ───────────────────────────────────────────────────────────────────────────── + private function bookingClient() + { + return new Client(array( + 'timeout' => 30, + 'headers' => array( + 'x-rapidapi-host' => env('BULUT_RAPIDAPI_HOST', self::API_HOST), + 'x-rapidapi-key' => env('BULUT_RAPIDAPI_KEY'), + 'Content-Type' => 'application/json', + ), + )); + } + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 0: Booking.com'dan otel verilerini çek + // ───────────────────────────────────────────────────────────────────────────── + private function fetchBookingData($hotelId, $checkin, $checkout) + { + $client = $this->bookingClient(); + + // Otel detayı (property bilgileri: isim, konum, dil, timezone vb.) + $detailResp = $client->get(self::API_BASE . '/api/v1/hotels/getHotelDetails', array( + 'query' => array( + 'hotel_id' => $hotelId, + 'arrival_date' => $checkin, + 'departure_date' => $checkout, + 'adults' => 1, + 'room_qty' => 1, + 'languagecode' => 'en-us', + 'currency_code' => 'USD', + 'units' => 'metric', + 'temperature_unit' => 'c', + ), + )); + $detail = json_decode($detailResp->getBody()->getContents(), true); + $detail = isset($detail['data']) ? $detail['data'] : array(); + + // Oda listesi — tüm oda tiplerini çek (getRoomList daha kapsamlı) + // getHotelDetails sadece o güne müsait odaları döndürür, getRoomList tüm tipleri verir + $checkinRooms = date('Y-m-d', strtotime('+30 days')); + $checkoutRooms = date('Y-m-d', strtotime('+37 days')); + $roomListResp = $client->get(self::API_BASE . '/api/v1/hotels/getRoomList', array( + 'query' => array( + 'hotel_id' => $hotelId, + 'arrival_date' => $checkinRooms, + 'departure_date' => $checkoutRooms, + 'adults' => 2, + 'room_qty' => 1, + 'currency_code' => 'EUR', + 'languagecode' => 'en-us', + ), + )); + $roomListData = json_decode($roomListResp->getBody()->getContents(), true); + $roomListData = isset($roomListData['data']) ? $roomListData['data'] : array(); + + // getRoomList'ten gelen block[] ve rooms[] ile detail'i zenginleştir + if (!empty($roomListData['block'])) { + $detail['block'] = $roomListData['block']; + } + if (!empty($roomListData['rooms'])) { + $detail['rooms'] = $roomListData['rooms']; + } + + // Facility listesi + $facilResp = $client->get(self::API_BASE . '/api/v1/hotels/getHotelFacilities', array( + 'query' => array('hotel_id' => $hotelId), + )); + $facilData = json_decode($facilResp->getBody()->getContents(), true); + $facilData = isset($facilData['data']) ? $facilData['data'] : array(); + + // Otel fotograflari + $photoResp = $client->get(self::API_BASE . '/api/v1/hotels/getHotelPhotos', array( + 'query' => array('hotel_id' => $hotelId), + )); + $photoData = json_decode($photoResp->getBody()->getContents(), true); + + return array('detail' => $detail, 'facilities' => $facilData, 'photos' => $photoData); + } + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 1: Kullanıcı + Property oluştur + // → user/register-user-with-property mantığı + // ───────────────────────────────────────────────────────────────────────────── + private function createUserAndProperty(array $params, $propertyName, $userId = null) + { + // Kullanıcı zaten varsa sadece property ekle + $userService = app(\App\Core\Service\UserService::class); + $propertyService = app(\App\Core\Service\PropertyService::class); + $userPropertyMappingService = app(\App\Core\Service\UserPropertyMappingService::class); + $productService = app(\App\Core\Service\ProductService::class); + $jwtService = app(\App\Core\Service\JwtService::class); + $apiAccessTokenService = app(\App\Core\Service\ApiAccessTokenService::class); + + // Kullanıcı oluştur + $userParams = array( + 'name' => $params['name'], + 'surname' => $params['surname'], + 'email' => $params['email'], + 'password' => $params['password'], + 'status' => 1, + 'user_type' => 1, + 'property_name' => $propertyName, + ); + $userCreate = $userService->create($userParams); + if ($userCreate['status'] != 'success') { + throw new ApiErrorException('Kullanıcı oluşturulamadı: ' . $userCreate['message']); + } + $user = $userCreate['data']; + + // UserService::create rastgele şifre üretiyor (Str::random(6)) + // Kullanıcının istediği şifreyi DB'ye doğrudan güncelle + DB::table('user') + ->where('id', $user['id']) + ->update(array('password' => \Illuminate\Support\Facades\Hash::make($params['password']))); + + // Property oluştur + $propertyCreate = $propertyService->create(array( + 'name' => $propertyName, + 'status' => 1, + 'created_by' => $user['id'], + 'updated_by' => $user['id'], + 'created_at' => time(), + 'updated_at' => time(), + )); + if ($propertyCreate['status'] != 'success') { + throw new ApiErrorException('Property oluşturulamadı: ' . $propertyCreate['message']); + } + $property = $propertyCreate['data']; + + // User-Property mapping + $mappingCreate = $userPropertyMappingService->create(array( + 'user_id' => $user['id'], + 'property_id' => $property['id'], + 'status' => 1, + 'created_by' => $user['id'], + 'updated_by' => $user['id'], + 'created_at' => time(), + 'updated_at' => time(), + )); + if ($mappingCreate['status'] != 'success') { + throw new ApiErrorException('Kullanıcı-Property eşleştirme hatası: ' . $mappingCreate['message']); + } + + // Default ürünleri ata + $productService->setDefaultPropertyProducts(array( + 'user_id' => $user['id'], + 'property_id' => $property['id'], + )); + + // JWT token al + $jwtResult = $jwtService->jwtCreate(array('user_id' => $user['id'])); + if ($jwtResult['status'] != 'success') { + throw new ApiErrorException('Token oluşturulamadı'); + } + $jwt = $jwtResult['data']; + + $apiAccessTokenService->create(array( + 'token' => md5(fillOnUndefined($jwt, 'token')), + 'expire_date' => fillOnUndefined($jwt, 'exp'), + 'user_id' => $user['id'], + 'invalidate' => fillOnUndefined($jwt, 'invalidate', 0), + )); + + return array( + 'user' => $user, + 'property_id' => $property['id'], + 'token' => $jwt['token'], + ); + } + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 2: Property bilgilerini güncelle + // → property/info/update + // Booking.com detail → property_info, additional_info, locale + // ───────────────────────────────────────────────────────────────────────────── + private function updatePropertyInfo($propertyId, $userId, array $detail) + { + $propertyService = app(\App\Core\Service\PropertyService::class); + + $name = isset($detail['hotel_name']) ? $detail['hotel_name'] : null; + $raw = isset($detail['rawData']) ? $detail['rawData'] : array(); + + // Stars: rawData.accuratePropertyClass > rawData.propertyClass > rawData.qualityClass + $rating = null; + foreach (array('accuratePropertyClass', 'propertyClass', 'qualityClass') as $rk) { + if (!empty($raw[$rk])) { + $rating = (int) $raw[$rk]; + break; + } + } + + // Check-in/out: rawData.checkin.fromTime / rawData.checkout.untilTime + $checkIn = isset($raw['checkin']['fromTime']) ? $raw['checkin']['fromTime'] : null; + $checkOut = isset($raw['checkout']['untilTime']) ? $raw['checkout']['untilTime'] : null; + + // Ülke kodu (max 5 char) + $countryCode = null; + foreach (array('countryCode', 'countrycode', 'country_code') as $ck) { + if (!empty($raw[$ck])) { + $countryCode = strtoupper($raw[$ck]); + break; + } + } + if (!$countryCode && !empty($detail['countrycode'])) { + $countryCode = strtoupper($detail['countrycode']); + } + + // Konuşulan diller: "en-gb" → "en_gb", "tr" → "tr" + $spokenRaw = isset($detail['spoken_languages']) ? $detail['spoken_languages'] : array('en'); + $langCodes = array(); + foreach ($spokenRaw as $lang) { + $normalized = str_replace('-', '_', strtolower($lang)); + // Sadece 2 veya 5 harfli geçerli kodlar + if (preg_match('/^[a-z]{2}(_[a-z]{2})?$/', $normalized)) { + $langCodes[] = $normalized; + } + } + if (empty($langCodes)) { + $langCodes = array('en'); + } + + $updateParams = array( + 'property_id' => $propertyId, + 'user_id' => $userId, + 'locale' => 'en', + 'has_locale_name' => false, + 'property_language_spoken' => $langCodes, + 'property_info' => array( + 'name' => $name, + 'property_type_id' => 1, // 1 = Hotel + 'chain_id' => 1, // 1 = Independent + 'rating' => $rating, + 'country' => $countryCode, + ), + 'additional_info' => array( + 'checkin_time' => $checkIn, + 'checkout_time' => $checkOut, + 'number_of_rooms' => !empty($detail['number_of_rooms']) ? (string)$detail['number_of_rooms'] : null, + 'property_timezone' => !empty($detail['timezone']) + ? $this->resolveTimezoneId($detail['timezone']) + : null, + ), + ); + + $result = $propertyService->propertyUpdate($updateParams); + if ($result['status'] != 'success') { + throw new ApiErrorException('Property güncelleme hatası: ' . $result['message']); + } + + return $result['data']; + } + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 3: İletişim bilgilerini kaydet + // → property/contact/update + // ───────────────────────────────────────────────────────────────────────────── + private function updatePropertyContact($propertyId, $userId, array $detail, $phone = null) + { + $propertyContactService = app(\App\Core\Service\PropertyContactService::class); + + // Booking.com top-level alanlar + $email = isset($detail['email']) ? $detail['email'] : null; + $web = isset($detail['url']) ? $detail['url'] : null; + $address = isset($detail['address']) ? $detail['address'] : null; + $zip = isset($detail['zip']) ? $detail['zip'] : null; + $lat = isset($detail['latitude']) ? $detail['latitude'] : null; + $lng = isset($detail['longitude']) ? $detail['longitude'] : null; + + if (!$phone && isset($detail['phone'])) { + $phone = $detail['phone']; + } + + // Validator: email required|email — boşsa placeholder kullan + if (!$email) { + $email = 'property' . $propertyId . '@extranetwork.com'; + } + + // Validator: phone veya mobile gerekli + if (!$phone) { + $phone = '0000000000'; + } + + $contactParams = array( + 'property_id' => $propertyId, + 'user_id' => $userId, + 'contact' => array( + 'phone' => $phone, + 'email' => $email, + 'web' => $web, + 'address' => $address, + 'zip_code' => $zip, + 'latitude' => $lat, + 'longitude' => $lng, + ), + ); + + $result = $propertyContactService->propertyContactUpdateOrCreate($contactParams); + if ($result['status'] != 'success') { + throw new ApiErrorException('İletişim kaydetme hatası: ' . $result['message']); + } + + return $result['data']; + } + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 4: Facility eşleştir → property_fact_mapping'e ekle + // → property/fact-mapping/add + // ───────────────────────────────────────────────────────────────────────────── + private function mapAndSaveFacilities($propertyId, $userId, array $facilData) + { + $propertyFactMappingService = app(\App\Core\Service\PropertyFactMappingService::class); + + // Tüm API isimlerini topla + $apiNames = array(); + $addUniq = function ($name) use (&$apiNames) { + $name = trim($name); + if ($name !== '' && !in_array($name, $apiNames)) { + $apiNames[] = $name; + } + }; + + if (isset($facilData['facilities']) && is_array($facilData['facilities'])) { + foreach ($facilData['facilities'] as $fac) { + if (!empty($fac['instances'])) { + foreach ($fac['instances'] as $inst) { + if (!empty($inst['title'])) { + $addUniq($inst['title']); + } + } + } + } + } + if (isset($facilData['accommodationHighlights']) && is_array($facilData['accommodationHighlights'])) { + foreach ($facilData['accommodationHighlights'] as $h) { + if (!empty($h['title'])) { + $addUniq($h['title']); + } + } + } + if (isset($facilData['highlights']) && is_array($facilData['highlights'])) { + foreach ($facilData['highlights'] as $h) { + if (!empty($h['instances'])) { + foreach ($h['instances'] as $inst) { + if (!empty($inst['title'])) { + $addUniq($inst['title']); + } + } + } + } + } + + // DB exact index + $allFacts = PropertyFact::select(array('id', 'name', 'type'))->get(); + $exactIndex = array(); + foreach ($allFacts as $fact) { + $key = mb_strtolower(trim($fact->name)); + if (!isset($exactIndex[$key]) || $fact->type == 1) { + $exactIndex[$key] = $fact; + } + } + + $saved = array(); + $skipped = array(); + + foreach ($apiNames as $apiName) { + $lower = mb_strtolower(trim($apiName)); + $searchKey = isset($this->aliasMap[$lower]) ? $this->aliasMap[$lower] : $lower; + $fact = null; + $matchType = 'none'; + + // 1. Exact / alias + if (isset($exactIndex[$searchKey])) { + $fact = $exactIndex[$searchKey]; + $matchType = ($searchKey !== $lower) ? 'alias' : 'exact'; + } + + // 2. DB LIKE + if (!$fact) { + $like = PropertyFact::where('type', 1) + ->whereRaw('LOWER(name) LIKE ?', array('%' . $lower . '%')) + ->orderByRaw('LENGTH(name) ASC') + ->first(); + if ($like) { + $fact = $like; + $matchType = 'like'; + } + } + + // 3. Ters LIKE + if (!$fact) { + $reverse = PropertyFact::where('type', 1) + ->whereRaw('LOWER(?) LIKE CONCAT(\'%\', LOWER(name), \'%\')', array($lower)) + ->whereRaw('LENGTH(name) >= 5') + ->orderByRaw('LENGTH(name) DESC') + ->first(); + if ($reverse) { + $fact = $reverse; + $matchType = 'reverse_like'; + } + } + + if ($fact) { + // Kaydet + $mappingResult = $propertyFactMappingService->addPropertyFactMapping(array( + 'property_id' => $propertyId, + 'user_id' => $userId, + 'data' => array( + array('id' => $fact->id), + ), + )); + $saved[] = array( + 'api_name' => $apiName, + 'fact_id' => $fact->id, + 'fact_name' => $fact->name, + 'match_type' => $matchType, + 'saved' => ($mappingResult['status'] == 'success'), + ); + } else { + $skipped[] = $apiName; + } + } + + return array( + 'api_total' => count($apiNames), + 'saved_count' => count($saved), + 'skipped_count' => count($skipped), + 'saved' => $saved, + 'skipped' => $skipped, + ); + } + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 5: Booking.com'daki oda bilgilerinden oda ekle + // → property/room/add-room-bed + // ───────────────────────────────────────────────────────────────────────────── + // ───────────────────────────────────────────────────────────────────────────── + // Booking.com bed_type ID → sistemdeki property_room_bed_type.id + // + // Booking.com standart IDs (DataCrawler API): + // 1=Twin 2=Double 3=King 4=Queen 5=Single 6=Sofa 7=Bunk 8=Futon + // Sistemdeki property_room_bed_type: + // 1=Bunk Bed 2=Double Bed 3=French Bed 4=Futon 5=King Bed + // 6=Queen Bed 7=Single Bed 8=Sofa Bed 9=Twin Bed 10=Foldable Bed + // 11=Sleeping Bag 12=Triple Bunk Bed 13=Extra bed 14=Cot + // ───────────────────────────────────────────────────────────────────────────── + private $bedTypeMap = array( + 1 => 9, // Twin Bed(s) → Twin Bed + 2 => 2, // Double Bed → Double Bed + 3 => 5, // King Bed → King Bed + 4 => 6, // Queen Bed → Queen Bed + 5 => 7, // Single Bed → Single Bed + 6 => 8, // Sofa Bed → Sofa Bed + 7 => 1, // Bunk Bed → Bunk Bed + 8 => 4, // Futon → Futon + 9 => 3, // French Bed → French Bed + 10 => 10, // Foldable Bed → Foldable Bed + 37 => 14, // Cot/Cribs → Cot + 40 => 13, // Extra Bed → Extra bed + ); + + // ───────────────────────────────────────────────────────────────────────────── + // Booking.com view adı (highlight.translated_name) → property_room_view_type.id + // ───────────────────────────────────────────────────────────────────────────── + private $viewTypeMap = array( + 'park view' => 1, + 'pool view' => 2, + 'river view' => 3, + 'sea view' => 4, + 'ocean view' => 22, + 'street view' => 5, + 'valley view' => 6, + 'monument view' => 7, + 'bay view' => 8, + 'beach view' => 9, + 'city view' => 10, + 'countryside view' => 11, + 'courtyard view' => 12, + 'garden view' => 13, + 'gulf view' => 14, + 'harbor view' => 15, + 'lagoon view' => 16, + 'lake view' => 17, + 'marina view' => 18, + 'mountain view' => 19, + 'nature view' => 20, + 'no window' => 21, + 'land view' => 23, + 'forest view' => 24, + 'island view' => 25, + 'golf course view' => 26, + 'landmark view' => 30, + 'sea view' => 4, + ); + + // ───────────────────────────────────────────────────────────────────────────── + // Booking.com oda adı keyword → property_room_type.id + // ───────────────────────────────────────────────────────────────────────────── + private $roomTypeKeywords = array( + 'standard' => 24, // Standard Room + 'standart' => 24, + 'single' => 42, // Single Room + 'double' => 43, // Double Room + 'twin' => 43, // Double Room (çoğu twin=double planı) + 'triple' => 44, // Triple Room + 'quad' => 45, // Quadruple Room + 'family' => 18, // Family Room + 'suite' => 25, // Suite + 'junior suite' => 20, + 'grand suite' => 19, + 'king suite' => 21, + 'deluxe' => 16, // Deluxe + 'superior' => 26, // Superior + 'executive' => 48, // Executive Room + 'villa' => 27, // Villa + 'bungalow' => 14, // Bungalow + 'loft' => 31, // Loft Room + 'studio' => 29, // Studio Room + 'apart' => 12, // Apartment + 'apartment' => 12, + 'penthouse' => 22, // Pent House + 'accessible' => 11, // Accessible Room + 'honeymoon' => 34, // Honeymoon Room + 'economy' => 17, // Economy + 'premium' => 23, // Premium Room + 'club' => 15, // Club Room + 'swim up' => 46, // Swim Up Room + 'swim-up' => 46, + 'corner' => 49, // Corner Room + 'large' => 47, // Standard Large Room + ); + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 5: Booking.com'daki oda bilgilerinden oda ekle + // + // Kaynak 1: detail['block'][] → room_name, max_occupancy, nr_adults, + // room_surface_in_m2, number_of_bathrooms + // Kaynak 2: detail['rooms'][id] → bed_configurations, highlights (view) + // ───────────────────────────────────────────────────────────────────────────── + private function insertRooms($propertyId, $userId, array $detail) + { + $propertyRoomService = app(\App\Core\Service\PropertyRoomService::class); + + // ── Kaynak 1: block[] → her room_id için tek kayıt ────────────────────── + $blockByRoomId = array(); + $blockRaw = isset($detail['block']) ? $detail['block'] : array(); + foreach ($blockRaw as $b) { + $rid = isset($b['room_id']) ? $b['room_id'] : null; + if ($rid && !isset($blockByRoomId[$rid])) { + $blockByRoomId[$rid] = $b; + } + } + + // ── Kaynak 2: rooms[room_id] → bed configs + view highlights ──────────── + $roomsRaw = isset($detail['rooms']) ? $detail['rooms'] : array(); + + // rooms[] boşsa sadece block verisini kullan + if (empty($roomsRaw) && empty($blockByRoomId)) { + // Hiç oda yoksa tek generic oda + $blockByRoomId = array( + 0 => array( + 'room_name' => 'Standard Room', + 'max_occupancy' => 2, + 'nr_adults' => 2, + 'room_surface_in_m2' => null, + 'number_of_bathrooms' => null, + ), + ); + } + + // rooms[] boşsa block ID'lerini rooms olarak kullan + if (empty($roomsRaw)) { + foreach ($blockByRoomId as $rid => $b) { + $roomsRaw[$rid] = array(); + } + } + if (empty($blockByRoomId)) { + foreach (array_keys($roomsRaw) as $rid) { + $blockByRoomId[$rid] = array(); + } + } + + // Distinct room_id birleşimi + $allRoomIds = array_unique(array_merge(array_keys($blockByRoomId), array_keys($roomsRaw))); + + $inserted = array(); + + foreach ($allRoomIds as $roomId) { + $block = isset($blockByRoomId[$roomId]) ? $blockByRoomId[$roomId] : array(); + $room = isset($roomsRaw[$roomId]) ? $roomsRaw[$roomId] : array(); + + // ── Oda adı ────────────────────────────────────────────────────────── + $roomName = trim(isset($block['room_name']) ? $block['room_name'] : 'Standard Room'); + + // ── room_type_id: oda adındaki anahtar kelimeye göre ───────────────── + $roomTypeId = $this->guessRoomTypeId($roomName); + + // ── Kapasite ───────────────────────────────────────────────────────── + $maxOccupancy = isset($block['max_occupancy']) ? (int)$block['max_occupancy'] : 2; + $maxAdult = isset($block['nr_adults']) ? (int)$block['nr_adults'] : $maxOccupancy; + $maxChild = isset($block['nr_children']) ? (int)$block['nr_children'] : 0; + + // ── Oda boyutu ─────────────────────────────────────────────────────── + // Validator required|max:30 → null yerine 0 gönder + $roomSizeRaw = isset($block['room_surface_in_m2']) ? (int)$block['room_surface_in_m2'] : 0; + $roomSize = $roomSizeRaw > 0 ? $roomSizeRaw : 0; + $roomSizeType = $roomSizeRaw > 0 ? 1 : 0; // 1 = m² + + // ── Oda adedi (kaç adet bu tipten var) ────────────────────────────── + $roomTypeCount = isset($block['room_count']) && $block['room_count'] > 0 + ? (int)$block['room_count'] : 1; + + // ── Banyo sayısı ───────────────────────────────────────────────────── + $bathroomCount = isset($block['number_of_bathrooms']) && $block['number_of_bathrooms'] > 0 + ? (int)$block['number_of_bathrooms'] : null; + + // ── Açıklama ───────────────────────────────────────────────────────── + $description = isset($room['description']) ? $room['description'] : null; + + // ── Yatak konfigürasyonları → room_bed_group ───────────────────────── + // Sistem formatı: [[{bed_type_id:9}, {bed_type_id:9}], [{bed_type_id:2}]] + // Her conf = bir grup, içindeki bed_types[].count kadar tekrar + $bedConfigs = isset($room['bed_configurations']) ? $room['bed_configurations'] : array(); + $roomBedGroup = array(); + + foreach ($bedConfigs as $conf) { + $groupBeds = array(); + $bedTypes = isset($conf['bed_types']) ? $conf['bed_types'] : array(); + foreach ($bedTypes as $bt) { + $bookingBedId = isset($bt['bed_type']) ? (int)$bt['bed_type'] : 0; + $ourBedTypeId = isset($this->bedTypeMap[$bookingBedId]) ? $this->bedTypeMap[$bookingBedId] : null; + $bedCount = isset($bt['count']) ? (int)$bt['count'] : 1; + if ($ourBedTypeId) { + for ($i = 0; $i < $bedCount; $i++) { + $groupBeds[] = array('bed_type_id' => $ourBedTypeId); + } + } + } + if (!empty($groupBeds)) { + $roomBedGroup[] = $groupBeds; + } + } + + // ── View tipleri → room_view_type ──────────────────────────────────── + // Booking.com highlights'daki "translated_name" → viewTypeMap eşleşmesi + // Eşleşmezse icon'a bak: "city"→10, "sea"→4, "pool"→2, "mountain"→19 + $iconViewMap = array( + 'city' => 10, + 'sea' => 4, + 'ocean' => 22, + 'pool' => 2, + 'mountain' => 19, + 'garden' => 13, + 'park' => 1, + 'river' => 3, + 'lake' => 17, + 'beach' => 9, + 'forest' => 24, + 'harbor' => 15, + 'marina' => 18, + 'eye' => 4, // Booking.com Sea view ikonu "eye" olarak geliyor + 'landmark' => 30, // Landmark view + ); + $highlights = isset($room['highlights']) ? $room['highlights'] : array(); + $roomViewType = array(); + foreach ($highlights as $h) { + $hName = mb_strtolower(trim(isset($h['translated_name']) ? $h['translated_name'] : '')); + $hIcon = mb_strtolower(trim(isset($h['icon']) ? $h['icon'] : '')); + $viewId = null; + // 1) Tam eşleşme: "city view" + if (isset($this->viewTypeMap[$hName])) { + $viewId = $this->viewTypeMap[$hName]; + } + // 2) Kısmi eşleşme: highlight adında keyword geçiyor + if (!$viewId) { + foreach ($this->viewTypeMap as $keyword => $vid) { + $base = str_replace(' view', '', $keyword); + if (strpos($hName, $base) !== false) { + $viewId = $vid; + break; + } + } + } + // 3) Icon eşleşmesi: "city" ikonu → 10 + if (!$viewId && isset($iconViewMap[$hIcon])) { + $viewId = $iconViewMap[$hIcon]; + } + if ($viewId && !in_array($viewId, $roomViewType)) { + $roomViewType[] = $viewId; + } + } + + // ── Service'e gönder ───────────────────────────────────────────────── + $roomParams = array( + 'property_id' => $propertyId, + 'user_id' => $userId, + 'locale' => 'en', + 'name' => $roomName, + 'room_type_id' => $roomTypeId, + 'max_occupancy' => $maxOccupancy, + 'max_adult' => $maxAdult, + 'max_child' => $maxChild, + 'occupancy_lock' => 0, + 'exclude_occupancy' => 0, + 'room_size' => $roomSize, + 'room_size_type' => $roomSizeType, + 'room_type_count' => $roomTypeCount, + 'bathroom_count' => $bathroomCount, + 'toilet_count' => null, + 'lounge_count' => null, + 'room_count' => null, + 'max_child_number' => null, + 'description' => array( + array('language_code' => 'en', 'description' => $description) + ), + 'room_bed_group' => $roomBedGroup, + 'room_view_type' => $roomViewType, + 'is_connected_room' => null, + 'is_connected_room_price' => null, + 'is_connected_room_availability' => null, + 'connected_rooms' => array(), + ); + + $result = $propertyRoomService->addPropertyRoomAndBed($roomParams); + $isSaved = ($result['status'] == 'success' || $result['status'] === true); + $facSummary = array(); + $photoSummary = array(); + + if ($isSaved) { + // addPropertyRoomAndBed data döndürmüyor — son eklenen room_id'yi çek + $newRoom = DB::table('property_room') + ->where('property_id', $propertyId) + ->where('name', $roomName) + ->orderBy('id', 'desc') + ->first(); + $newRoomId = $newRoom ? $newRoom->id : null; + + if ($newRoomId) { + // Oda olanakları → property_room_fact_mapping + if (!empty($room['facilities'])) { + $facSummary = $this->mapAndSaveRoomFacilities( + $newRoomId, + $propertyId, + $userId, + $room['facilities'] + ); + } + + // Oda fotoğrafları → property_photo + property_room_photo_mapping + if (!empty($room['photos'])) { + $photoSummary = $this->mapAndSaveRoomPhotos( + $newRoomId, + $propertyId, + $userId, + $room['photos'], + isset($detail['hotel_id']) ? $detail['hotel_id'] : 0 + ); + } + } + } + + $inserted[] = array( + 'room_id' => $roomId, + 'room_name' => $roomName, + 'room_type_id' => $roomTypeId, + 'beds' => array_map(function ($g) { + return $g; + }, $roomBedGroup), + 'views' => $roomViewType, + 'room_facts' => $facSummary, + 'room_photos' => $photoSummary, + 'saved' => $isSaved, + 'message' => !$isSaved ? $result['message'] : null, + ); + } + + return array( + 'room_count' => count($inserted), + 'rooms' => $inserted, + ); + } + + // ───────────────────────────────────────────────────────────────────────────── + // Oda fotoğraflarını indir, property_photo'ya kaydet, room_photo_mapping'e bağla + // Booking.com: rooms[id]['photos'] → [{url_original, url_max750, ...}] + // ───────────────────────────────────────────────────────────────────────────── + private function mapAndSaveRoomPhotos($roomId, $propertyId, $userId, array $photos, $hotelId) + { + if (empty($photos)) { + return array('saved_count' => 0, 'skipped_count' => 0); + } + + $uploadRoot = rtrim(Config::get('app.fileSystemDriver'), '/'); + $urlPath = '/property-photos/' . $propertyId . '/'; + $filePath = $uploadRoot . $urlPath; + + if (!File::exists($filePath)) { + File::makeDirectory($filePath, 0777, true); + } + + $saved = array(); + $skipped = array(); + $index = 0; + + foreach ($photos as $photo) { + // URL: url_original > url_max750 > url_max1280 > url_max300 + $url = ''; + foreach (array('url_original', 'url_max750', 'url_max1280', 'url_max300') as $key) { + if (!empty($photo[$key])) { + $url = $photo[$key]; + break; + } + } + if ($url === '') { + $skipped[] = 'empty_url'; + continue; + } + + try { + $photoId = isset($photo['photo_id']) ? $photo['photo_id'] : ''; + $fileName = time() . '_bk' . $hotelId . '_r' . $roomId . '_' . $index; + $fileExt = 'jpg'; + $tempPath = sys_get_temp_dir() . '/' . $fileName . '_orig.jpg'; + + $this->downloadRawImage($url, $tempPath); + + $image = Image::make($tempPath); + $imageWidth = $image->width(); + $imageHeight = $image->height(); + $fileSize = (int) ceil(filesize($tempPath) / 1024); + $quality = 80; + + // Orijinal — 2000px üzeriyse küçült + if ($imageHeight > 2000 || $imageWidth > 2000) { + if ($imageHeight > $imageWidth) { + $r = $imageHeight / 2000; + $image->fit((int)($imageWidth / $r), 2000); + } else { + $r = $imageWidth / 2000; + $image->fit(2000, (int)($imageHeight / $r)); + } + } + $image->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt); + + // _medium (max 1600x768) + $mediumImage = Image::make($tempPath); + $ratio = $imageHeight > 0 ? ($imageHeight / 768) : 1; + $resizeWidth = (int)($imageWidth / max($ratio, 1)); + if ($resizeWidth > 1599) { + $mediumImage->fit(1600, 768); + } else { + $mediumImage->fit(max($resizeWidth, 1), 768); + } + $mediumImage->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '_medium.' . $fileExt); + + // _thumbnail (300x300) + Image::make($tempPath) + ->fit(300, 300) + ->encode($fileExt, $quality) + ->orientate() + ->save($filePath . $fileName . '_thumbnail.' . $fileExt); + + if (File::exists($tempPath)) { + File::delete($tempPath); + } + + $isCompatible = ($imageWidth >= 1150 && $imageHeight >= 600) ? 1 : 0; + + $photoRecord = PropertyPhoto::create(array( + 'property_id' => $propertyId, + 'property_photo_category_id' => 1, + 'photo_path' => $urlPath, + 'photo_name' => $fileName, + 'file_size' => $fileSize, + 'file_ext' => $fileExt, + 'photo_resolution' => $imageWidth . 'x' . $imageHeight, + 'is_default' => 0, + 'is_compatible_with_myweb_slider' => $isCompatible, + 'photo_order' => 0, + 'photo_rank' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + )); + + // property_room_photo_mapping'e bağla + $alreadyLinked = DB::table('property_room_photo_mapping') + ->where('room_id', $roomId) + ->where('photo_id', $photoRecord->id) + ->exists(); + + if (!$alreadyLinked) { + DB::table('property_room_photo_mapping')->insert(array( + 'property_id' => $propertyId, + 'room_id' => $roomId, + 'photo_id' => $photoRecord->id, + 'created_by' => $userId, + 'updated_by' => $userId, + 'created_at' => time(), + 'updated_at' => time(), + )); + } + + $saved[] = array( + 'photo_id' => $photoRecord->id, + 'file_name' => $fileName . '.' . $fileExt, + 'resolution' => $imageWidth . 'x' . $imageHeight, + 'source_url' => $url, + ); + } catch (Exception $e) { + Log::error('propertyInsert.roomPhoto [room=' . $roomId . ', i=' . $index . ']: ' . $e->getMessage()); + $skipped[] = $url; + } + + $index++; + } + + return array( + 'saved_count' => count($saved), + 'skipped_count' => count($skipped), + 'photos' => $saved, + ); + } + + // ───────────────────────────────────────────────────────────────────────────── + // Oda olanaklarını (room facilities) property_room_fact_mapping'e kaydet + // Booking.com: rooms[id]['facilities'] → [{id, name, alt_facilitytype_id, ...}] + // View tiplerini atla (viewTypeMap ile zaten ekleniyor) + // ───────────────────────────────────────────────────────────────────────────── + private function mapAndSaveRoomFacilities($roomId, $propertyId, $userId, array $facilities) + { + // View kategorisi facilitytype_id'leri (9=View, 14=View alt) — zaten view mapping'de ekleniyor + $skipFacilityTypeIds = array(9, 14); + + // Sistemdeki tüm fact'leri küçük harfli isimle index'e al (type=1: property/room fact) + $allFacts = PropertyFact::select(array('id', 'name', 'type'))->where('type', 1)->get(); + $exactIndex = array(); + foreach ($allFacts as $fact) { + $key = mb_strtolower(trim($fact->name)); + // Aynı isimde birden fazla fact varsa daha küçük id'liyi (ilk ekleneni) tut + if (!isset($exactIndex[$key])) { + $exactIndex[$key] = $fact; + } + } + + // roomAliasMap (oda özel) + aliasMap (genel) birleştir, room öncelikli + $combinedAlias = array_merge($this->aliasMap, $this->roomAliasMap); + + $saved = array(); + $skipped = array(); + + foreach ($facilities as $fac) { + // View tiplerini atla + $faciltypeId = isset($fac['facilitytype_id']) ? (int)$fac['facilitytype_id'] : 0; + $altFaciltypeId = isset($fac['alt_facilitytype_id']) ? (int)$fac['alt_facilitytype_id'] : 0; + if (in_array($faciltypeId, $skipFacilityTypeIds) || in_array($altFaciltypeId, $skipFacilityTypeIds)) { + continue; + } + + $name = trim(isset($fac['name']) ? $fac['name'] : ''); + if ($name === '') { + continue; + } + + $lower = mb_strtolower($name); + // Alias map'te varsa o karşılığı kullan, yoksa direkt ismi kullan + $searchKey = isset($combinedAlias[$lower]) ? mb_strtolower($combinedAlias[$lower]) : $lower; + $fact = null; + + // Sadece birebir eşleşme — tahmin yok, LIKE yok + if (isset($exactIndex[$searchKey])) { + $fact = $exactIndex[$searchKey]; + } + + if ($fact) { + // Yalnızca mevcut property_room_fact_mapping tablosuna yaz, yeni kayıt türü oluşturma + $exists = DB::table('property_room_fact_mapping') + ->where('property_id', $propertyId) + ->where('room_id', $roomId) + ->where('fact_id', $fact->id) + ->exists(); + + if (!$exists) { + DB::table('property_room_fact_mapping')->insert(array( + 'property_id' => $propertyId, + 'room_id' => $roomId, + 'fact_id' => $fact->id, + 'is_feature' => 0, + 'created_by' => $userId, + 'updated_by' => $userId, + 'created_at' => time(), + 'updated_at' => time(), + )); + } + + $saved[] = array( + 'booking_name' => $name, + 'fact_id' => $fact->id, + 'fact_name' => $fact->name, + 'via_alias' => ($searchKey !== $lower), + ); + } else { + // Eşleşme yok — sisteme kesinlikle bir şey yazma, sadece logla + Log::warning('propertyInsert.roomFacility.noMatch', array( + 'room_id' => $roomId, + 'property_id' => $propertyId, + 'booking_name' => $name, + 'search_key' => $searchKey, + 'hint' => 'roomAliasMap veya aliasMap\'e eklenebilir', + )); + $skipped[] = $name; + } + } + + return array( + 'saved_count' => count($saved), + 'skipped_count' => count($skipped), + 'saved' => $saved, + 'skipped' => $skipped, + ); + } + + // ───────────────────────────────────────────────────────────────────────────── + // Oda adındaki anahtar kelimelere göre room_type_id tahmin et + // ───────────────────────────────────────────────────────────────────────────── + private function guessRoomTypeId($roomName) + { + $lower = mb_strtolower($roomName); + + // Önce çift kelimeli eşleşmeleri dene (daha spesifik) + foreach ($this->roomTypeKeywords as $keyword => $typeId) { + if (strpos($keyword, ' ') !== false && strpos($lower, $keyword) !== false) { + return $typeId; + } + } + // Sonra tek kelimeli + foreach ($this->roomTypeKeywords as $keyword => $typeId) { + if (strpos($keyword, ' ') === false && strpos($lower, $keyword) !== false) { + return $typeId; + } + } + + return 24; // Default: Standard Room + } + + // ───────────────────────────────────────────────────────────────────────────── + // ANA METOD: POST /bulut/property-insert + // ───────────────────────────────────────────────────────────────────────────── + public function insert(Request $request) + { + $hotelId = $request->input('hotel_id', ''); + $email = $request->input('email', ''); + $password = $request->input('password', ''); + $name = $request->input('name', ''); + $surname = $request->input('surname', ''); + $propertyName = $request->input('property_name', ''); + $phone = $request->input('phone', null); + $dryRun = (bool) $request->input('dry_run', false); + $checkin = $request->input('checkin', date('Y-m-d', strtotime('+30 days'))); + $checkout = $request->input('checkout', date('Y-m-d', strtotime('+31 days'))); + + // Zorunlu alan validasyonu + $required = array('hotel_id', 'email', 'password', 'name', 'surname', 'property_name'); + $missing = array(); + foreach ($required as $field) { + if (!$request->input($field)) { + $missing[] = $field; + } + } + if (count($missing)) { + return response()->json(array( + 'status' => false, + 'message' => 'Eksik alanlar: ' . implode(', ', $missing), + ), 422); + } + + $report = array( + 'hotel_id' => $hotelId, + 'dry_run' => $dryRun, + 'steps' => array(), + ); + + try { + DB::beginTransaction(); + + // ── ADIM 0: Booking.com'dan veri çek ───────────────────────────────── + $report['steps']['0_booking_fetch'] = array('status' => 'pending'); + $bookingData = $this->fetchBookingData($hotelId, $checkin, $checkout); + $detail = $bookingData['detail']; + $facilData = $bookingData['facilities']; + $photoData = $bookingData['photos']; + + $report['steps']['0_booking_fetch'] = array( + 'status' => 'success', + 'hotel_name' => isset($detail['hotel_name']) ? $detail['hotel_name'] : null, + 'facility_count' => $this->countFacilities($facilData), + 'photo_count' => count($this->extractPhotoUrls($photoData)), + ); + + // Eğer property_name boş geldiyse Booking.com adını kullan + if (!$propertyName && !empty($detail['hotel_name'])) { + $propertyName = $detail['hotel_name']; + } + + if ($dryRun) { + DB::rollBack(); + $report['dry_run_note'] = 'dry_run=true → DB\'ye hiçbir şey yazılmadı. Aşağıdaki veriler oluşturulacaktı.'; + $report['steps']['1_user_property'] = array('status' => 'skipped (dry_run)', 'email' => $email, 'property_name' => $propertyName); + $report['steps']['2_property_info'] = array('status' => 'skipped (dry_run)', 'data' => $this->previewPropertyInfo($detail)); + $report['steps']['3_contact'] = array('status' => 'skipped (dry_run)', 'phone' => $phone ?: (isset($detail['phone']) ? $detail['phone'] : null), 'email' => isset($detail['email']) ? $detail['email'] : null); + $report['steps']['4_fact_mapping'] = array('status' => 'skipped (dry_run)', 'facility_count' => $this->countFacilities($facilData)); + $report['steps']['5_rooms'] = array('status' => 'skipped (dry_run)'); + $report['steps']['6_photos'] = array('status' => 'skipped (dry_run)', 'photo_count' => count($this->extractPhotoUrls($photoData))); + return response()->json(array('status' => true, 'message' => 'Dry run tamamlandı', 'report' => $report)); + } + + // ── ADIM 1: Kullanıcı + Property oluştur ──────────────────────────── + $report['steps']['1_user_property'] = array('status' => 'pending'); + $userPropertyResult = $this->createUserAndProperty( + array('name' => $name, 'surname' => $surname, 'email' => $email, 'password' => $password), + $propertyName + ); + $createdUserId = $userPropertyResult['user']['id']; + $createdPropertyId = $userPropertyResult['property_id']; + + $report['steps']['1_user_property'] = array( + 'status' => 'success', + 'user_id' => $createdUserId, + 'property_id' => $createdPropertyId, + 'token' => $userPropertyResult['token'], + ); + + // ── ADIM 2: Property info güncelle ────────────────────────────────── + $report['steps']['2_property_info'] = array('status' => 'pending'); + $this->updatePropertyInfo($createdPropertyId, $createdUserId, $detail); + $raw2 = isset($detail['rawData']) ? $detail['rawData'] : array(); + $report['steps']['2_property_info'] = array( + 'status' => 'success', + 'hotel_name' => isset($detail['hotel_name']) ? $detail['hotel_name'] : null, + 'rating' => isset($raw2['accuratePropertyClass']) ? (int)$raw2['accuratePropertyClass'] : null, + 'country' => isset($detail['countrycode']) ? strtoupper($detail['countrycode']) : null, + 'checkin' => isset($raw2['checkin']['fromTime']) ? $raw2['checkin']['fromTime'] : null, + 'checkout' => isset($raw2['checkout']['untilTime']) ? $raw2['checkout']['untilTime'] : null, + 'languages' => isset($detail['spoken_languages']) ? $detail['spoken_languages'] : array(), + ); + + // ── ADIM 3: İletişim bilgilerini kaydet ────────────────────────────── + $report['steps']['3_contact'] = array('status' => 'pending'); + $this->updatePropertyContact($createdPropertyId, $createdUserId, $detail, $phone); + $report['steps']['3_contact'] = array( + 'status' => 'success', + 'phone' => $phone ?: (isset($detail['phone']) ? $detail['phone'] : null), + 'email' => isset($detail['email']) ? $detail['email'] : null, + ); + + // ── ADIM 4: Facility → Fact mapping ───────────────────────────────── + $report['steps']['4_fact_mapping'] = array('status' => 'pending'); + $facilityResult = $this->mapAndSaveFacilities($createdPropertyId, $createdUserId, $facilData); + $report['steps']['4_fact_mapping'] = array_merge(array('status' => 'success'), $facilityResult); + + // ── ADIM 5: Oda ekle ───────────────────────────────────────────────── + $report['steps']['5_rooms'] = array('status' => 'pending'); + $roomResult = $this->insertRooms($createdPropertyId, $createdUserId, $detail); + $report['steps']['5_rooms'] = array_merge(array('status' => 'success'), $roomResult); + + // ── ADIM 6: Görselleri indir ve kaydet ─────────────────────────────── + $report['steps']['6_photos'] = array('status' => 'pending'); + $photoLimit = $request->input('photo_limit', 20); + $photoResult = $this->processAndSavePhotos($photoData, $hotelId, $createdPropertyId, $photoLimit); + $report['steps']['6_photos'] = array_merge(array('status' => 'success'), $photoResult); + + DB::commit(); + + return response()->json(array( + 'status' => true, + 'message' => 'Property başarıyla oluşturuldu', + 'property_id' => $createdPropertyId, + 'user_id' => $createdUserId, + 'report' => $report, + )); + } catch (ApiErrorException $e) { + DB::rollBack(); + Log::error('propertyInsert.ApiError: ' . $e->getMessage()); + return response()->json(array( + 'status' => false, + 'message' => implode(', ', $e->getMessageArr()), + 'report' => $report, + ), 400); + } catch (Exception $e) { + DB::rollBack(); + Log::error('propertyInsert.Error: ' . $e->getFile() . ':' . $e->getLine() . ' ' . $e->getMessage()); + return response()->json(array( + 'status' => false, + 'message' => $e->getMessage(), + 'report' => $report, + ), 500); + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // ADIM 6: Fotograflari indir, isle ve kaydet + // ───────────────────────────────────────────────────────────────────────────── + private function processAndSavePhotos(array $responseData, $hotelId, $propertyId, $limit = 20) + { + $photoUrls = $this->extractPhotoUrls($responseData); + + if (empty($photoUrls)) { + return array( + 'saved_count' => 0, + 'total_in_api' => 0, + 'message' => 'API yanitinda fotograf URL bulunamadi', + ); + } + + $uploadRoot = rtrim(Config::get('app.fileSystemDriver'), '/'); + $urlPath = '/property-photos/' . $propertyId . '/'; + $filePath = $uploadRoot . $urlPath; + + if (!File::exists($filePath)) { + File::makeDirectory($filePath, 0777, true); + } + + $hasDefault = PropertyPhoto::where('property_id', $propertyId) + ->where('status', 1) + ->where('is_default', 1) + ->exists(); + + $saved = array(); + $isFirstPhoto = true; + $total = min(count($photoUrls), $limit); + + for ($i = 0; $i < $total; $i++) { + $photoUrl = $photoUrls[$i]; + try { + $fileName = time() . '_bk' . $hotelId . '_' . $i; + $fileExt = 'jpg'; + $tempPath = sys_get_temp_dir() . '/' . $fileName . '_orig.jpg'; + + $this->downloadRawImage($photoUrl, $tempPath); + + $image = Image::make($tempPath); + $imageWidth = $image->width(); + $imageHeight = $image->height(); + $fileSize = (int) ceil(filesize($tempPath) / 1024); + $quality = 80; + + // Orijinal — 2000px uzeriyse kucult + if ($imageHeight > 2000 || $imageWidth > 2000) { + if ($imageHeight > $imageWidth) { + $r = $imageHeight / 2000; + $image->fit((int)($imageWidth / $r), 2000); + } else { + $r = $imageWidth / 2000; + $image->fit(2000, (int)($imageHeight / $r)); + } + } + $image->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt); + + // _medium (max 1600x768) + $mediumImage = Image::make($tempPath); + $ratio = $imageHeight > 0 ? ($imageHeight / 768) : 1; + $resizeWidth = (int)($imageWidth / max($ratio, 1)); + if ($resizeWidth > 1599) { + $mediumImage->fit(1600, 768); + } else { + $mediumImage->fit(max($resizeWidth, 1), 768); + } + $mediumImage->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '_medium.' . $fileExt); + + // _thumbnail (300x300) + Image::make($tempPath) + ->fit(300, 300) + ->encode($fileExt, $quality) + ->orientate() + ->save($filePath . $fileName . '_thumbnail.' . $fileExt); + + if (File::exists($tempPath)) { + File::delete($tempPath); + } + + $isCompatible = ($imageWidth >= 1150 && $imageHeight >= 600) ? 1 : 0; + $isDefault = (!$hasDefault && $isFirstPhoto) ? 1 : 0; + $isFirstPhoto = false; + + $photoRecord = PropertyPhoto::create(array( + 'property_id' => $propertyId, + 'property_photo_category_id' => 1, + 'photo_path' => $urlPath, + 'photo_name' => $fileName, + 'file_size' => $fileSize, + 'file_ext' => $fileExt, + 'photo_resolution' => $imageWidth . 'x' . $imageHeight, + 'is_default' => $isDefault, + 'is_compatible_with_myweb_slider' => $isCompatible, + 'photo_order' => 0, + 'photo_rank' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + )); + + $saved[] = array( + 'db_id' => $photoRecord->id, + 'file_name' => $fileName . '.' . $fileExt, + 'resolution' => $imageWidth . 'x' . $imageHeight, + 'source_url' => $photoUrl, + ); + } catch (Exception $e) { + Log::error('propertyInsert.photo [' . $i . ']: ' . $e->getMessage() . ' | ' . $photoUrl); + $saved[] = array('error' => $e->getMessage(), 'source_url' => $photoUrl); + } + } + + $okCount = count(array_filter($saved, function ($s) { + return !isset($s['error']); + })); + + return array( + 'saved_count' => $okCount, + 'total_in_api' => count($photoUrls), + 'limit_applied' => $total, + 'photos' => $saved, + ); + } + + private function extractPhotoUrls(array $data) + { + $urls = array(); + + // Format 1: data[] dizisi + if (isset($data['data']) && is_array($data['data']) && !empty($data['data'])) { + foreach ($data['data'] as $item) { + if (!is_array($item)) { + continue; + } + if (!empty($item['url_original'])) { + $urls[] = $item['url_original']; + } elseif (!empty($item['url_max'])) { + $urls[] = $item['url_max']; + } elseif (!empty($item['url'])) { + $urls[] = $item['url']; + } elseif (!empty($item['photo']['url_original'])) { + $urls[] = $item['photo']['url_original']; + } + } + } + + // Format 2: data.photos[] + if (empty($urls) && isset($data['data']['photos']) && is_array($data['data']['photos'])) { + foreach ($data['data']['photos'] as $item) { + if (!empty($item['url_original'])) { + $urls[] = $item['url_original']; + } elseif (!empty($item['url'])) { + $urls[] = $item['url']; + } + } + } + + // Format 3: photos[] root'ta + if (empty($urls) && isset($data['photos']) && is_array($data['photos'])) { + foreach ($data['photos'] as $item) { + if (!empty($item['url_original'])) { + $urls[] = $item['url_original']; + } elseif (!empty($item['url'])) { + $urls[] = $item['url']; + } + } + } + + return $urls; + } + + private function downloadRawImage($url, $savePath) + { + $client = new Client(array( + 'timeout' => 30, + 'verify' => false, + 'headers' => array('User-Agent' => 'Mozilla/5.0'), + )); + $response = $client->get($url, array('sink' => $savePath)); + if ($response->getStatusCode() !== 200) { + throw new Exception('Image download failed HTTP ' . $response->getStatusCode() . ' | ' . $url); + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // Yardımcı: timezone string → general_timezone.id + // ───────────────────────────────────────────────────────────────────────────── + private function resolveTimezoneId($timezoneLocation) + { + $row = DB::table('general_timezone') + ->where('location', $timezoneLocation) + ->first(); + return $row ? (string)$row->id : null; + } + + // ───────────────────────────────────────────────────────────────────────────── + // Yardımcı: dry_run için property_info önizleme + // ───────────────────────────────────────────────────────────────────────────── + private function previewPropertyInfo(array $detail) + { + $raw = isset($detail['rawData']) ? $detail['rawData'] : array(); + $rating = null; + foreach (array('accuratePropertyClass', 'propertyClass', 'qualityClass') as $rk) { + if (!empty($raw[$rk])) { + $rating = (int)$raw[$rk]; + break; + } + } + return array( + 'name' => isset($detail['hotel_name']) ? $detail['hotel_name'] : null, + 'rating' => $rating, + 'country' => isset($detail['countrycode']) ? strtoupper($detail['countrycode']) : null, + 'address' => isset($detail['address']) ? $detail['address'] : null, + 'city' => isset($detail['city_name_en']) ? $detail['city_name_en'] : null, + 'district' => isset($detail['district']) ? $detail['district'] : null, + 'zip' => isset($detail['zip']) ? $detail['zip'] : null, + 'latitude' => isset($detail['latitude']) ? $detail['latitude'] : null, + 'longitude' => isset($detail['longitude']) ? $detail['longitude'] : null, + 'timezone' => isset($detail['timezone']) ? $detail['timezone'] : null, + 'checkin' => isset($raw['checkin']['fromTime']) ? $raw['checkin']['fromTime'] : null, + 'checkout' => isset($raw['checkout']['untilTime']) ? $raw['checkout']['untilTime'] : null, + 'languages' => isset($detail['spoken_languages']) ? $detail['spoken_languages'] : array(), + 'review_score' => isset($raw['reviewScore']) ? $raw['reviewScore'] : null, + ); + } + + // ───────────────────────────────────────────────────────────────────────────── + // Yardımcı: facility sayısını döndür + // ───────────────────────────────────────────────────────────────────────────── + private function countFacilities(array $facilData) + { + $count = 0; + if (isset($facilData['facilities'])) { + foreach ($facilData['facilities'] as $fac) { + $count += count(isset($fac['instances']) ? $fac['instances'] : array()); + } + } + return $count; + } + + // ───────────────────────────────────────────────────────────────────────────── + // GET /bulut/property-check?email=... + // Verilen email'e ait kullanıcı ve property bilgilerini döndür + // ───────────────────────────────────────────────────────────────────────────── + public function check(Request $request) + { + $email = trim($request->input('email', '')); + if (!$email) { + return response()->json(array('status' => false, 'message' => 'email parametresi gerekli'), 422); + } + + $user = DB::table('user')->where('email', $email)->first(); + if (!$user) { + return response()->json(array( + 'status' => false, + 'message' => 'Kullanıcı bulunamadı', + 'email' => $email, + )); + } + + // Kullanıcıya bağlı property'ler + $mappings = DB::table('user_property_mapping as upm') + ->join('property as p', 'p.id', '=', 'upm.property_id') + ->where('upm.user_id', $user->id) + ->select(array( + 'p.id as property_id', + 'p.name as property_name', + 'p.status as property_status', + 'upm.created_at', + )) + ->get(); + + $properties = array(); + foreach ($mappings as $m) { + $roomCount = DB::table('property_room')->where('property_id', $m->property_id)->count(); + $photoCount = DB::table('property_photo')->where('property_id', $m->property_id)->count(); + $factCount = DB::table('property_fact_mapping')->where('property_id', $m->property_id)->count(); + + $properties[] = array( + 'property_id' => $m->property_id, + 'property_name' => $m->property_name, + 'status' => $m->property_status, + 'room_count' => $roomCount, + 'photo_count' => $photoCount, + 'fact_count' => $factCount, + 'created_at' => date('Y-m-d H:i:s', $m->created_at), + ); + } + + return response()->json(array( + 'status' => true, + 'user_id' => $user->id, + 'email' => $user->email, + 'name' => $user->name . ' ' . $user->surname, + 'user_status' => $user->status, + 'properties' => $properties, + )); + } + + // ───────────────────────────────────────────────────────────────────────────── + // DELETE /bulut/property-delete + // Body: { "property_id": 123 } veya { "email": "..." } (emailde tüm property'ler) + // Odalar, fotoğraflar (dosya dahil), fact mapping, iletişim, user-property mapping, + // property kaydı ve kullanıcı silinir. + // ───────────────────────────────────────────────────────────────────────────── + public function delete(Request $request) + { + $propertyId = $request->input('property_id'); + $email = trim($request->input('email', '')); + + if (!$propertyId && !$email) { + return response()->json(array('status' => false, 'message' => 'property_id veya email gerekli'), 422); + } + + $report = array(); + + try { + DB::beginTransaction(); + + // Email ile geldiyse önce kullanıcı + property_id'leri bul + $userIdToDelete = null; + $propertyIds = array(); + + if ($email) { + $user = DB::table('user')->where('email', $email)->first(); + if (!$user) { + return response()->json(array('status' => false, 'message' => 'Kullanıcı bulunamadı')); + } + $userIdToDelete = $user->id; + $pids = DB::table('user_property_mapping') + ->where('user_id', $userIdToDelete) + ->pluck('property_id') + ->toArray(); + $propertyIds = $pids; + } else { + $propertyIds = array((int)$propertyId); + // Bu property'nin tek sahibi varsa onu da sil + $ownerCount = DB::table('user_property_mapping') + ->where('property_id', $propertyId) + ->count(); + if ($ownerCount === 1) { + $userIdToDelete = DB::table('user_property_mapping') + ->where('property_id', $propertyId) + ->value('user_id'); + } + } + + foreach ($propertyIds as $pid) { + $deleted = array('property_id' => $pid); + + // 1. Fotoğraf dosyalarını sil + $photos = DB::table('property_photo')->where('property_id', $pid)->get(); + $filesDeleted = 0; + $uploadRoot = rtrim(Config::get('app.fileSystemDriver'), '/'); + foreach ($photos as $photo) { + $base = $uploadRoot . $photo->photo_path . $photo->photo_name; + foreach (array('', '_medium', '_thumbnail') as $suffix) { + $path = $base . $suffix . '.' . $photo->file_ext; + if (File::exists($path)) { + File::delete($path); + $filesDeleted++; + } + } + } + $deleted['photos_db'] = DB::table('property_photo')->where('property_id', $pid)->delete(); + $deleted['photos_files'] = $filesDeleted; + + // 2. Oda ilişkileri + $roomIds = DB::table('property_room')->where('property_id', $pid)->pluck('id')->toArray(); + if (!empty($roomIds)) { + DB::table('property_room_bed')->whereIn('room_id', $roomIds)->delete(); + DB::table('property_room_view_mapping')->whereIn('room_id', $roomIds)->delete(); + DB::table('property_room_fact_mapping')->whereIn('room_id', $roomIds)->delete(); + DB::table('property_room_photo_mapping')->whereIn('room_id', $roomIds)->delete(); + } + $deleted['rooms'] = DB::table('property_room')->where('property_id', $pid)->delete(); + + // 3. Fact mapping + $deleted['fact_mapping'] = DB::table('property_fact_mapping')->where('property_id', $pid)->delete(); + + // 4. İletişim + $deleted['contact'] = DB::table('property_contact')->where('property_id', $pid)->delete(); + + // 5. Property info tabloları + DB::table('property_additional_info')->where('property_id', $pid)->delete(); + DB::table('property_language_spoken')->where('property_id', $pid)->delete(); + + // 6. User-property mapping + $deleted['user_mapping'] = DB::table('user_property_mapping')->where('property_id', $pid)->delete(); + + // 7. Property kaydı + $deleted['property'] = DB::table('property')->where('id', $pid)->delete(); + + $report[] = $deleted; + } + + // 8. Kullanıcıyı sil (başka property'si yoksa) + if ($userIdToDelete) { + $remaining = DB::table('user_property_mapping')->where('user_id', $userIdToDelete)->count(); + if ($remaining === 0) { + DB::table('api_access_token')->where('user_id', $userIdToDelete)->delete(); + DB::table('user')->where('id', $userIdToDelete)->delete(); + $report['user_deleted'] = $userIdToDelete; + } + } + + DB::commit(); + + return response()->json(array( + 'status' => true, + 'message' => 'Silme tamamlandı', + 'report' => $report, + )); + } catch (Exception $e) { + DB::rollBack(); + Log::error('propertyDelete.Error: ' . $e->getFile() . ':' . $e->getLine() . ' ' . $e->getMessage()); + return response()->json(array( + 'status' => false, + 'message' => $e->getMessage(), + 'report' => $report, + ), 500); + } + } +} diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php new file mode 100644 index 0000000..fc2dd11 --- /dev/null +++ b/app/Http/Middleware/Authenticate.php @@ -0,0 +1,44 @@ +auth = $auth; + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @param string|null $guard + * @return mixed + */ + public function handle($request, Closure $next, $guard = null) + { + if ($this->auth->guard($guard)->guest()) { + return response('Unauthorized.', 401); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/BookingEngineTokenMiddleware.php b/app/Http/Middleware/BookingEngineTokenMiddleware.php new file mode 100644 index 0000000..bfa1ce4 --- /dev/null +++ b/app/Http/Middleware/BookingEngineTokenMiddleware.php @@ -0,0 +1,168 @@ +propertyWebService = $propertyWebService; + $this->channelService = $channelService; + $this->propertyChannelMappingService = $propertyChannelMappingService; + $this->propertyBookingEngineService = $propertyBookingEngineService; + $this->findCountryCodeService = $findCountryCodeService; + } + + public function handle($request, Closure $next, $guard = null) + { + $channelToken = $request->header('channelToken'); + $bookingEngineToken = $request->header('bookingEngineToken'); + + if (!$channelToken) { + return apiResponse(0, 'Token not provided.', null, 401); + } + + $channelRequest = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => $channelToken], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + + $channelCheck = $this->channelService->select($channelRequest); + + if ($channelCheck['status'] != 'success' || empty($channelCheck['data'])) { + return apiResponse(0, 'Channel Token not found.', null, 401); + } + + + $bookingEnginePropertyId = null; + if (in_array($channelCheck['data']['channel_category_id'], [2, 3, 7])) { + + if (is_null($bookingEngineToken)) { + if (!in_array($channelCheck['data']['channel_category_id'], [7])) { + return apiResponse(0, 'Booking Engine Token not found.', null, 401); + } + } + + $bookingEngineRequest = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => $bookingEngineToken], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'firstRow' => 1 + ]; + + $bookingEngineCheck = $this->propertyBookingEngineService->select($bookingEngineRequest); + + if ($bookingEngineCheck['status'] != 'success' || empty($bookingEngineCheck['data'])) { + if (!in_array($channelCheck['data']['channel_category_id'], [7])) { + return apiResponse(0, 'Booking Engine Token not found.', null, 401); + } + } + + $bookingEnginePropertyId = isset($bookingEngineCheck['data']['property_id']) ? $bookingEngineCheck['data']['property_id'] : null; + + + //channelToken Manipulation + $params = json_decode($request->getContent(), 1); + + if (fillOnUndefined($params, 'ipAddress') && fillOnUndefined($params,'referrer') != 'google') { + // Find Country Code with IP + $ipResponse = $this->findCountryCodeService->findCountryWithIpAddress($params['ipAddress']); + + if ($ipResponse['status'] == 'success') { + + $propertyChannelMappingParam = [ + 'criteria' => [ + ['field' => 'property_id', 'condition' => '=', 'value' => $bookingEnginePropertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['channel'], + ]; + + $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingParam); + + $ipCountryCode = isset($ipResponse['data']['code']) ? $ipResponse['data']['code'] : 'tr'; + + if ($propertyChannelMapping['status'] == 'success') { + + $propertyChannelMappingCollect = collect($propertyChannelMapping['data']); + $countryChannel = $propertyChannelMappingCollect + ->where('channel.channel_category_id', 3) + ->where('channel.country_code', $ipCountryCode) + ->where('channel.parent_id', 1) + ->first(); + + if (!empty($countryChannel)) { + $channelToken = $countryChannel['channel']['token']; + $channelCheck['data']['id'] = $countryChannel['channel']['id']; + } + + //countryCodeGroup + if (empty($countryChannel)) { + $countryChannelGroup = $propertyChannelMappingCollect + ->where('channel.channel_category_id', 3) + ->where('channel.country_code', 'group') + ->where('channel.parent_id', 1) + ->toArray(); + + if (!empty($countryChannelGroup)) { + foreach ($countryChannelGroup as $country) { + if (!empty($country['channel']['country_code_group'])) { + if (in_array($ipCountryCode, $country['channel']['countryCodeGroupArray'])) { + + $channelToken = $country['channel']['token']; + $channelCheck['data']['id'] = $country['channel']['id']; + + break; + } + } + } + } + } + //countryCodeGroup + + } + + } + //channelToken Manipulation + } + + } + + $request->channelId = $channelCheck['data']['id']; + $request->channelToken = $channelToken; + $request->bookingEngineToken = $bookingEngineToken; + $request->bookingEnginePropertyId = $bookingEnginePropertyId; + $request->bookingEngineChannelCategoryId = $channelCheck['data']['channel_category_id']; + + + return $next($request); + } +} diff --git a/app/Http/Middleware/CheckPropertyChannelConnectionMiddleware.php b/app/Http/Middleware/CheckPropertyChannelConnectionMiddleware.php new file mode 100644 index 0000000..647244e --- /dev/null +++ b/app/Http/Middleware/CheckPropertyChannelConnectionMiddleware.php @@ -0,0 +1,58 @@ +propertyChannelMappingService = $propertyChannelMappingService; + $this->request = $request; + $this->response = $response; + } + + + public function handle($request, Closure $next, $guard = null) + { + + //TODO: Buraya kanal (channel_id) ve kanal grupları (channel_group_id) için property için bir kontrol koyulacak + //dd($this->request->params['property_id']); + + /*$response = $next($request); + $propertyId = $request->property_id ? $request->property_id : fillOnUndefined($request->params, 'property_id'); + $channelId = fillOnUndefined($request->params, 'channel_id'); + + $checkParams = [ + 'property_id' => $propertyId, + 'channel_id' => $channelId, + + ] ; + + $checkMappingStatus =$this->propertyChannelMappingService->checkPropertyChannelMapping($checkParams); + if ($checkMappingStatus['status'] != 'success') { + return apiResponse(false, $checkMappingStatus['message'], null, 400); + } + + return $response;*/ + + return $next($request); + } + +} diff --git a/app/Http/Middleware/ContentWizardMiddleware.php b/app/Http/Middleware/ContentWizardMiddleware.php new file mode 100644 index 0000000..0dac122 --- /dev/null +++ b/app/Http/Middleware/ContentWizardMiddleware.php @@ -0,0 +1,56 @@ +propertyConfigService = $propertyConfigService; + $this->request = $request; + $this->response = $response; + } + + + public function handle($request, Closure $next, $guard = null) + { + $response = $next($request); + if ($response->getData()->status == 200) { + $params = $this->request->params; + $url = collect($this->request->route()); + $getNameArray = $url->where('as', '!=', null)->first(); + $routeAlias = fillOnUndefined($getNameArray, 'as'); + + if($routeAlias == 'Property.Contact.Update'){ + if(isset($params['contact']['address']) && isset($params['contact']['latitude']) && isset($params['contact']['longitude'])){ + $routeAlias = 'Property.Location.Update' ; + } + } + $rateParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $this->request->credentials->user_id, + 'property_rate_for' => $routeAlias, + ]; + $this->propertyConfigService->rateProperty(array_merge($params, $rateParams)); + } + return $response; + } + +} diff --git a/app/Http/Middleware/CorsMiddleware.php b/app/Http/Middleware/CorsMiddleware.php new file mode 100644 index 0000000..66209d2 --- /dev/null +++ b/app/Http/Middleware/CorsMiddleware.php @@ -0,0 +1,35 @@ + '*', + 'Access-Control-Allow-Methods' => 'POST, GET, OPTIONS, PUT, DELETE', + 'Access-Control-Allow-Credentials' => 'true', + 'Access-Control-Max-Age' => '86400', + 'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With, language, authToken' + ]; + + $apiHeader = collect($request->headers)->toArray(); + + if ($request->isMethod('OPTIONS')) + { + return response()->json('{"method":"OPTIONS"}', 200, $headers); + } + $response = $next($request); + foreach($headers as $key => $value) + { + //$response->header($key, $value); + $response->headers->set($key, $value); + } + return $response; + } +} diff --git a/app/Http/Middleware/ExampleMiddleware.php b/app/Http/Middleware/ExampleMiddleware.php new file mode 100644 index 0000000..7e9b7a8 --- /dev/null +++ b/app/Http/Middleware/ExampleMiddleware.php @@ -0,0 +1,20 @@ +apiAccessTokenService = $apiAccessTokenService ; + } + + public function handle($request, Closure $next, $guard = null) + { + $token = $request->header('authToken'); + + if (!$token) { + return apiResponse(0, 'Token not provided.', null, 401); + } + + try { + $credentials = JWT::decode($token, Config::get('app.jwt.secret'), ['HS256']); + + $findTokenCriteria = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => md5($token) ], + ['field' => 'expire_date', 'condition' => '>', 'value' => time() ], + ['field' => 'user_id', 'condition' => '=', 'value' => $credentials->user_id ], + ['field' => 'invalidate', 'condition' => '=', 'value' => 0 ], + ], + 'firstRow' => 1 + ]; + $getTokenData = $this->apiAccessTokenService->select($findTokenCriteria); + if(!$getTokenData['data']){ + throw new ExpiredException(); + } + + } catch (ExpiredException $e) { + return apiResponse(0, lang('Token is expired.'), null, 401); + } catch (Exception $e) { + return apiResponse(0, lang('An error while decoding token.'), null, 500); + } + + + $inputs = json_decode($request->getContent(), true); + $inputs = is_array($inputs) ? $inputs : ["params" => []]; + + $user = User::find($credentials->user_id); + + // Now let's put the user in the request class so that you can grab it from there + $request->credentials = $credentials; + $request->body = $inputs; + $request->auth = $user; + return $next($request); + } +} diff --git a/app/Http/Middleware/LanguageSettingMiddleware.php b/app/Http/Middleware/LanguageSettingMiddleware.php new file mode 100644 index 0000000..d118b40 --- /dev/null +++ b/app/Http/Middleware/LanguageSettingMiddleware.php @@ -0,0 +1,27 @@ +headers)->toArray(); + if(!isset($apiHeader['language'])){ + return apiResponse(0, 'Language field is null.', null, 400); + } + $apiRequest = collect($request->params)->toArray(); + $apiRequest['locale'] = isset($apiRequest['locale']) ? $apiRequest['locale'] : reset($apiHeader['language']); + LanguageService::setCurrentLanguage(reset($apiHeader['language'])); + $request->params = $apiRequest; + app('translator')->setLocale(reset($apiHeader['language'])); + + return $next($request); + } +} diff --git a/app/Http/Middleware/MyWebTokenMiddleware.php b/app/Http/Middleware/MyWebTokenMiddleware.php new file mode 100644 index 0000000..7fed64e --- /dev/null +++ b/app/Http/Middleware/MyWebTokenMiddleware.php @@ -0,0 +1,67 @@ +propertyWebService = $propertyWebService ; + } + + public function handle($request, Closure $next, $guard = null) + { + $token = $request->header('authToken'); + + if (!$token) { + return apiResponse(0, 'Token not provided.', null, 401); + } + + try { + + $findTokenCriteria = [ + 'criteria' => [ + ['field' => 'token', 'condition' => '=', 'value' => $token], + ], + 'firstRow' => 1 + ]; + $getTokenData = $this->propertyWebService->select($findTokenCriteria); + + if(!$getTokenData['data']){ + throw new ExpiredException(); + } + + } catch (ExpiredException $e) { + return apiResponse(0, lang('Token is expired.'), null, 400); + } catch (Exception $e) { + return apiResponse(0, lang('An error while decoding token.'), null, 500); + + + } + $inputs = json_decode($request->getContent(), true); + $inputs = is_array($inputs) ? $inputs : ["params" => []]; + $inputs['params']['property_id'] = $getTokenData['data']['property_id']; + $inputs['params']['property_web_id'] = $getTokenData['data']['id']; + $inputs['params']['domain'] = $getTokenData['data']['domain']; + $inputs['params']['default_language'] = $getTokenData['data']['default_language']; + $inputs['params']['template_id'] = $getTokenData['data']['template_id']; + $request->body = $inputs; + + return $next($request); + } +} diff --git a/app/Http/Middleware/PropertyMiddleware.php b/app/Http/Middleware/PropertyMiddleware.php new file mode 100644 index 0000000..0cd2cc1 --- /dev/null +++ b/app/Http/Middleware/PropertyMiddleware.php @@ -0,0 +1,93 @@ +userPropertyMappingRepository = $userPropertyMappingRepository; + $this->serviceLogService = $serviceLogService; + } + + + public function handle($request, Closure $next, $guard = null) + { + $userId = $request->credentials->user_id; + + $propertyId = $request->property_id ? $request->property_id : fillOnUndefined($request->params, 'property_id'); + if (!$propertyId) { + return apiResponse(0, 'Property_id required.', null, 401); + } + $checkPropertyUserRequest = [ + 'criteria' => [ + ['field' => 'user_id', 'condition' => '=', 'value' => $userId], + ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], + ['field' => 'status', 'condition' => '=', 'value' => 1], + ], + 'with' => ['property'], + 'firstRow' => 1 + ]; + + $checkPropertyUser = $this->userPropertyMappingRepository->findByCriteria($checkPropertyUserRequest); + if (!$checkPropertyUser) { + return apiResponse(0, 'User not matched this property.', null, 400); + } + + if (!$checkPropertyUser['property']['status']) { + return apiResponse(0, 'User not matched this property.', null, 400); + } + + /** ServiceLog **/ + $request->serviceLogId = null; + $request->serviceLogRequestTime = microtime(true); + $selectedRoute = [ + //'Property.Dashboard', + 'Property.RoomRateMapping.RoomRateAvailabilityUpdate', + 'Property.RoomRateMapping.BulkUpdate', + 'RoomRateChannelPromotion.Update', + 'Property.Promotion.Update', + 'PA.Property.Quick-Pricing.Sync' + ]; + $route = $request->route(); + $routeName = isset($route[1]['as']) ? $route[1]['as'] : null; + $inputs = json_decode($request->getContent(), true); + if (in_array($routeName, $selectedRoute)) { + $serviceLogParam = [ + 'property_id' => $propertyId, + 'user_id' => $userId, + 'service' => $routeName, + 'request' => json_encode($inputs), + 'ip_address' => $request->ip(), + 'status' => 2 + ]; + + $serviceLog = $this->serviceLogService->create($serviceLogParam); + + if($serviceLog['status'] == 'success' && !empty($serviceLog['data'])) { + $request->serviceLogId = $serviceLog['data']['id']; + } + + } + /** ServiceLog **/ + + return $next($request); + + } + + +} diff --git a/app/Http/Middleware/UserRoutePermissionAuthorize.php b/app/Http/Middleware/UserRoutePermissionAuthorize.php new file mode 100644 index 0000000..102a261 --- /dev/null +++ b/app/Http/Middleware/UserRoutePermissionAuthorize.php @@ -0,0 +1,45 @@ +routePermissionAuthorize =$routePermissionAuthorize; + } + + public function handle($request, Closure $next, $guard = null) + { + + $params = $request->params; + $requestParams = [ + 'property_id' => fillOnUndefined($params, 'property_id'), + 'user_id' => $request->credentials->user_id, + ]; + + $result = $this->routePermissionAuthorize->isUserAuthorizedForCurrentRoute($requestParams); + + if ( !$result) + { + return apiResponse(0, "Your permission not authorised" , null, 400); + } + return $next($request); + } +} diff --git a/app/Jobs/ExampleJob.php b/app/Jobs/ExampleJob.php new file mode 100644 index 0000000..1e58741 --- /dev/null +++ b/app/Jobs/ExampleJob.php @@ -0,0 +1,26 @@ +propertyId = $propertyId; + $this->language = $language; + $this->email = $email; + } + + /** + * Execute the job. + */ + public function handle() + { + chdir(base_path()); + + Artisan::call('cron:property-catalog-service', [ + 'property_id' => $this->propertyId, + 'language' => $this->language, + '--email' => $this->email, + ]); + } +} diff --git a/app/Jobs/PropertyReviewAnalyzeServiceJob.php b/app/Jobs/PropertyReviewAnalyzeServiceJob.php new file mode 100644 index 0000000..2e844b7 --- /dev/null +++ b/app/Jobs/PropertyReviewAnalyzeServiceJob.php @@ -0,0 +1,32 @@ +reviewId = $reviewId; + } + + /** + * Execute the job. + */ + public function handle() + { + Artisan::call('cron:property-review-analyze-service', [ + 'review_id' => $this->reviewId, + ]); + } +} diff --git a/app/Jobs/PropertyReviewServiceJob.php b/app/Jobs/PropertyReviewServiceJob.php new file mode 100644 index 0000000..33cd243 --- /dev/null +++ b/app/Jobs/PropertyReviewServiceJob.php @@ -0,0 +1,35 @@ +propertyId = $propertyId; + $this->channelId = $channelId; + } + + /** + * Execute the job. + */ + public function handle() + { + Artisan::call('cron:property-review-service', [ + 'property_id' => $this->propertyId, + 'channel_id' => $this->channelId + ]); + } +} diff --git a/app/Jobs/SlackLogJob.php b/app/Jobs/SlackLogJob.php new file mode 100644 index 0000000..a86ad1d --- /dev/null +++ b/app/Jobs/SlackLogJob.php @@ -0,0 +1,52 @@ +slack = $slack; + } + + public function handle() + { + try { + + $client = new Client(); + $client->request('POST', config('app.log_slack'), + [ + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'body' => json_encode($this->slack), + ]); + + } catch (Exception $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error('--SlackLogJob RequestException--'); + Log::error($message); + } catch (RequestException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error('--SlackLogJob RequestException--'); + Log::error($message); + } catch (ApiErrorException $e) { + $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); + Log::error('--SlackLogJob ApiErrorException--'); + Log::error($message); + } + } + +} diff --git a/app/Listeners/ExampleListener.php b/app/Listeners/ExampleListener.php new file mode 100644 index 0000000..eff6e58 --- /dev/null +++ b/app/Listeners/ExampleListener.php @@ -0,0 +1,31 @@ +hasOne('App\Models\User', 'id', 'user_id'); + } + + + public function getExpireTimeAttribute() + { + try { + return date('Y-m-d H:i:s', $this->expire_date); + } catch (Exception $e) { + return null; + } + } + + +} diff --git a/app/Models/AwardsCertificateCategory.php b/app/Models/AwardsCertificateCategory.php new file mode 100644 index 0000000..a4e9465 --- /dev/null +++ b/app/Models/AwardsCertificateCategory.php @@ -0,0 +1,39 @@ +hasMany('App\Models\PropertyAwardsCertificate','category_id', 'id') + ->select('id', 'property_id', 'category_id', 'name', 'date', 'url', 'file_path', 'file_type', 'status'); + } + + +} diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php new file mode 100644 index 0000000..5cd8ebf --- /dev/null +++ b/app/Models/BaseModel.php @@ -0,0 +1,113 @@ +appends; + if ( ! is_array ( $this->appends )) + { + return $origin; + } + + if(isset(self::$removeAppends[0]) && self::$removeAppends[0] == "*") + { + $this->appends = []; + return $this->appends; + } + + foreach ($this->appends as $key => $perAppend) + { + if (in_array ( $perAppend, self::$removeAppends )) + { + unset( $this->appends[ $key ] ); + } + } + return $this->appends; + } catch ( Exception $e ) + { + $message = $e->getFile()." ".$e->getLine()." ".$e->getMessage(); + Log::error($message); + } + + } + + private function _addAppends() + { + try + { + foreach (self::$addAppends as $perAppend) + { + $methodMakeUp = "get".strtolower($perAppend)."attribute"; + if(method_exists($this,$methodMakeUp)) + { + $this->append($perAppend); + } + } + } catch ( Exception $e ) + { + $message = $e->getFile()." ".$e->getLine()." ".$e->getMessage(); + Log::error($message); + } + } + + /*private function _addWith () + { + foreach (self::$addWith as &$perWith) + { + try { + $this->load($perWith); + } catch (Exception $e) { + continue; + } + + /* + if (method_exists($this,$perWith)) + { + $this->load($perWith); + } + } + }*/ + + + protected function getArrayableAppends() + { + /*$this->_addWith();*/ + $this->_removeAppends(); + $this->_addAppends(); + return $this->appends; + } + +} diff --git a/app/Models/Booking.php b/app/Models/Booking.php new file mode 100644 index 0000000..c7dbc00 --- /dev/null +++ b/app/Models/Booking.php @@ -0,0 +1,163 @@ +hasOne('App\Models\BookingContact', 'booking_id', 'id') + ->select(['id', 'booking_id', 'name', 'surname', 'phone_code', 'phone_number', 'email', 'note', 'extra_param', 'language_code', 'country_code', 'invoice_request', 'invoice']); + } + + public function bookingChannel() + { + return $this->hasOne('App\Models\PropertyChannel', 'id', 'channel_id') + ->select(['id', 'name', 'official_name', 'channel_category_id', 'description', 'country_code', 'default_currency', 'logo']); + } + + public function channelManager() + { + return $this->hasOne('App\Models\ChannelManager', 'id', 'channel_manager_id') + ->select(['id', 'name', 'status']); + } + + public function bookingPaymentType() + { + return $this->hasOne('App\Models\PropertyBookingPaymentType', 'code', 'payment_type_code') + ->select(['name', 'language_key', 'code']); + } + + public function bookingRoom() + { + return $this->hasMany('App\Models\BookingRoom', 'booking_id', 'id') + ->select(['id', 'booking_id', 'occupancy_code', 'checkin_date', 'checkout_date', 'availability_code', 'room_id', 'room_name', + 'room_rate_name', 'room_rate_mapping_id', 'cancellation_policy', 'rate_detail', 'extra_param', 'daily_amount', 'amount', 'discount_amount', + 'total', 'currency_code', 'property_room_bed_id', 'property_room_bed_group_id', 'smoking_fact_id', 'status']); + } + + public function bookingRoomSummary() + { + return $this->hasMany('App\Models\BookingRoom', 'booking_id', 'id') + ->select(['id', 'booking_id', 'occupancy_code', 'checkin_date', 'checkout_date', 'room_id', 'room_name', 'room_rate_name', 'total', 'currency_code', 'status']); + } + + public function bookingRoomPax() + { + return $this->hasMany('App\Models\BookingRoomPax', 'booking_id', 'id') + ->select(['id', 'booking_id', 'booking_room_id', 'type', 'name', 'surname', 'gender', 'citizen', 'birth_date', 'status']); + } + + public function bookingAddon() + { + return $this->hasMany('App\Models\BookingAddon', 'booking_id', 'id') + ->select(['id', 'booking_id', 'property_channel_addon_id', 'count', 'amount', 'total', 'currency_code', 'attribute']); + } + + public function bookingPayment() + { + return $this->hasOne('App\Models\BookingPayment', 'booking_id', 'id')->orderByDesc('id'); + } + + public function bookingPaymentData() + { + return $this->hasMany('App\Models\BookingPaymentData', 'booking_id', 'id')->orderByDesc('id') + ->select(['id', 'booking_id', 'type', 'data']); + } + + public function bookingPaymentDataCheck() + { + return $this->hasMany('App\Models\BookingPaymentDataCheck', 'booking_id', 'id')->orderBy('id'); + } + + public function bookingProperty() + { + return $this->hasOne('App\Models\Property', 'id', 'property_id') + ->select(['id', 'name', 'country', 'official_name', 'tax_office', 'tax_number', 'commission', 'commission_offline', 'commission_channel', 'commission_wholesaler']); + } + + public function propertyBookingEngine() + { + return $this->hasOne('App\Models\PropertyBookingEngine', 'property_id', 'property_id') + ->select(['id', 'property_id', 'channel_id', 'token']) + ->where('channel_id', '=', 1); + } + + public function propertyBookingChannel() + { + return $this->hasOne('App\Models\PropertyChannel', 'id', 'channel_id') + ->select(["id", "parent_id", "name", "official_name", "token", "description", "channel_category_id", "country_code", "currency_code", "default_currency", "logo", "address", "zip_code", "email", "phone", "phone2", "fax", "score"]); + } + + public function bookingPropertyWeb() + { + return $this->hasOne('App\Models\PropertyWeb', 'property_id', 'property_id'); + } + + public function bookingPaymentTransaction() + { + return $this->hasMany('App\Models\PaymentTransaction', 'order_id', 'booking_code') + ->orderByDesc('id'); + } + + public function bookingStatus() + { + return $this->hasOne('App\Models\BookingStatus', 'id', 'status') + ->select(['name', 'language_key', 'id'])->where('status', 1); + } + + public function property() + { + return $this->hasOne("App\Models\Property", 'id', 'property_id')->select(['id', 'name']); + } + + public function bookingActiveMessageCount() + { + return $this->hasMany('App\Models\BookingTicket', 'booking_id', 'id') + ->select(['id', 'booking_id', 'code', 'parent_id']) + ->where('status', 1)->where('user_id', null)->where('is_viewed', null) + ->where('is_log', null); + } + + public function propertyChannelMapping() + { + return $this->hasOne('App\Models\PropertyChannelMapping', 'property_id', 'property_id') + ->where('channel_id', '=', 1) + ->select("id", "property_id", "channel_id", "currency_code", "channel_tax_id", "status"); + } + + + public function propertyBookingEngineSearch() + { + return $this->hasOne('App\Models\PropertyBookingEngineSearch', 'search_key', 'search_key') + ->where('channel_id', '=', 1)->select(); + } + + public function channelManagerBooking() + { + return $this->hasMany('App\Models\ChannelManagerBooking', 'booking_id', 'id') + ->select(['id', 'property_id', 'booking_id','channel_manager_id', 'type', 'is_pushed', 'status']); + } +} diff --git a/app/Models/BookingAddon.php b/app/Models/BookingAddon.php new file mode 100644 index 0000000..051f72f --- /dev/null +++ b/app/Models/BookingAddon.php @@ -0,0 +1,38 @@ +hasOne('App\Models\PropertyChannelAddon','id', 'property_channel_addon_id') + ->select(['id','title', 'description','property_addon_id','min_stay','type','amount']); + } + + public function propertyAddon(){ + return $this->hasOne('App\Models\PropertyAddon','id', 'property_addon_id') + ->select(['id','fact_id', 'title', 'attribute']); + } + +} diff --git a/app/Models/BookingContact.php b/app/Models/BookingContact.php new file mode 100644 index 0000000..36300c0 --- /dev/null +++ b/app/Models/BookingContact.php @@ -0,0 +1,46 @@ +name . ' ' . $this->surname; + } + + public function getPhoneFormattedAttribute() + { + return $this->phone_code . '' . $this->phone_number; + } + + public function getInvoiceArrayAttribute() + { + if (!is_null($this->invoice)) { + return json_decode($this->invoice, 1); + } else { + return null; + } + } +} diff --git a/app/Models/BookingPayment.php b/app/Models/BookingPayment.php new file mode 100644 index 0000000..3dbdf55 --- /dev/null +++ b/app/Models/BookingPayment.php @@ -0,0 +1,34 @@ +hasOne('App\Models\PropertyBookingPaymentType', 'code', 'payment_type_code')->select(['code','name','language_key']); + } + + + +} diff --git a/app/Models/BookingPaymentData.php b/app/Models/BookingPaymentData.php new file mode 100644 index 0000000..2b34ec2 --- /dev/null +++ b/app/Models/BookingPaymentData.php @@ -0,0 +1,35 @@ +hasOne('App\Models\Booking', 'id', 'booking_id') + ->select(['id', 'channel_id', 'channel_manager_id', 'booking_code', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'created_at', 'updated_at', 'status']); + } + +} diff --git a/app/Models/BookingPaymentDataCheck.php b/app/Models/BookingPaymentDataCheck.php new file mode 100644 index 0000000..dc8e81e --- /dev/null +++ b/app/Models/BookingPaymentDataCheck.php @@ -0,0 +1,30 @@ +hasMany('App\Models\BookingRoomPax', 'booking_room_id', 'id'); + } + + public function roomRateMapping() + { + return $this->hasOne('App\Models\PropertyRoomRateMapping','id', 'room_rate_mapping_id')->select(); + } + + public function propertyRoomBed() + { + return $this->hasOne('App\Models\PropertyRoomBed', 'id', 'property_room_bed_id')->select(); + } + + public function smokingFact() + { + return $this->hasOne('App\Models\PropertyFact', 'id', 'smoking_fact_id')->select(); + } + + +} diff --git a/app/Models/BookingRoomPax.php b/app/Models/BookingRoomPax.php new file mode 100644 index 0000000..a1e921b --- /dev/null +++ b/app/Models/BookingRoomPax.php @@ -0,0 +1,34 @@ +hasOne('App\Models\Country', 'country_code', 'citizen') + ->select(['id', 'name', 'language_key', 'country_code', 'phone_code']) ; + } + +} diff --git a/app/Models/BookingStatus.php b/app/Models/BookingStatus.php new file mode 100644 index 0000000..19c7878 --- /dev/null +++ b/app/Models/BookingStatus.php @@ -0,0 +1,22 @@ +created_at)->toDateTimeString(); + } + + public function user() + { + return $this->hasOne('App\Models\User', 'id', 'user_id')->select(['id','name','surname']); + } + +} diff --git a/app/Models/ChannelManager.php b/app/Models/ChannelManager.php new file mode 100644 index 0000000..7fdc9bc --- /dev/null +++ b/app/Models/ChannelManager.php @@ -0,0 +1,20 @@ +hasOne('App\Models\Booking', 'id', 'booking_id') + ->select([ + 'id', 'channel_id', 'channel_manager_id', 'property_id', 'booking_code','channel_booking_code', 'checkin_date', 'checkout_date', 'rooms', + 'payment_type_code', 'room_amount', 'addon_amount', 'discount_amount', 'total', 'currency_code', 'extra_param', 'created_at', 'updated_at', 'status' + ]); + } + + public function channelManager() + { + return $this->hasOne('App\Models\ChannelManager', 'id', 'channel_manager_id')->select(['id', 'name', 'status']); + } +} diff --git a/app/Models/ChannelManagerLog.php b/app/Models/ChannelManagerLog.php new file mode 100644 index 0000000..783bd07 --- /dev/null +++ b/app/Models/ChannelManagerLog.php @@ -0,0 +1,20 @@ +hasOne("App\Models\PropertyChannel", 'id', 'property_channel_id')->select(['id', 'name']); + } + +} diff --git a/app/Models/ChannelManagerPropertyMapping.php b/app/Models/ChannelManagerPropertyMapping.php new file mode 100644 index 0000000..e955820 --- /dev/null +++ b/app/Models/ChannelManagerPropertyMapping.php @@ -0,0 +1,29 @@ +hasMany("App\Models\ChannelManagerPropertyRateMapping", 'channel_manager_property_mapping_id', 'id') + ->select(['id','channel_manager_property_mapping_id','property_room_rate_mapping_id','channel_manager_room_id', 'channel_manager_room_rate_id','included_occupancy']); + } + + public function property(){ + return $this->hasOne('App\Models\Property','id', 'property_id') + ->select(['id', 'name','country','destination_id','property_type_id','status']); + } +} diff --git a/app/Models/ChannelManagerPropertyRateMapping.php b/app/Models/ChannelManagerPropertyRateMapping.php new file mode 100644 index 0000000..8ee4085 --- /dev/null +++ b/app/Models/ChannelManagerPropertyRateMapping.php @@ -0,0 +1,24 @@ +hasOne("App\Models\PropertyRoomRateMapping", 'id', 'property_room_rate_mapping_id')->select(['id','room_id','room_rate_id','status']); + } + +} diff --git a/app/Models/Country.php b/app/Models/Country.php new file mode 100644 index 0000000..740dfc4 --- /dev/null +++ b/app/Models/Country.php @@ -0,0 +1,18 @@ +hasOne('App\Models\IpNationCountries','code', 'country'); + } + +} diff --git a/app/Models/IpNationCountries.php b/app/Models/IpNationCountries.php new file mode 100644 index 0000000..108acf6 --- /dev/null +++ b/app/Models/IpNationCountries.php @@ -0,0 +1,25 @@ +hasMany('App\Models\OfferAccommodationMapping', 'offer_id', 'id')->select("id", "offer_id", "property_accommodation_id"); + } + + public function offerCancellationPolicy() + { + return $this->hasMany('App\Models\OfferCancellationPolicy', 'offer_id', 'id')->select("id", "offer_id", "days_before", "type", "value"); + } + + public function offerContactMapping() + { + return $this->hasMany('App\Models\OfferContactMapping', 'offer_id', 'id') + ->select("id", "offer_id", "property_executive_id", "status") + ->where("status", "=", 1); + } + + public function offerFactMapping() + { + return $this->hasMany('App\Models\OfferFactMapping', 'offer_id', 'id')->select("id", + "offer_id", + "category_id", + "property_fact_parent_id", + "property_fact_id" + ); + } + + public function offerImportantNotes() + { + return $this->hasMany('App\Models\OfferImportantNotes', 'offer_id', 'id')->select("id", + "offer_id", + "note" + ); + } + + public function offerPhotoMapping() + { + return $this->hasMany('App\Models\OfferPhotoMapping', 'offer_id', 'id')->select("id", + "offer_id", + "property_photo_id", + "is_cover" + ); + } + + public function offerPrice() + { + return $this->hasMany('App\Models\OfferPrice', 'offer_id', 'id')->select("id", + "offer_id", + "date", + "property_room_id", + "property_accommodation_id", + "number_of_rooms", + "currency", + "amount", + "total_amount" + ); + } + + public function offerRoomMapping() + { + return $this->hasMany('App\Models\OfferRoomMapping', 'offer_id', 'id')->select("id", + "offer_id", + "property_room_id" + ); + } + + public function offerLanguage() + { + return $this->hasOne('App\Models\Language', 'code', 'language')->select( + "code", + "name", + "language_key" + ); + } + + public function offerAcceptStatus() + { + return $this->hasOne('App\Models\OfferAcceptStatus', 'id', 'accept_status')->select("id", "name", "language_key"); + } + + public function createUser() + { + return $this->hasOne('App\Models\User', 'id', 'created_by')->select("id", "name", "surname", "email"); + } + + public function property() + { + return $this->hasOne("App\Models\Property", 'id', 'property_id')->select(['id', 'name']); + } + + public function paymentTransaction() + { + return $this->hasMany('App\Models\PaymentTransaction', 'order_id', 'payment_transaction_order_id') + ->select("id", "code", "order_id", "base_amount", "base_currency", "status"); + } + + +} diff --git a/app/Models/OfferAcceptStatus.php b/app/Models/OfferAcceptStatus.php new file mode 100644 index 0000000..04f54df --- /dev/null +++ b/app/Models/OfferAcceptStatus.php @@ -0,0 +1,30 @@ +hasOne('App\Models\PropertyFact','id', 'property_accommodation_id') + ->select( + "id", + "name", + "parent_id", + "type", + "order_number", + "icon", + "status", + "language_key" + + ); + } + + +} diff --git a/app/Models/OfferCancellationPolicy.php b/app/Models/OfferCancellationPolicy.php new file mode 100644 index 0000000..28a7ac1 --- /dev/null +++ b/app/Models/OfferCancellationPolicy.php @@ -0,0 +1,33 @@ +hasOne('App\Models\PropertyExecutive','id', 'property_executive_id'); + } + +} diff --git a/app/Models/OfferFactMapping.php b/app/Models/OfferFactMapping.php new file mode 100644 index 0000000..50996e8 --- /dev/null +++ b/app/Models/OfferFactMapping.php @@ -0,0 +1,36 @@ +hasOne('App\Models\PropertyFact','id', 'property_fact_id'); + } +} diff --git a/app/Models/OfferImportantNotes.php b/app/Models/OfferImportantNotes.php new file mode 100644 index 0000000..e9b51ba --- /dev/null +++ b/app/Models/OfferImportantNotes.php @@ -0,0 +1,33 @@ +hasOne('App\Models\PropertyPhoto','id', 'property_photo_id'); + } + +} diff --git a/app/Models/OfferPrice.php b/app/Models/OfferPrice.php new file mode 100644 index 0000000..f987fde --- /dev/null +++ b/app/Models/OfferPrice.php @@ -0,0 +1,33 @@ +hasOne('App\Models\PropertyRoom','id', 'property_room_id') + ->select( + "id", + "property_id", + "name", + "room_type_id", + "max_occupancy", + "max_adult", + "max_child", + "exclude_occupancy", + "room_size", + 'room_size_type', + 'room_type_count', + 'room_count', + 'bathroom_count', + 'toilet_count', + 'lounge_count', + 'max_child_number' +); + } + + +} diff --git a/app/Models/PaymentBinNumber.php b/app/Models/PaymentBinNumber.php new file mode 100644 index 0000000..35a85f4 --- /dev/null +++ b/app/Models/PaymentBinNumber.php @@ -0,0 +1,29 @@ +params)) { + return json_decode($this->params, 1); + } else { + return null; + } + } + + public function getExtraParamsArrayAttribute() + { + if (!is_null($this->extra_params)) { + return json_decode($this->extra_params, 1); + } else { + return null; + } + } + + public function getResponseArrayAttribute() + { + if (!is_null($this->response)) { + return json_decode($this->response, 1); + } else { + return null; + } + } + + public function getManuelPaymentLinkAttribute() + { + if (!is_null($this->transaction_type == 'LNK')) { + return Config::get('app.paymentFormLink') . $this['order_id']; + } else { + return null; + } + } + + public function paymentTypeMapping() + { + return $this->hasOne('App\Models\PropertyPaymentMapping', 'id', 'payment_type_mapping_id'); + } + + public function bookingDetail() + { + return $this->hasMany('App\Models\Booking', 'booking_code', 'order_id') + ->select(['id', 'channel_id', 'booking_code', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'created_at', 'updated_at', 'status']); + } + + public function relatedTransactions() + { + return $this->hasMany('App\Models\PaymentTransaction', 'order_id', 'order_id')->where('code', '<>', null)->orderByDesc('id'); + } + + public function paymentTransactionStatus() + { + return $this->hasOne('App\Models\PaymentTransactionStatus', 'id', 'status'); + } + + public function parentTransaction() + { + return $this->hasOne('App\Models\PaymentTransaction', 'order_id', 'order_id')->where('code', '=', null); + } + + public function paymentUser() + { + return $this->hasOne('App\Models\User', 'id', 'created_by')->select(['id', 'name', 'surname', 'email', 'user_type', 'language', 'phone']); + } + + public function property() + { + return $this->belongsTo("App\Models\Property", "property_id"); + } +} diff --git a/app/Models/PaymentTransactionStatus.php b/app/Models/PaymentTransactionStatus.php new file mode 100644 index 0000000..cefd186 --- /dev/null +++ b/app/Models/PaymentTransactionStatus.php @@ -0,0 +1,30 @@ +params)) { + return json_decode($this->params,1); + } else { + return null; + } + } + + +} diff --git a/app/Models/Permission.php b/app/Models/Permission.php new file mode 100644 index 0000000..881b7c6 --- /dev/null +++ b/app/Models/Permission.php @@ -0,0 +1,36 @@ +hasOne("\App\Models\PermissionGroupMapping","permission_id"); + } + + + +} diff --git a/app/Models/PermissionGroup.php b/app/Models/PermissionGroup.php new file mode 100644 index 0000000..7d898ed --- /dev/null +++ b/app/Models/PermissionGroup.php @@ -0,0 +1,40 @@ +parameter_rules, true); + } + catch (Exception $e) + { + return null; + } + } + +} diff --git a/app/Models/PermissionGroupMapping.php b/app/Models/PermissionGroupMapping.php new file mode 100644 index 0000000..a4f3030 --- /dev/null +++ b/app/Models/PermissionGroupMapping.php @@ -0,0 +1,38 @@ +belongsTo("App\Models\PermissionGroup","permission_group_id"); + } + + public function permission () + { + return $this->belongsTo("App\Models\Permission","permission_id"); + } + +} diff --git a/app/Models/PermissionGroupUserMapping.php b/app/Models/PermissionGroupUserMapping.php new file mode 100644 index 0000000..525b4e2 --- /dev/null +++ b/app/Models/PermissionGroupUserMapping.php @@ -0,0 +1,88 @@ +belongsTo("App\Models\User","created_by"); + } + + public function property () + { + return $this->belongsTo("App\Models\Property","property_id"); + } + + public function user () + { + return $this->belongsTo("App\Models\User","user_id"); + } + + public function permissionGroup () + { + return $this->belongsTo("App\Models\PermissionGroup","permission_group_id"); + } + + public function permissionGroupMapping () + { + return $this->hasMany("\App\Models\PermissionGroupMapping","permission_group_id","permission_group_id"); + } + + public function getRelatedParametersArrayAttribute () + { + try + { + return json_decode ( $this->related_parameters,true ); + } catch ( Exception $e ) + { + return []; + } + } + + public function getRelatedParametersEmployeeDepartmentsAttribute () + { + try + { + $departments = []; + $relatedParameters = $this->getRelatedParametersArrayAttribute (); + + if ( !$relatedParameters) + { + return null; + } + + foreach ($relatedParameters["employee_departments"] as $perParameter) + { + $departments[] = $perParameter["department_id"]; + } + + return $departments; + } catch ( Exception $e ) + { + return null; + } + } + +} diff --git a/app/Models/PermissionMenuModel.php b/app/Models/PermissionMenuModel.php new file mode 100644 index 0000000..f2e12c8 --- /dev/null +++ b/app/Models/PermissionMenuModel.php @@ -0,0 +1,79 @@ +hasMany("\App\Models\PermissionMenuModel","parent_id","id") + ->with("children"); + } + + + protected function getHasLinkAttribute () + { + return $this->url?true:false; + } + + + + public function getMenuDetailArrayAttribute () + { + try + { + return json_decode ( $this->menu_detail, true ); + } catch ( Exception $e ) + { + return []; + } + } + + public function getUrlAttribute ( ) + { + try + { + return $this->menuDetailArray[ "url" ]; + } catch ( Exception $e ) + { + return null; + } + } + + public function getComponentAttribute ( ) + { + try + { + return $this->menuDetailArray[ "component" ]; + } catch ( Exception $e ) + { + return null; + } + } + +} diff --git a/app/Models/PhotoGoogleLabel.php b/app/Models/PhotoGoogleLabel.php new file mode 100644 index 0000000..3dc3274 --- /dev/null +++ b/app/Models/PhotoGoogleLabel.php @@ -0,0 +1,41 @@ +hasOne('App\Models\PropertyPhotoCategoryLabelMapping','photo_google_label_id', 'id'); + + } + +} diff --git a/app/Models/PlaceCategoryField.php b/app/Models/PlaceCategoryField.php new file mode 100644 index 0000000..ea21485 --- /dev/null +++ b/app/Models/PlaceCategoryField.php @@ -0,0 +1,42 @@ +hasMany('App\Models\PlaceCategoryFieldOptionFieldMapping','field_id', 'id') + ->select("id", "field_id", "field_option_id", "order_number", "status") + ->where('status','=',1) + ->orderBy('order_number'); + + } + +} diff --git a/app/Models/PlaceCategoryFieldMapping.php b/app/Models/PlaceCategoryFieldMapping.php new file mode 100644 index 0000000..b6e1db4 --- /dev/null +++ b/app/Models/PlaceCategoryFieldMapping.php @@ -0,0 +1,48 @@ +hasOne('App\Models\PropertyPlaceCategory','id', 'property_place_category_id') + ->select("id", "name", "language_key", "parent_id", "level","order_number","icon") ; + + } + + + public function placeCategoryField() + { + return $this->hasOne('App\Models\PlaceCategoryField','id', 'property_place_category_field_id') + ->select("id", "name", "language_key", "label", "element","type","field_attribute", "status") ; + + } + +} diff --git a/app/Models/PlaceCategoryFieldOption.php b/app/Models/PlaceCategoryFieldOption.php new file mode 100644 index 0000000..b6317e7 --- /dev/null +++ b/app/Models/PlaceCategoryFieldOption.php @@ -0,0 +1,33 @@ +hasOne('App\Models\PlaceCategoryFieldOption','id', 'field_option_id') + ->select("id", "name", "language_key", "status") + ->where('status','=',1) ; + + } + +} diff --git a/app/Models/Product.php b/app/Models/Product.php new file mode 100644 index 0000000..1efd361 --- /dev/null +++ b/app/Models/Product.php @@ -0,0 +1,30 @@ +hasOne(ProductParameter::class, 'id', 'product_parameter_id'); + } + +} diff --git a/app/Models/PromotionType.php b/app/Models/PromotionType.php new file mode 100644 index 0000000..5b95d37 --- /dev/null +++ b/app/Models/PromotionType.php @@ -0,0 +1,37 @@ +hasOne('App\Models\PromotionType', 'id', 'parent_id') + ->select(['id', 'name', 'language_key', 'parent_id', 'type_code', 'order_number', 'status']); + } + +} diff --git a/app/Models/Property.php b/app/Models/Property.php new file mode 100644 index 0000000..cf50272 --- /dev/null +++ b/app/Models/Property.php @@ -0,0 +1,202 @@ +hasMany('App\Models\PropertyPhoto', 'property_id', 'id') + ->where('status', '=', 1); + } + + public function propertyExecutive() + { + return $this->hasMany('App\Models\PropertyExecutive', 'property_id', 'id'); + } + + public function propertyUser() + { + return $this->hasMany('App\Models\UserPropertyMapping', 'property_id', 'id'); + } + + public function propertyContractUser() + { + return $this->hasOne('App\Models\User', 'id', 'contract_user_id'); + } + + public function propertyType() + { + return $this->belongsTo('App\Models\PropertyType', 'property_type_id', 'id') + ->select('id', 'name', 'language_key'); + } + + + public function defaultPropertyPhoto() + { + return $this->hasOne('App\Models\PropertyPhoto', 'property_id', 'id') + ->where('is_default', '=', 1); + } + + + public function propertyChain() + { + return $this->hasOne('App\Models\PropertyChain', 'id', 'chain_id') + ->select('id', 'name'); + } + + public function propertyDestination() + { + return $this->hasOne('App\Models\Destination', 'id', 'destination_id')->select('id', 'name'); + } + + public function propertyRooms() + { + return $this->hasMany('App\Models\PropertyRoom', 'property_id', 'id') + ->select( + 'id', 'property_id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'max_child_number', + 'exclude_occupancy', 'room_type_count', 'room_size', 'room_size_type', 'room_count', 'bathroom_count', 'lounge_count' + ) + ->where('status', '=', 1); + + } + + public function propertyBrand() + { + return $this->hasOne('App\Models\PropertyBrand', 'property_id', 'id')->select(['id', 'property_id', 'title', 'color_codes', 'logo_name', 'logo_file_ext']); + } + + + public function propertyContact() + { + return $this->hasOne('App\Models\PropertyContact', 'property_id', 'id') + ->select(['id', 'property_id', 'phone_code', 'phone', 'mobile_code', 'mobile', 'mobile2_code', 'mobile2', 'fax_code', 'fax', 'email', 'web', 'meta', 'social_media_addresses', 'zip_code', 'address', 'latitude', 'longitude']); + } + + public function propertyWeb() + { + return $this->hasOne('App\Models\PropertyWeb', 'property_id', 'id') + ->select(['id', 'property_id', 'domain', 'default_language', 'template_id', 'token', 'status', 'is_dns_checked', 'is_ssl_active', 'is_published']) + ->orderBy('id', 'DESC'); + } + + public function propertyWebRooms() + { + return $this->hasMany('App\Models\PropertyWebRoomMapping', 'property_id', 'id') + ->select('id', 'property_id', 'property_room_id') + ->where('status', '=', 1); + } + + public function propertyBookingEngineGroupMapping() + { + return $this->hasMany('App\Models\PropertyGroupMapping', 'property_id', 'id') + ->select(['id', 'property_id', 'property_group_id', 'order_number', 'status']) + ->where('status', '=', 1); + } + + public function propertyBookingEngineToken() + { + return $this->hasOne('App\Models\PropertyBookingEngine', 'property_id', 'id') + ->select(['id', 'property_id', 'channel_id', 'token', 'status']) + ->where('channel_id', '=', 1) + ->where('status', '=', 1); + } + + public function propertyBookingEngines() + { + return $this->hasMany('App\Models\PropertyBookingEngine', 'property_id', 'id') + ->select(['id', 'property_id', 'channel_id', 'token', 'status']) + ->where('status', '=', 1); + } + + public function propertyLanguageSpoken() + { + return $this->hasMany('App\Models\PropertyLanguageSpoken', 'property_id', 'id') + ->select('id', 'property_id', 'language_code', 'status') + ->where('status', '=', 1); + + } + + public function propertyAwardsCertificates() + { + return $this->hasMany('App\Models\PropertyAwardsCertificate', 'property_id', 'id') + ->select('id', 'property_id', 'category_id', 'name', 'date', 'url', 'file_path', 'file_type', 'language_code', 'status'); + + } + + public function propertyAdditionalInfos() + { + return $this->hasMany('App\Models\PropertyAdditionalInfo', 'property_id', 'id')->select('id', 'property_id', 'additional_info_key_id', 'value'); + + } + + public function propertyProductMapping() + { + return $this->hasMany('App\Models\PropertyProductMapping', 'property_id', 'id')->select('id', 'property_id', 'product_id', 'expiration_date', 'status'); + } + + public function userPropertyMapping() + { + return $this->hasMany('App\Models\UserPropertyMapping', 'property_id', 'id')->select('id', 'property_id', 'user_id', 'status')->where('status', '=', 1); + } + + public function propertyPlace() + { + return $this->hasMany('App\Models\PropertyPlace', 'property_id', 'id')->select('id', 'property_id', 'name', 'place_category_id', 'description', 'status')->where('status', '=', 1); + } + + public function country() + { + return $this->hasOne('App\Models\Country', 'country_code', 'country')->select('name', 'language_key', 'country_code'); + } + + public function propertyWebComponent() + { + return $this->hasMany('App\Models\PropertyWebComponentMapping', 'property_id', 'id')->select('id', 'property_id', 'channel_id', 'component_id', 'status')->where('status', '=', 1); + } + + public function propertyPromotionMapping() + { + return $this->hasMany('App\Models\PropertyPromotionMapping', 'property_id', 'id')->select('id', 'property_id', 'room_rate_channel_mapping_id', 'property_promotion_id', 'status')->where('status', '=', 1); + } + + public function propertyFactMapping() + { + return $this->hasMany('App\Models\PropertyFactMapping', 'property_id', 'id')->select('id', 'property_id', 'fact_id'); + } + + public function propertyPaymentMapping() + { + return $this->hasMany('App\Models\PropertyPaymentMapping', 'property_id', 'id')->select('id', 'property_id', 'payment_type_id', 'status'); + } + + public function propertyStatus() + { + return $this->hasOne('App\Models\PropertyStatus', 'id', 'status')->select('id', 'name', 'status'); + } + +} diff --git a/app/Models/PropertyAdditionalInfo.php b/app/Models/PropertyAdditionalInfo.php new file mode 100644 index 0000000..ed60b45 --- /dev/null +++ b/app/Models/PropertyAdditionalInfo.php @@ -0,0 +1,26 @@ +hasOne("App\Models\PropertyAdditionalInfoKey", 'id', 'additional_info_key_id')->select(['id' , 'additional_info_key', 'language_key' ]); + } + +} diff --git a/app/Models/PropertyAdditionalInfoKey.php b/app/Models/PropertyAdditionalInfoKey.php new file mode 100644 index 0000000..7eda919 --- /dev/null +++ b/app/Models/PropertyAdditionalInfoKey.php @@ -0,0 +1,24 @@ +hasMany('App\Models\PropertyAdditionalInfoKeyLocale','additional_info_key_id','id'); + } +} diff --git a/app/Models/PropertyAddon.php b/app/Models/PropertyAddon.php new file mode 100644 index 0000000..3516a72 --- /dev/null +++ b/app/Models/PropertyAddon.php @@ -0,0 +1,36 @@ +attribute)) { + $attribute = json_decode($this->attribute, 1); + } + return $attribute; + } + + public function fact() + { + return $this->hasOne("App\Models\PropertyFact", 'id', 'fact_id')->select('id', 'parent_id', 'name', 'icon', 'language_key'); + } + +} diff --git a/app/Models/PropertyAvailabilityType.php b/app/Models/PropertyAvailabilityType.php new file mode 100644 index 0000000..652af49 --- /dev/null +++ b/app/Models/PropertyAvailabilityType.php @@ -0,0 +1,30 @@ +hasOne('App\Models\AwardsCertificateCategory','id', 'category_id') + ->select("id", "name", "language_key", "country_code", "logo", "type", "status"); + } + + +} diff --git a/app/Models/PropertyBookingEngine.php b/app/Models/PropertyBookingEngine.php new file mode 100644 index 0000000..facf2e4 --- /dev/null +++ b/app/Models/PropertyBookingEngine.php @@ -0,0 +1,76 @@ +parameters)) { + $parametersArray = json_decode($this->parameters, 1); + if (is_array($parametersArray)) { + $parameters = $parametersArray; + } + } + + return $parameters; + } + + public function property() + { + return $this->hasOne("App\Models\Property", 'id', 'property_id') + ->select(['id', 'name', 'country', 'destination_id','property_type_id','mapping']); + } + + public function channel() + { + return $this->hasOne("App\Models\PropertyChannel", 'id', 'channel_id')->select(['id', 'name', 'token', 'channel_category_id', 'contact']); + } + + public function propertyWeb() + { + return $this->hasOne('App\Models\PropertyWeb', 'property_id', 'property_id') + ->where('status', '=', 1) + ->select("id", "property_id", "domain", "default_language", "template_id", "token"); + } + + public function propertyChannelMapping() + { + return $this->hasOne('App\Models\PropertyChannelMapping', 'property_id', 'property_id') + ->where('channel_id', '=', 1) + ->select("id", "property_id", "channel_id", "currency_code", "channel_tax_id", "status"); + } + + public function propertyWebComponent() + { + return $this->hasMany('App\Models\PropertyWebComponentMapping','property_id', 'property_id') + ->select('id','property_id','channel_id','component_id','parameter','language','status')->where('status',1); + } + +} diff --git a/app/Models/PropertyBookingEngineSearch.php b/app/Models/PropertyBookingEngineSearch.php new file mode 100644 index 0000000..79436d1 --- /dev/null +++ b/app/Models/PropertyBookingEngineSearch.php @@ -0,0 +1,30 @@ +logo_name)) { + $logoUrl = Config::get('app.imageUrl') . '/property-photos/' . $this->property_id . '/logo/' . $this->logo_name . '_250x250.' . $this->logo_file_ext; + } + + return $logoUrl; + } + +} diff --git a/app/Models/PropertyCancellationPolicy.php b/app/Models/PropertyCancellationPolicy.php new file mode 100644 index 0000000..05a7aff --- /dev/null +++ b/app/Models/PropertyCancellationPolicy.php @@ -0,0 +1,21 @@ +id . '.png'; + return Config::get('app.client_server') . '/assets/img/brands/' . $this->logo; + } + + public function getCountryCodeGroupArrayAttribute() + { + if (!is_null($this->country_code_group)) { + return json_decode($this->country_code_group, 1); + } else { + return null; + } + } + + + public function propertyChannelCategory() + { + return $this->hasOne('App\Models\PropertyChannelCategory', 'id', 'channel_category_id')->select(['id', 'name', 'language_key']); + } + + public function parentChannel() + { + return $this->hasOne('App\Models\PropertyChannel', 'id', 'parent_id')->select(['id', 'parent_id', 'name', 'official_name', 'description', 'channel_category_id', 'country_code', 'currency_code', 'default_currency', 'logo', 'address', 'zip_code', 'email', 'phone', 'phone2', 'fax', 'score', 'status']); + } +} diff --git a/app/Models/PropertyChannelAddon.php b/app/Models/PropertyChannelAddon.php new file mode 100644 index 0000000..9920b18 --- /dev/null +++ b/app/Models/PropertyChannelAddon.php @@ -0,0 +1,37 @@ +description)) { + $description = json_decode($this->description, 1); + } + return $description; + } + + public function propertyAddon() + { + return $this->hasOne("App\Models\PropertyAddon", 'id', 'property_addon_id')->select('id','fact_id','title','attribute'); + } + + +} diff --git a/app/Models/PropertyChannelBookingPaymentSetup.php b/app/Models/PropertyChannelBookingPaymentSetup.php new file mode 100644 index 0000000..438fa53 --- /dev/null +++ b/app/Models/PropertyChannelBookingPaymentSetup.php @@ -0,0 +1,45 @@ +cancellation_policy)) { + return json_decode($this->cancellation_policy, 1); + } else { + return null; + } + } + + public function paymentType() + { + return $this->hasOne("App\Models\PropertyBookingPaymentType", 'id', 'payment_type_id')->select(['id', 'name', 'language_key', 'code', 'status']); + } +} diff --git a/app/Models/PropertyChannelCategory.php b/app/Models/PropertyChannelCategory.php new file mode 100644 index 0000000..6ffc823 --- /dev/null +++ b/app/Models/PropertyChannelCategory.php @@ -0,0 +1,33 @@ +hasMany('App\Models\PropertyChannelGroupChannelMapping','channel_group_id','id')->where('status',1); + } +} diff --git a/app/Models/PropertyChannelGroupChannelMapping.php b/app/Models/PropertyChannelGroupChannelMapping.php new file mode 100644 index 0000000..eba3d7a --- /dev/null +++ b/app/Models/PropertyChannelGroupChannelMapping.php @@ -0,0 +1,32 @@ +hasOne("App\Models\PropertyChannel",'id', 'channel_id')->select(['id', 'name', 'token' ]); + } +} diff --git a/app/Models/PropertyChannelMapping.php b/app/Models/PropertyChannelMapping.php new file mode 100644 index 0000000..ddbc4a6 --- /dev/null +++ b/app/Models/PropertyChannelMapping.php @@ -0,0 +1,74 @@ +hasOne("App\Models\Property", 'id', 'property_id')->select(['id' , 'name']); + } + + public function channel() + { + return $this->hasOne("App\Models\PropertyChannel", 'id', 'channel_id')->select(['id', 'parent_id', 'token','name', 'official_name', 'description', 'channel_category_id', 'country_code', 'country_code_group', 'currency_code', 'default_currency', 'logo', 'address', 'zip_code', 'email', 'phone', 'phone2', 'fax', 'contact', 'status']); + } + + public function channelBookingType() + { + return $this->hasOne("App\Models\PropertyBookingType", 'id', 'property_booking_type_id')->select(['id', 'name', 'language_key', 'code', 'status']); + } + + public function channelAvailabilityType() + { + return $this->hasOne("App\Models\PropertyAvailabilityType", 'id', 'property_availability_type_id')->select(['id', 'name', 'language_key', 'code', 'status']); + } + + public function channelRoomPricingType() + { + return $this->hasOne("App\Models\PropertyRoomPricingType", 'id', 'property_room_pricing_type_id')->select(['id', 'name', 'language_key', 'code', 'status']); + } + + public function channelBookingPaymentType() + { + return $this->hasMany("App\Models\PropertyChannelBookingPaymentSetup", 'property_channel_mapping_id', 'id')->select(['id', 'property_channel_mapping_id', 'payment_type_id', 'cancellation_policy', 'is_affected_price', 'is_get_payment_data','action_type', 'value_type', 'value', 'is_selected', 'status']); + } + + public function channelContact() + { + return $this->hasMany("App\Models\PropertyChannelContact", 'property_channel_mapping_id', 'id')->select(['id', 'property_channel_mapping_id','name', 'email', 'status']); + } + + + public function channelTax() + { + return $this->hasOne("App\Models\PropertyChannelTax", 'id', 'channel_tax_id')->select(['id', 'name', 'language_key', 'code', 'status']); + } + + public function currency() + { + return $this->hasOne("App\Models\Currency", 'code', 'currency_code')->select(['id', 'code', 'symbol', 'name', 'language_key', 'status']); + } +} diff --git a/app/Models/PropertyChannelRoomRateCancellationPolicyMapping.php b/app/Models/PropertyChannelRoomRateCancellationPolicyMapping.php new file mode 100644 index 0000000..55dd7db --- /dev/null +++ b/app/Models/PropertyChannelRoomRateCancellationPolicyMapping.php @@ -0,0 +1,25 @@ +hasOne('App\Models\PropertyCancellationPolicy', 'id', 'cancellation_policy_id') + ->select(['id', 'property_id', 'name', 'before_arrival', 'is_nonrefundable', 'is_free_cancellation', 'type', 'value', 'is_affected_price', 'affect_price_action_type', 'affect_price_type', 'affect_price_value', 'is_date_range', 'start_date', 'finish_date', 'status']); + } + +} diff --git a/app/Models/PropertyChannelRoomRatePricingPolicyAdultMapping.php b/app/Models/PropertyChannelRoomRatePricingPolicyAdultMapping.php new file mode 100644 index 0000000..ed54395 --- /dev/null +++ b/app/Models/PropertyChannelRoomRatePricingPolicyAdultMapping.php @@ -0,0 +1,40 @@ +hasOne('App\Models\PropertyPricingPolicyAdult', 'id', 'pricing_policy_adult_id') + ->select("id", "property_id", "name", "adult_action_type", "adult", "action_type", "type", "value", "reservation_start_date", "reservation_end_date", "status"); + } + + +} diff --git a/app/Models/PropertyChannelRoomRatePricingPolicyChildMapping.php b/app/Models/PropertyChannelRoomRatePricingPolicyChildMapping.php new file mode 100644 index 0000000..a316faa --- /dev/null +++ b/app/Models/PropertyChannelRoomRatePricingPolicyChildMapping.php @@ -0,0 +1,41 @@ +hasOne('App\Models\PropertyPricingPolicyChild', 'id','pricing_policy_child_id') + ->select("id", "property_id", "name","adult", "child_order","child_age_start","child_age_end","type","value","reservation_start_date", "reservation_end_date","status"); + } + +} diff --git a/app/Models/PropertyChannelTax.php b/app/Models/PropertyChannelTax.php new file mode 100644 index 0000000..e677d78 --- /dev/null +++ b/app/Models/PropertyChannelTax.php @@ -0,0 +1,33 @@ +belongsTo('App\Models\Property', 'property_id', 'id'); + } +} diff --git a/app/Models/PropertyCompetitorGroupMapping.php b/app/Models/PropertyCompetitorGroupMapping.php new file mode 100644 index 0000000..87b36bc --- /dev/null +++ b/app/Models/PropertyCompetitorGroupMapping.php @@ -0,0 +1,22 @@ +phone,'444') !== false) { + $phoneNumber = $this->phone; + preg_match_all('/(444).*/m', $this->phone, $matchesPhone, PREG_SET_ORDER, 0); + $matchesPhone = reset($matchesPhone); + $matchesPhone = reset($matchesPhone); + $matchesPhone = str_replace(' ','', $matchesPhone); + + $phoneNumber = substr($matchesPhone,0,3).' '.substr($matchesPhone,3,1).' '.substr($matchesPhone,4,3); + + return $phoneNumber; + } + + if ($this->phone == null || $this->phone == "" || strlen($this->phone) < self::CHARACTER_LENGTH) { + return null; + } + + if ($this->phone_code) { + $response = strpos($this->phone_code, '+') === false ? '+' . $this->phone_code . $this->phone : $this->phone_code . $this->phone; + } else { + $response = strpos($this->phone, '+') === false ? '+' . $this->phone : $this->phone; + } + return $response; + } catch (Exception $e) { + return null; + } + } + + public function getViewFullMobileAttribute() + { + try { + $response = null; + + if ($this->mobile == null || $this->mobile == "" || strlen($this->mobile) < self::CHARACTER_LENGTH) { + return null; + } + if ($this->mobile_code) { + $response = strpos($this->mobile_code, '+') === false ? '+' . $this->mobile_code . $this->mobile : $this->mobile_code . $this->mobile; + } else { + $response = strpos($this->mobile, '+') === false ? '+' . $this->mobile : $this->mobile; + } + return $response; + } catch (Exception $e) { + return null; + } + } + + + public function getViewFullMobile2Attribute() + { + try { + $response = null; + if ($this->mobile2 == null || $this->mobile2 == "" || strlen($this->mobile2) < self::CHARACTER_LENGTH) { + return null; + } + + if ($this->mobile2_code) { + $response = strpos($this->mobile2_code, '+') === false ? '+' . $this->mobile2_code . $this->mobile2 : $this->mobile2_code . $this->mobile2; + } else { + $response = strpos($this->mobile2, '+') === false ? '+' . $this->mobile2 : $this->mobile2; + } + return $response; + + } catch (Exception $e) { + return null; + } + } + + + public function getViewFullFaxAttribute() + { + try { + $response = null; + if ($this->fax == null || $this->fax == "" || strlen($this->fax) < self::CHARACTER_LENGTH) { + return null; + } + if ($this->fax_code) { + $response = strpos($this->fax_code, '+') === false ? '+' . $this->fax_code . $this->fax : $this->fax_code . $this->fax; + } else { + $response = strpos($this->fax, '+') === false ? '+' . $this->fax : $this->fax; + } + return $response; + } catch (Exception $e) { + return null; + } + } + + public function getFormattedMobileNumberAttribute() + { + try { + $mobilePhoneNumber = null; + if ($this->mobile == null || $this->mobile == "" || strlen($this->mobile) < self::CHARACTER_LENGTH) { + return null; + } + if ($this->mobile) { + $this->mobile = preg_replace('/[^0-9,.]+/i', '', $this->mobile); + $mobilePhoneNumber = $this->mobile_code . $this->mobile; + } + return $mobilePhoneNumber; + } catch (Exception $e) { + return null; + } + } + + +} diff --git a/app/Models/PropertyContent.php b/app/Models/PropertyContent.php new file mode 100644 index 0000000..23f293e --- /dev/null +++ b/app/Models/PropertyContent.php @@ -0,0 +1,36 @@ +hasMany('App\Models\PropertyContentCategory','id'); + } + +} diff --git a/app/Models/PropertyContentCategory.php b/app/Models/PropertyContentCategory.php new file mode 100644 index 0000000..77505d0 --- /dev/null +++ b/app/Models/PropertyContentCategory.php @@ -0,0 +1,35 @@ +hasMany('App\Models\PropertyContentCategoryLocale','content_category_id', 'id'); + } +} diff --git a/app/Models/PropertyExecutive.php b/app/Models/PropertyExecutive.php new file mode 100644 index 0000000..17998ee --- /dev/null +++ b/app/Models/PropertyExecutive.php @@ -0,0 +1,98 @@ +phone == null || $this->phone == "" || strlen( $this->phone) < self::CHARACTER_LENGTH){ + return null ; + } + if($this->phone_code){ + $response = strpos($this->phone_code, '+') === false ? '+'.$this->phone_code.$this->phone : $this->phone_code.$this->phone; + }else{ + $response = strpos($this->phone, '+') === false ? '+'.$this->phone : $this->phone; + } + return $response; + } catch (Exception $e) { + return null; + } + } + + public function getViewFullMobileAttribute() + { + try { + $response = null ; + if($this->mobile == null || $this->mobile == "" || strlen( $this->mobile) < self::CHARACTER_LENGTH){ + return null ; + } + if($this->mobile_code){ + $response = strpos($this->mobile_code, '+') === false ? '+'.$this->mobile_code.$this->mobile : $this->mobile_code.$this->mobile; + }else{ + $response = strpos($this->mobile, '+') === false ? '+'.$this->mobile : $this->mobile; + } + return $response; + } catch (Exception $e) { + return null; + } + } + + + public function getViewFullFaxAttribute() + { + try { + $response = null ; + if($this->fax == null || $this->fax == "" || strlen( $this->fax) < self::CHARACTER_LENGTH){ + return null ; + } + if($this->fax_code){ + $response = strpos($this->fax_code, '+') === false ? '+'.$this->fax_code.$this->fax : $this->fax_code.$this->fax; + }else{ + $response = strpos($this->fax, '+') === false ? '+'.$this->fax : $this->fax; + } + return $response; + } catch (Exception $e) { + return null; + } + } + + public function executiveType() + { + return $this->hasOne('App\Models\PropertyExecutiveType','id', 'executive_type_id')->select('id', 'name', 'language_key', 'icon'); + } + + + /*public function PropertyContentCategories() + { + return $this->hasMany('App\Models\PropertyContentCategory','id'); + }*/ +} diff --git a/app/Models/PropertyExecutiveType.php b/app/Models/PropertyExecutiveType.php new file mode 100644 index 0000000..6e0704f --- /dev/null +++ b/app/Models/PropertyExecutiveType.php @@ -0,0 +1,33 @@ +hasOne('App\Models\PropertyFact','id', 'parent_id')->select('id', 'parent_id', 'name', 'language_key', 'title_language_key', 'order_number'); + } +} diff --git a/app/Models/PropertyFactAttribute.php b/app/Models/PropertyFactAttribute.php new file mode 100644 index 0000000..2b72402 --- /dev/null +++ b/app/Models/PropertyFactAttribute.php @@ -0,0 +1,33 @@ +description)) { + $description = json_decode($this->description, 1); + } + return $description; + } + + public function fact() + { + return $this->hasOne("App\Models\PropertyFact", 'id', 'fact_id')->select('id', 'parent_id', 'name', 'icon', 'language_key'); + } + +} diff --git a/app/Models/PropertyGroup.php b/app/Models/PropertyGroup.php new file mode 100644 index 0000000..0fbbdb1 --- /dev/null +++ b/app/Models/PropertyGroup.php @@ -0,0 +1,24 @@ +hasMany('App\Models\PropertyGroupMapping','property_group_id', 'id') + ->select([ "id","property_id","property_group_id", "order_number", "status"]) + ->where("status" , "=", 1); + } + +} diff --git a/app/Models/PropertyGroupMapping.php b/app/Models/PropertyGroupMapping.php new file mode 100644 index 0000000..8383034 --- /dev/null +++ b/app/Models/PropertyGroupMapping.php @@ -0,0 +1,29 @@ +hasOne('App\Models\PropertyGroup','id', 'property_group_id') + ->select(['id', 'name', 'type', 'status']); + } + + public function property(){ + return $this->hasOne('App\Models\Property','id', 'property_id') + ->select(['id', 'name', 'content_code']); + } + +} diff --git a/app/Models/PropertyInvoice.php b/app/Models/PropertyInvoice.php new file mode 100644 index 0000000..423cae1 --- /dev/null +++ b/app/Models/PropertyInvoice.php @@ -0,0 +1,18 @@ +hasOne('App\Models\Property','property_id', 'id'); + } + + + public function language(){ + return $this->hasOne('App\Models\Language','language_code', 'id'); + } + + public function languageCode(){ + return $this->hasOne('App\Models\Language','code', 'language_code'); + } + +} diff --git a/app/Models/PropertyMeta.php b/app/Models/PropertyMeta.php new file mode 100644 index 0000000..97a5202 --- /dev/null +++ b/app/Models/PropertyMeta.php @@ -0,0 +1,35 @@ +param)) { + return json_decode($this->param,1); + } else { + return null; + } + } + + public function property() + { + return $this->hasOne("App\Models\Property",'id','property_id')->select(['id', 'name', 'country']); + } + +} diff --git a/app/Models/PropertyMetaRoomRate.php b/app/Models/PropertyMetaRoomRate.php new file mode 100644 index 0000000..09b8d28 --- /dev/null +++ b/app/Models/PropertyMetaRoomRate.php @@ -0,0 +1,22 @@ +hasOne("App\Models\PropertyMetaRoomRate",'code','code')->select(); + } + + +} diff --git a/app/Models/PropertyMetaRoomRatePrice.php b/app/Models/PropertyMetaRoomRatePrice.php new file mode 100644 index 0000000..9261fe7 --- /dev/null +++ b/app/Models/PropertyMetaRoomRatePrice.php @@ -0,0 +1,22 @@ +hasOne('App\Models\PropertyModule','id', 'property_module_id'); + } +} diff --git a/app/Models/PropertyNonrefundable.php b/app/Models/PropertyNonrefundable.php new file mode 100644 index 0000000..d04b53a --- /dev/null +++ b/app/Models/PropertyNonrefundable.php @@ -0,0 +1,45 @@ +hasOne('App\Models\PropertyChannel','id', 'channel_id') + ->select(['id', 'parent_id', 'name', 'official_name', 'description', 'channel_category_id', 'country_code', 'currency_code', 'logo', 'address', 'zip_code', 'email', 'phone', 'phone2', 'fax', 'score', 'status']); + } + + public function propertyRoomRateMapping() + { + return $this->hasOne('App\Models\PropertyRoomRateMapping','id', 'room_rate_mapping_id') + ->select('id', 'room_id', 'room_rate_id', 'description', 'rack_rate', 'included_occupancy'); + } + +} diff --git a/app/Models/PropertyPaymentInstallment.php b/app/Models/PropertyPaymentInstallment.php new file mode 100644 index 0000000..13c0aae --- /dev/null +++ b/app/Models/PropertyPaymentInstallment.php @@ -0,0 +1,30 @@ +params)) { + return json_decode($this->params,1); + } else { + return null; + } + } + + public function paymentType() + { + return $this->hasOne('App\Models\PaymentType','id', 'payment_type_id'); + } + + public function propertyPaymentMappingInstallments() + { + return $this->hasMany('App\Models\PropertyPaymentInstallment','property_payment_mapping_id', 'id' ) + ->select('id', 'property_id', 'property_payment_mapping_id', 'installment', 'commission', 'status'); + } +} diff --git a/app/Models/PropertyPhoto.php b/app/Models/PropertyPhoto.php new file mode 100644 index 0000000..1135d23 --- /dev/null +++ b/app/Models/PropertyPhoto.php @@ -0,0 +1,69 @@ +property_id . '/' . $this->photo_name . '_1024x768.' . $this->file_ext; + $photoUrlThumbFilePath = Config::get('app.fileSystemDriver') . '/property-photos/' . $this->property_id . '/' . $this->photo_name . '_200x200.' . $this->file_ext; + + if (File::exists($photoUrlFilePath)) { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $this->property_id . '/' . $this->photo_name .'_1024x768.' . $this->file_ext; + }else { + $photoUrlFilePath = Config::get('app.imageUrl') . '/property-photos/' . $this->property_id . '/' . $this->photo_name .'_medium.' . $this->file_ext; + } + + if (File::exists($photoUrlThumbFilePath)) { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $this->property_id . '/' . $this->photo_name .'_200x200.'. $this->file_ext; + }else { + $photoUrlThumbFilePath = Config::get('app.imageUrl') . '/property-photos/' . $this->property_id . '/' . $this->photo_name .'_thumbnail.'. $this->file_ext; + } + $photoTypes = [ + 'default' => Config::get('app.imageUrl') . '/property-photos/' . $this->property_id . '/' . $this->photo_name . '.' . $this->file_ext, + 'thumb' => $photoUrlThumbFilePath, + 'fixed' => $photoUrlFilePath, + + ]; + + return $photoTypes; + } + + // Config::get('app.imageUrl') . '/property-photos/' . $params['property_id'] . '/' .$value['property_photo']['photo_name'] . '.' . $value['property_photo']['file_ext'] + + public function propertyRooms() + { + return $this->hasMany('App\Models\PropertyRoomPhotoMapping', 'photo_id', 'id'); + } + + public function propertyPlaces() + { + return $this->hasMany('App\Models\PropertyPlacePhotoMapping', 'photo_id', 'id'); + } +} diff --git a/app/Models/PropertyPhotoCategory.php b/app/Models/PropertyPhotoCategory.php new file mode 100644 index 0000000..5daea56 --- /dev/null +++ b/app/Models/PropertyPhotoCategory.php @@ -0,0 +1,37 @@ +hasMany('App\Models\PropertyPhoto','property_photo_category_id','id'); + } + +} diff --git a/app/Models/PropertyPhotoCategoryLabelMapping.php b/app/Models/PropertyPhotoCategoryLabelMapping.php new file mode 100644 index 0000000..647fb0a --- /dev/null +++ b/app/Models/PropertyPhotoCategoryLabelMapping.php @@ -0,0 +1,35 @@ +hasOne('App\Models\PropertyPhotoCategory','id', 'property_photo_category_id'); + + } + +} diff --git a/app/Models/PropertyPlace.php b/app/Models/PropertyPlace.php new file mode 100644 index 0000000..5748d9f --- /dev/null +++ b/app/Models/PropertyPlace.php @@ -0,0 +1,84 @@ +language_name)) { + $languageName = json_decode($this->language_name, 1); + } + return $languageName; + } + + function getSlugAttribute() + { + return '/place/' . Str::slug($this->name, $separator = '-', $language = 'en') . '-' . $this->id; + } + + //$propertyPlace['route'] = '/place/' . Str::slug($propertyPlace['name'], $separator = '-', $language = 'en') . '-' . $propertyPlace['id']; + + public function propertyPlaceCategory() + { + return $this->hasOne('App\Models\PropertyPlaceCategory','id', 'place_category_id') + ->where('status', '=', 1) + ->select("id", "name", "language_key", "parent_id", "level", "order_number","icon","status"); + } + + public function propertyPlaceWorkingHour() + { + return $this->hasOne('App\Models\PropertyPlaceWorkingHour','id', 'place_working_hour_id') + ->where('status', '=', 1) + ->select("id", "name", "language_key", "status"); + } + + public function propertyPlacePhotoMapping() + { + return $this->hasMany('App\Models\PropertyPlacePhotoMapping','place_id', 'id') ; + } + + public function propertyPlaceFactMapping() + { + return $this->hasMany('App\Models\PropertyPlaceFactMapping','property_place_id', 'id') ; + } + + public function propertyPlaceCategoryFieldValue() + { + return $this->hasMany('App\Models\PropertyPlaceCategoryFieldValue','place_id', 'id') ; + } + + public function propertyWebPlaceMapping() + { + return $this->hasMany('App\Models\PropertyWebPlaceMapping', 'property_place_id', 'id')->select(['id', 'property_id', 'property_web_id', 'property_place_id']); + } + +} diff --git a/app/Models/PropertyPlaceCategory.php b/app/Models/PropertyPlaceCategory.php new file mode 100644 index 0000000..79bd089 --- /dev/null +++ b/app/Models/PropertyPlaceCategory.php @@ -0,0 +1,34 @@ +hasOne('App\Models\PlaceCategoryField','id', 'place_category_field_id') ; + } + + public function unitValue() + { + return $this->hasOne('App\Models\PropertyUnit','id', 'unit_value') ; + } + + + public function optionValue() + { + return $this->hasOne('App\Models\PlaceCategoryFieldOption','id', 'option_value') ; + } + + +} diff --git a/app/Models/PropertyPlaceFact.php b/app/Models/PropertyPlaceFact.php new file mode 100644 index 0000000..ee2c24e --- /dev/null +++ b/app/Models/PropertyPlaceFact.php @@ -0,0 +1,33 @@ +hasOne('App\Models\propertyPlaceFactTitleFactMapping', 'id', 'place_fact_title_fact_mapping_id'); + } + + +} diff --git a/app/Models/PropertyPlaceFactTitle.php b/app/Models/PropertyPlaceFactTitle.php new file mode 100644 index 0000000..6f4335e --- /dev/null +++ b/app/Models/PropertyPlaceFactTitle.php @@ -0,0 +1,33 @@ +hasOne('App\Models\PropertyPlaceCategory','id', 'place_category_id') + ->select("id", "name", "language_key", "parent_id", "level", "order_number", "icon", "status"); + } + + public function placeFactTitle() + { + return $this->hasOne('App\Models\PropertyPlaceFactTitle','id', 'place_fact_title_id') + ->select("id", "name", "language_key", "icon", "status"); + } + + public function placeFact() + { + return $this->hasOne('App\Models\PropertyPlaceFact','id', 'place_fact_id') + ->select("id", "name", "language_key", "icon", "status"); + } + + +} diff --git a/app/Models/PropertyPlacePhotoMapping.php b/app/Models/PropertyPlacePhotoMapping.php new file mode 100644 index 0000000..9cee6f4 --- /dev/null +++ b/app/Models/PropertyPlacePhotoMapping.php @@ -0,0 +1,44 @@ +hasOne('App\Models\PropertyPhoto','id', 'photo_id'); + } + + + public function propertyPlace() + { + return $this->hasOne('App\Models\PropertyPlace','id', 'place_id'); + } +} diff --git a/app/Models/PropertyPlaceWorkingHour.php b/app/Models/PropertyPlaceWorkingHour.php new file mode 100644 index 0000000..9540ab5 --- /dev/null +++ b/app/Models/PropertyPlaceWorkingHour.php @@ -0,0 +1,34 @@ +data)) { + return json_decode($this->data, 1); + } else { + return null; + } + } + + public function propertyDetail() + { + return $this->hasOne('App\Models\Property', 'id', 'property_id') + ->select(['id', 'name', 'country', 'official_name', 'tax_office', 'tax_number', 'commission', 'commission_offline', 'commission_channel', 'commission_wholesaler','contract_user_id']); + } +} diff --git a/app/Models/PropertyPricingPolicyAdult.php b/app/Models/PropertyPricingPolicyAdult.php new file mode 100644 index 0000000..bf9af73 --- /dev/null +++ b/app/Models/PropertyPricingPolicyAdult.php @@ -0,0 +1,40 @@ +hasMany('App\Models\PropertyChannelRoomRatePricingPolicyAdultMapping','pricing_policy_adult_id', 'id')->select("id", "property_id", "room_rate_channel_mapping_id", "pricing_policy_adult_id", "status"); + } + + + +} diff --git a/app/Models/PropertyPricingPolicyChild.php b/app/Models/PropertyPricingPolicyChild.php new file mode 100644 index 0000000..aff980a --- /dev/null +++ b/app/Models/PropertyPricingPolicyChild.php @@ -0,0 +1,40 @@ +hasMany('App\Models\PropertyChannelRoomRatePricingPolicyChildMapping','pricing_policy_child_id', 'id')->select("id", "property_id", "room_rate_channel_mapping_id", "pricing_policy_child_id", "status"); + } + + + +} diff --git a/app/Models/PropertyProductMapping.php b/app/Models/PropertyProductMapping.php new file mode 100644 index 0000000..60f3b1d --- /dev/null +++ b/app/Models/PropertyProductMapping.php @@ -0,0 +1,38 @@ +parameter)) { + $attribute = json_decode($this->parameter, 1); + } + return $attribute; + } + + +} diff --git a/app/Models/PropertyProductOffer.php b/app/Models/PropertyProductOffer.php new file mode 100644 index 0000000..93b31ee --- /dev/null +++ b/app/Models/PropertyProductOffer.php @@ -0,0 +1,50 @@ +detail)) { + $parametersArray = json_decode($this->detail, 1); + if (is_array($parametersArray)) { + $parameters = $parametersArray; + } + } + + return $parameters; + } + + + public function getOfferTimeFormattedAttribute() + { + try { + return Carbon::parse($this->offer_date)->format('d.m.Y H:i:s'); + } catch (Exception $e) { + return null; + } + } + + public function getOfferExpireTimeFormattedAttribute() + { + try { + return Carbon::parse($this->offer_expire_date)->format('d.m.Y H:i:s'); + } catch (Exception $e) { + return null; + } + } +} diff --git a/app/Models/PropertyPromotion.php b/app/Models/PropertyPromotion.php new file mode 100644 index 0000000..c5c53da --- /dev/null +++ b/app/Models/PropertyPromotion.php @@ -0,0 +1,55 @@ +hasOne('App\Models\PromotionType', 'id', 'promotion_type_id') + ->select(['id', 'name', 'language_key', 'parent_id', 'type_code', 'order_number', 'status']); + } + + public function getDaysArrayAttribute() + { + if (!is_null($this->days)) { + return json_decode($this->days, 1); + } else { + return null; + } + } + + public function getExcludeDatesArrayAttribute() + { + if (!is_null($this->exclude_dates)) { + return json_decode($this->exclude_dates, 1); + } else { + return null; + } + } +} diff --git a/app/Models/PropertyPromotionMapping.php b/app/Models/PropertyPromotionMapping.php new file mode 100644 index 0000000..34d102e --- /dev/null +++ b/app/Models/PropertyPromotionMapping.php @@ -0,0 +1,43 @@ +hasOne('App\Models\PropertyPromotion', 'id', 'property_promotion_id') + ->select(['id', 'property_id', 'promotion_type_id', 'start_date', 'end_date', 'reservation_start_date', 'reservation_end_date', 'exclude_dates', 'is_time', 'start_time', 'end_time', 'day_before', 'amount', 'min_stay', 'is_mobile', 'days', 'params', 'detail', 'status']); + } + + public function propertyRoomRateChannelMapping() + { + return $this->hasOne('App\Models\PropertyRoomRateChannelMapping', 'id', 'room_rate_channel_mapping_id') + ->select(['id', 'property_id', 'room_rate_mapping_id', 'channel_id', 'status']); + } +} diff --git a/app/Models/PropertyQuickPricingMapping.php b/app/Models/PropertyQuickPricingMapping.php new file mode 100644 index 0000000..d65873e --- /dev/null +++ b/app/Models/PropertyQuickPricingMapping.php @@ -0,0 +1,33 @@ +hasOne('App\Models\PropertyRoomRateChannelMapping', 'id', 'room_rate_channel_mapping_id') + ->select(['id', 'property_id', 'room_rate_mapping_id', 'channel_id', 'status']); + } +} diff --git a/app/Models/PropertyReview.php b/app/Models/PropertyReview.php new file mode 100644 index 0000000..2075ce4 --- /dev/null +++ b/app/Models/PropertyReview.php @@ -0,0 +1,51 @@ +sentiment == 1) { + $sentimentText = 'Positive'; + } elseif ($this->sentiment == 0 && !is_null($this->sentiment)) { + $sentimentText = 'Negative'; + } + + return $sentimentText; + + } + + public function channel() + { + return $this->hasOne('App\Models\PropertyReviewChannel', 'id', 'channel_id')->select(['id', 'name', 'logo']); + } + + public function property() + { + return $this->hasOne('App\Models\Property', 'id', 'property_id')->select(['id', 'name', 'status']); + } + + public function categoryMapping() + { + return $this->hasMany('App\Models\PropertyReviewCategoryMapping', 'review_id', 'id')->select(['id', 'review_id', 'category_id', 'sentiment']); + } + + public function keywordMapping() + { + return $this->hasMany('App\Models\PropertyReviewKeywordMapping', 'review_id', 'id')->select(['id', 'review_id', 'channel_id', 'keyword', 'sentiment']); + } +} diff --git a/app/Models/PropertyReviewCategory.php b/app/Models/PropertyReviewCategory.php new file mode 100644 index 0000000..d561bd0 --- /dev/null +++ b/app/Models/PropertyReviewCategory.php @@ -0,0 +1,19 @@ +sentiment == 1) { + $sentimentText = 'Positive'; + } elseif ($this->sentiment == 0 && !is_null($this->sentiment)) { + $sentimentText = 'Negative'; + } + + return $sentimentText; + + } + public function category() + { + return $this->hasOne('App\Models\PropertyReviewCategory', 'id', 'category_id')->select(['id', 'name', 'language_key']); + } +} diff --git a/app/Models/PropertyReviewChannel.php b/app/Models/PropertyReviewChannel.php new file mode 100644 index 0000000..d271757 --- /dev/null +++ b/app/Models/PropertyReviewChannel.php @@ -0,0 +1,35 @@ +parameter)) { + $attribute = json_decode($this->parameter, 1); + } + return $attribute; + } + + public function getLogoUrlAttribute() + { + return config('app.url') . '/img/channel-icons/' . $this->logo; + + } + + +} diff --git a/app/Models/PropertyReviewChannelMapping.php b/app/Models/PropertyReviewChannelMapping.php new file mode 100644 index 0000000..53362dd --- /dev/null +++ b/app/Models/PropertyReviewChannelMapping.php @@ -0,0 +1,42 @@ +parameter)) { + return json_decode($this->parameter, 1); + } else { + return null; + } + } + + public function channel() + { + return $this->hasOne('App\Models\PropertyReviewChannel', 'id', 'channel_id')->select(['id', 'name', 'parameter', 'status']); + } + + public function property() + { + return $this->hasOne('App\Models\Property', 'id', 'property_id')->select(['id', 'name', 'status']); + } + + public function fetchStatus() + { + return $this->hasOne('App\Models\PropertyReviewFetchStatus', 'id', 'fetch_status')->select(['id', 'name', 'language_key', 'status']); + } +} diff --git a/app/Models/PropertyReviewFetchStatus.php b/app/Models/PropertyReviewFetchStatus.php new file mode 100644 index 0000000..bd8ce6d --- /dev/null +++ b/app/Models/PropertyReviewFetchStatus.php @@ -0,0 +1,20 @@ + 'm²', + 'ft' => 'ft² sq' + ]; + + $roomSizeType = isset($roomSizeTypes[$this->room_size_type]) && !empty($this->room_size_type) ? $roomSizeTypes[$this->room_size_type] : $this->room_size_type; + + return $roomSizeType; + } + + + public function propertyRoomType() + { + return $this->hasOne('App\Models\PropertyRoomType', 'id', 'room_type_id')->select(['id', 'name', 'status', 'language_key']); + } + + public function propertyRoomRateMapping() + { + return $this->hasMany('App\Models\PropertyRoomRateMapping', 'room_id', 'id')->select(['id', 'room_id', 'room_rate_id', 'description', 'rack_rate', 'included_occupancy','connected_rate_id','is_affected_price','affect_price_action_type','affect_price_type','affect_price_value']); + } + + public function propertyRoomBedGroup() + { + return $this->hasMany('App\Models\PropertyRoomBed', 'room_id', 'id')->select(['id', 'room_id', 'bed_group', 'count', 'bed_type_id', 'status']); + } + + public function propertyRoomFactMapping() + { + return $this->hasMany('App\Models\PropertyRoomFactMapping', 'room_id', 'id')->select(['id', 'room_id', 'fact_id', 'property_id', 'is_feature']); + } + + public function propertyRoomPhotoMapping() + { + return $this->hasMany('App\Models\PropertyRoomPhotoMapping', 'room_id', 'id')->select(['id', 'room_id', 'photo_id', 'property_id']); + } + + public function propertyRoomDefaultPhoto() + { + return $this->hasOne('App\Models\PropertyRoomPhotoMapping', 'room_id', 'id')->select(['id', 'room_id', 'photo_id', 'property_id']); + } + + public function propertyRoomViewMapping() + { + return $this->hasMany('App\Models\PropertyRoomViewMapping', 'room_id', 'id')->select(['id', 'room_id', 'room_view_type_id']); + } + + public function propertyRoomConnected() + { + return $this->hasMany('App\Models\PropertyRoomConnected', 'room_id', 'id')->select(['id', 'room_id', 'connected_room_id']); + } + + public function propertyWebRoomMapping() + { + return $this->hasOne('App\Models\PropertyWebRoomMapping', 'property_room_id', 'id')->select(['id', 'property_id', 'property_web_id', 'property_room_id']); + } + + public function smokingPreference() + { + return $this->hasMany('App\Models\PropertyRoomFactMapping', 'room_id', 'id') + ->whereIn('fact_id', [684,685])->select(); + } +} diff --git a/app/Models/PropertyRoomAvailability.php b/app/Models/PropertyRoomAvailability.php new file mode 100644 index 0000000..2a325b2 --- /dev/null +++ b/app/Models/PropertyRoomAvailability.php @@ -0,0 +1,37 @@ +hasOne('App\Models\PropertyRoom','id', 'property_room_id')->select(); + } + +} diff --git a/app/Models/PropertyRoomAvailabilityQueue.php b/app/Models/PropertyRoomAvailabilityQueue.php new file mode 100644 index 0000000..9350499 --- /dev/null +++ b/app/Models/PropertyRoomAvailabilityQueue.php @@ -0,0 +1,42 @@ +hasOne("App\Models\Property", 'id', 'property_id')->select(['id', 'name', 'country']); + } + + public function propertyChannelManager() + { + return $this->hasOne("App\Models\ChannelManagerPropertyMapping", 'property_id', 'property_id')->select(['id', 'property_id', 'channel_manager_id', 'channel_manager_property_id']); + } + +} diff --git a/app/Models/PropertyRoomBed.php b/app/Models/PropertyRoomBed.php new file mode 100644 index 0000000..d39f370 --- /dev/null +++ b/app/Models/PropertyRoomBed.php @@ -0,0 +1,41 @@ +hasMany('App\Models\PropertyRoom','id', 'room_id')->select(['id', 'name', 'status']); + } + + public function propertyRoomBedType() + { + return $this->hasOne('App\Models\PropertyRoomBedType','id', 'bed_type_id')->select(['id', 'name', 'status','icon', 'language_key']); + } +} diff --git a/app/Models/PropertyRoomBedType.php b/app/Models/PropertyRoomBedType.php new file mode 100644 index 0000000..9eff021 --- /dev/null +++ b/app/Models/PropertyRoomBedType.php @@ -0,0 +1,39 @@ +icon; + } + +} diff --git a/app/Models/PropertyRoomConnected.php b/app/Models/PropertyRoomConnected.php new file mode 100644 index 0000000..3936b83 --- /dev/null +++ b/app/Models/PropertyRoomConnected.php @@ -0,0 +1,33 @@ +hasOne('App\Models\PropertyRoom','id', 'room_id')->select(); + } + +} diff --git a/app/Models/PropertyRoomFactMapping.php b/app/Models/PropertyRoomFactMapping.php new file mode 100644 index 0000000..cd8e268 --- /dev/null +++ b/app/Models/PropertyRoomFactMapping.php @@ -0,0 +1,47 @@ +hasOne('App\Models\PropertyFact','id', 'fact_id') + ->select("id", + "name", + "language_key", + "parent_id", + "type", + "order_number", + "icon", + "status" +); + } + + +} diff --git a/app/Models/PropertyRoomPhotoMapping.php b/app/Models/PropertyRoomPhotoMapping.php new file mode 100644 index 0000000..53d07f0 --- /dev/null +++ b/app/Models/PropertyRoomPhotoMapping.php @@ -0,0 +1,43 @@ +hasOne('App\Models\PropertyPhoto','id', 'photo_id'); + } + + + public function propertyRoom() + { + return $this->hasOne('App\Models\PropertyRoom','id', 'room_id'); + } +} diff --git a/app/Models/PropertyRoomPricingType.php b/app/Models/PropertyRoomPricingType.php new file mode 100644 index 0000000..83e1550 --- /dev/null +++ b/app/Models/PropertyRoomPricingType.php @@ -0,0 +1,20 @@ +description)) { + $description = json_decode($this->description, 1); + } + return $description; + } + + public function propertyRoomRateInclusionMapping() + { + return $this->hasMany('App\Models\PropertyRoomRateInclusionMapping','room_rate_id', 'id')->select('id', 'room_rate_id', 'fact_id'); + } + + public function propertyRoomRateAccommodation() + { + return $this->hasOne('App\Models\PropertyFact', 'id', 'accommodation_type')->select('id', 'name', 'language_key'); + } +} diff --git a/app/Models/PropertyRoomRateChannelMapping.php b/app/Models/PropertyRoomRateChannelMapping.php new file mode 100644 index 0000000..4eb8124 --- /dev/null +++ b/app/Models/PropertyRoomRateChannelMapping.php @@ -0,0 +1,68 @@ +hasOne('App\Models\PropertyChannel','id', 'channel_id')->select(['id', 'parent_id', 'name', 'official_name', 'description', 'channel_category_id', 'country_code', 'currency_code', 'logo', 'address', 'zip_code', 'email', 'phone', 'phone2', 'fax', 'score', 'status']); + } + + public function propertyRoomRateMapping() + { + return $this->hasOne('App\Models\PropertyRoomRateMapping','id', 'room_rate_mapping_id')->select(['id', 'room_id', 'room_rate_id', 'rack_rate', 'included_occupancy','status']); + } + + public function propertyRoomRateChannelCancellationPolicy() + { + return $this->hasMany('App\Models\PropertyChannelRoomRateCancellationPolicyMapping','room_rate_channel_mapping_id', 'id')->select(['id', 'property_id', 'room_rate_channel_mapping_id', 'cancellation_policy_id','status']); + } + + public function propertyRoomRateChannelPricingAdultPolicy() + { + return $this->hasMany('App\Models\PropertyChannelRoomRatePricingPolicyAdultMapping','room_rate_channel_mapping_id', 'id')->select(['id', 'property_id', 'room_rate_channel_mapping_id', 'pricing_policy_adult_id']); + } + + public function propertyRoomRateChannelPricingChildPolicy() + { + return $this->hasMany('App\Models\PropertyChannelRoomRatePricingPolicyChildMapping','room_rate_channel_mapping_id', 'id')->select(['id', 'property_id', 'room_rate_channel_mapping_id', 'pricing_policy_child_id']); + } + + public function propertyRoomRateChannelPromotion() + { + return $this->hasMany('App\Models\PropertyPromotionMapping','room_rate_channel_mapping_id', 'id')->select(['id', 'property_id', 'room_rate_channel_mapping_id', 'property_promotion_id', 'status']); + } + + public function propertyRoomRateQuickPricingMapping() + { + return $this->hasOne('App\Models\PropertyQuickPricingMapping','room_rate_channel_mapping_id','id'); + } + +} diff --git a/app/Models/PropertyRoomRateInclusionMapping.php b/app/Models/PropertyRoomRateInclusionMapping.php new file mode 100644 index 0000000..89735e0 --- /dev/null +++ b/app/Models/PropertyRoomRateInclusionMapping.php @@ -0,0 +1,38 @@ +hasOne('App\Models\PropertyFact','id', 'fact_id')->select(['id', 'name', 'parent_id', 'language_key', 'type', 'order_number', 'icon']); + } + +} diff --git a/app/Models/PropertyRoomRateMapping.php b/app/Models/PropertyRoomRateMapping.php new file mode 100644 index 0000000..fe320e7 --- /dev/null +++ b/app/Models/PropertyRoomRateMapping.php @@ -0,0 +1,55 @@ +hasOne('App\Models\PropertyRoomRate','id', 'room_rate_id')->select(['id', 'name', 'description', 'min_stay', 'max_stay','accommodation_type']); + } + + public function propertyRoom() + { + return $this->hasOne('App\Models\PropertyRoom','id', 'room_id')->select(['id', 'name', 'room_type_id', 'max_occupancy', 'max_adult', 'max_child', 'max_child_number', 'exclude_occupancy','occupancy_lock', 'room_type_count', 'room_size', 'room_size_type','status']); + } + + + public function propertyRoomRateChannel() + { + return $this->hasMany('App\Models\PropertyRoomRateChannelMapping','room_rate_mapping_id', 'id')->select(['id', 'channel_id', 'room_rate_mapping_id', 'has_date', 'start_date', 'end_date', 'status']); + } + + public function connectedRate() + { + return $this->hasOne('App\Models\PropertyRoomRate','id', 'connected_rate_id')->select(['id', 'name', 'description']); + } + + +} diff --git a/app/Models/PropertyRoomRateMappingSetup.php b/app/Models/PropertyRoomRateMappingSetup.php new file mode 100644 index 0000000..e88493e --- /dev/null +++ b/app/Models/PropertyRoomRateMappingSetup.php @@ -0,0 +1,34 @@ +hasOne('App\Models\PropertyRoomRateMapping','id', 'room_rate_mapping_id')->select(); + } + +} diff --git a/app/Models/PropertyRoomRatePriceQueue.php b/app/Models/PropertyRoomRatePriceQueue.php new file mode 100644 index 0000000..cfcc330 --- /dev/null +++ b/app/Models/PropertyRoomRatePriceQueue.php @@ -0,0 +1,42 @@ +hasOne("App\Models\Property", 'id', 'property_id')->select(['id', 'name', 'country']); + } + + public function propertyChannelManager() + { + return $this->hasOne("App\Models\ChannelManagerPropertyMapping", 'property_id', 'property_id')->select(['id', 'property_id', 'channel_manager_id', 'channel_manager_property_id']); + } + +} diff --git a/app/Models/PropertyRoomSizeType.php b/app/Models/PropertyRoomSizeType.php new file mode 100644 index 0000000..b0d5935 --- /dev/null +++ b/app/Models/PropertyRoomSizeType.php @@ -0,0 +1,18 @@ +hasOne('App\Models\PropertyRoomViewType','id', 'room_view_type_id')->select(['id', 'name', 'language_key']); + } +} diff --git a/app/Models/PropertyRoomViewType.php b/app/Models/PropertyRoomViewType.php new file mode 100644 index 0000000..c7c7c41 --- /dev/null +++ b/app/Models/PropertyRoomViewType.php @@ -0,0 +1,32 @@ +is_ssl_active) { + $webProtocol = 'https://'; + } + return $webProtocol . $this->domain; + } + + public function propertyWebTemplate() + { + return $this->hasOne('App\Models\PropertyWebTemplate', 'id', 'template_id')->select("id", "name", "folder_name", "status"); + } + + public function propertyWebLanguage() + { + return $this->hasOne('App\Models\Language', 'code', 'default_language')->select("code", "name", "language_key"); + } + + public function propertyBookingEngine() + { + return $this->hasOne('App\Models\PropertyBookingEngine', 'property_id', 'property_id') + ->where('status', '=', 1) + ->select("id", "property_id", "channel_id", "token"); + } + + public function propertyWebMenuMenuMapping() + { + return $this->hasMany('App\Models\PropertyWebMenuMapping', 'property_id', 'property_id') + ->select("id", "property_id", "property_web_id", "property_web_menu_id", "order_number", "status", "type", "menu_code") + ->orderBy("order_number", "ASC"); + } + + public function propertyWebLanguageMapping() + { + return $this->hasMany('App\Models\PropertyWebLanguageMapping', 'property_id', 'property_id') + ->select("id", "property_id", "property_web_id", "language_code", "status"); + } + + public function propertyGroupMapping() + { + return $this->hasMany('App\Models\PropertyGroupMapping', 'property_id', 'property_id') + ->select("id", "property_id", "property_group_id", "order_number", "status") + ->where('status', 1); + } + + public function propertyWebPhotoMapping() + { + return $this->hasMany('App\Models\PropertyWebPhotoMapping', 'property_web_id', 'id') + ->where('status', '=', 1); + } + + public function property() + { + return $this->hasOne('App\Models\Property', 'id', 'property_id') + ->select('id', 'country', 'name', 'property_type_id', 'chain_id', 'official_name', + 'tax_office', 'tax_number', 'currency_type', 'country', 'content_code', 'status'); + } + + public function propertyWebAbout() + { + return $this->hasMany('App\Models\PropertyWebAboutUs', 'property_id', 'property_id')->select('id', 'property_id', 'language_code', 'value'); + } + + public function propertyWebPopup() + { + return $this->hasMany('App\Models\PropertyWebPopup', 'property_id', 'property_id') + ->select('id', 'property_id', 'language_code', 'code', 'title', 'start_date', 'url', 'end_date', 'image')->where('status', 1); + } + + public function propertyWebWeather() + { + return $this->hasOne('App\Models\PropertyWebWeather', 'property_id', 'property_id')->select('id', 'property_id', 'date', 'location', 'temp', 'conditions', 'icon'); + } + + + public function propertyWebComponent() + { + return $this->hasMany('App\Models\PropertyWebComponentMapping', 'property_id', 'property_id') + ->select('id', 'property_id', 'channel_id', 'component_id', 'parameter', 'language', 'status')->where('status', 1); + } + + public function propertyWebRoomMapping() + { + return $this->hasMany('App\Models\PropertyWebRoomMapping', 'property_web_id', 'id') + ->select("id", "property_id", "property_web_id", "property_room_id", "status") + ->where('status', 1); + } + + public function propertyWebPlaceMapping() + { + return $this->hasMany('App\Models\PropertyWebPlaceMapping', 'property_web_id', 'id') + ->select("id", "property_id", "property_web_id", "property_place_id", "status") + ->where('status', 1); + } + + public function propertyWebColorMapping() + { + return $this->hasMany('App\Models\PropertyWebColorMapping', 'property_web_id', 'id') + ->select('id', 'property_id', 'property_web_id', 'color_code', 'order_number') + ->where('status', 1) + ->orderBy('order_number'); + } + + public function propertyWebContent() + { + return $this->hasMany('App\Models\PropertyWebContent', 'property_id', 'property_id') + ->select('id', 'property_id', 'content_category_id', 'title', 'slug', 'language_code', 'content', 'image', 'status')->where('status', 1); + } + + public function propertyWebContentSummary() + { + return $this->hasMany('App\Models\PropertyWebContent', 'property_id', 'property_id') + ->select('id', 'property_id', 'content_category_id', 'title', 'slug', 'language_code', 'image', 'status')->where('status', 1); + } + + public function propertyWebMetaMapping() + { + return $this->hasMany('App\Models\PropertyWebMetaMapping', 'property_web_id', 'id') + ->where('status', 1); + } + +} diff --git a/app/Models/PropertyWebAboutUs.php b/app/Models/PropertyWebAboutUs.php new file mode 100644 index 0000000..68bad5e --- /dev/null +++ b/app/Models/PropertyWebAboutUs.php @@ -0,0 +1,24 @@ +parameter)) { + $attribute = json_decode($this->parameter, 1); + } + return $attribute; + } + + public function getIconUrlAttribute() + { + return config('app.url') . '/img/component-icons/' . $this->icon; + + } +} diff --git a/app/Models/PropertyWebComponentMapping.php b/app/Models/PropertyWebComponentMapping.php new file mode 100644 index 0000000..adafdaf --- /dev/null +++ b/app/Models/PropertyWebComponentMapping.php @@ -0,0 +1,45 @@ +parameter)) { + $attribute = json_decode($this->parameter, 1); + } + return $attribute; + } + + function getLanguageArrayAttribute() + { + $attribute = []; + if (!empty($this->language)) { + $attribute = json_decode($this->language, 1); + } + return $attribute; + } + + public function webComponent() + { + return $this->hasOne('App\Models\PropertyWebComponent', 'id', 'component_id')->select(['id', 'component', 'name','parameter','icon']); + } + +} diff --git a/app/Models/PropertyWebContent.php b/app/Models/PropertyWebContent.php new file mode 100644 index 0000000..89fe55d --- /dev/null +++ b/app/Models/PropertyWebContent.php @@ -0,0 +1,60 @@ +image; + + if (!empty($image)) { + $imageExplode = explode('.', $image, 2); + + $imageFileName = $imageExplode[0]; + $imageFileExtensionName = $imageExplode[1]; + + $imageUrl = [ + 'default' => Config::get('app.imageUrl') . '/property-web-content/' . $this->property_id . '/' . $imageFileName . '.' . $imageFileExtensionName, + 'thumb' => Config::get('app.imageUrl') . '/property-web-content/' . $this->property_id . '/' . $imageFileName . '_thumbnail.' . $imageFileExtensionName, + 'fixed' => Config::get('app.imageUrl') . '/property-web-content/' . $this->property_id . '/' . $imageFileName . '_medium.' . $imageFileExtensionName, + ]; + + } + + return $imageUrl; + } + + public function ContentCategory() + { + return $this->hasOne('App\Models\PropertyWebContentCategory', 'id', 'content_category_id')->select('id', 'name', 'language_key'); + } + + +} diff --git a/app/Models/PropertyWebContentCategory.php b/app/Models/PropertyWebContentCategory.php new file mode 100644 index 0000000..1fa1c57 --- /dev/null +++ b/app/Models/PropertyWebContentCategory.php @@ -0,0 +1,29 @@ +logo)) { + $logoUrl = Config::get('app.url') . '/property-group/' . $this->logo; + } + + return $logoUrl; + } + + public function propertyWebLanguage() + { + return $this->hasOne('App\Models\Language', 'code', 'default_language')->select("code", "name", "language_key"); + } + + public function propertyGroupMapping() + { + return $this->hasMany('App\Models\PropertyGroupMapping', 'property_group_id', 'property_group_id') + ->select("id", "property_id", "property_group_id", "order_number", "status") + ->where('status', 1); + } + +} diff --git a/app/Models/PropertyWebLanguageMapping.php b/app/Models/PropertyWebLanguageMapping.php new file mode 100644 index 0000000..3bd8054 --- /dev/null +++ b/app/Models/PropertyWebLanguageMapping.php @@ -0,0 +1,20 @@ +hasOne('App\Models\IpNationCountries','code', 'country_code')->select("code", "country"); + } + +} diff --git a/app/Models/PropertyWebMenu.php b/app/Models/PropertyWebMenu.php new file mode 100644 index 0000000..9c43b90 --- /dev/null +++ b/app/Models/PropertyWebMenu.php @@ -0,0 +1,20 @@ +hasOne('App\Models\PropertyWebMenu','id', 'property_web_menu_id') + ->select("id", "parent_id", "name", "language_key", "alias", "route", "type", "order_number", "icon", "status"); + } +} diff --git a/app/Models/PropertyWebMeta.php b/app/Models/PropertyWebMeta.php new file mode 100644 index 0000000..b54ae8e --- /dev/null +++ b/app/Models/PropertyWebMeta.php @@ -0,0 +1,26 @@ +hasOne('App\Models\Property', 'id', 'property_id'); + } + + public function propertyWeb() + { + return $this->hasOne('App\Models\PropertyWeb', 'id', 'property_web_id'); + } + + public function propertyWebMetaTag() + { + return $this->hasOne('App\Models\PropertyWebMetaTag', 'id', 'property_web_meta_tag_id'); + } + + public function propertyWebMeta() + { + return $this->hasOne('App\Models\PropertyWebMeta', 'id', 'property_web_meta_id'); + } + + public function getTextArrayAttribute() + { + return json_decode($this->text, true); + } +} diff --git a/app/Models/PropertyWebMetaTag.php b/app/Models/PropertyWebMetaTag.php new file mode 100644 index 0000000..f60af5e --- /dev/null +++ b/app/Models/PropertyWebMetaTag.php @@ -0,0 +1,26 @@ +hasOne('App\Models\PropertyPhoto','id', 'property_photo_id'); + } + + +} diff --git a/app/Models/PropertyWebPlaceMapping.php b/app/Models/PropertyWebPlaceMapping.php new file mode 100644 index 0000000..bf30245 --- /dev/null +++ b/app/Models/PropertyWebPlaceMapping.php @@ -0,0 +1,24 @@ +hasOne('App\Models\PropertyPlace','id', 'property_place_id')->select(); + } +} diff --git a/app/Models/PropertyWebPopup.php b/app/Models/PropertyWebPopup.php new file mode 100644 index 0000000..9b579be --- /dev/null +++ b/app/Models/PropertyWebPopup.php @@ -0,0 +1,55 @@ +image; + + if (!empty($image)) { + $imageExplode = explode('.', $image, 2); + + $imageFileName = $imageExplode[0]; + $imageFileExtensionName = $imageExplode[1]; + + $imageUrl = [ + 'default' => Config::get('app.imageUrl') . '/property-web-popup/' . $this->property_id . '/' . $imageFileName . '.' . $imageFileExtensionName, + 'thumb' => Config::get('app.imageUrl') . '/property-web-popup/' . $this->property_id . '/' . $imageFileName . '_thumbnail.' . $imageFileExtensionName, + 'fixed' => Config::get('app.imageUrl') . '/property-web-popup/' . $this->property_id . '/' . $imageFileName . '_medium.' . $imageFileExtensionName, + ]; + + } + + return $imageUrl; + } + + +} diff --git a/app/Models/PropertyWebReview.php b/app/Models/PropertyWebReview.php new file mode 100644 index 0000000..096de20 --- /dev/null +++ b/app/Models/PropertyWebReview.php @@ -0,0 +1,39 @@ +time)->format('d.m.Y H:i:s'); + } + +} diff --git a/app/Models/PropertyWebRoomMapping.php b/app/Models/PropertyWebRoomMapping.php new file mode 100644 index 0000000..314114a --- /dev/null +++ b/app/Models/PropertyWebRoomMapping.php @@ -0,0 +1,24 @@ +hasOne('App\Models\PropertyRoom','id', 'property_room_id')->select(); + } +} diff --git a/app/Models/PropertyWebSetup.php b/app/Models/PropertyWebSetup.php new file mode 100644 index 0000000..03d6f33 --- /dev/null +++ b/app/Models/PropertyWebSetup.php @@ -0,0 +1,19 @@ + config('app.url') . '/img/weather-icons/' . $this->icon . '.png', + 'monochrome' => config('app.url') . '/img/weather-icons-monochrome/' . $this->icon . '.png' + ]; + + return $iconSet; + + } + + public function getLanguageKeyAttribute() + { + + return 'property_web_weather-'.$this->icon; + + } + +} diff --git a/app/Models/ServiceLog.php b/app/Models/ServiceLog.php new file mode 100644 index 0000000..62cf6ad --- /dev/null +++ b/app/Models/ServiceLog.php @@ -0,0 +1,28 @@ +name . ' ' . $this->surname; + } + +} diff --git a/app/Models/UserPropertyMapping.php b/app/Models/UserPropertyMapping.php new file mode 100644 index 0000000..fd98bd9 --- /dev/null +++ b/app/Models/UserPropertyMapping.php @@ -0,0 +1,40 @@ +hasOne("App\Models\Property", 'id', 'property_id'); + } + + public function user() + { + return $this->hasOne("App\Models\User", 'id', 'user_id') + ->where('status', '=', 1); + } + +} diff --git a/app/Models/VWDestination.php b/app/Models/VWDestination.php new file mode 100644 index 0000000..b1be2a0 --- /dev/null +++ b/app/Models/VWDestination.php @@ -0,0 +1,29 @@ +hasOne('App\Models\BookingStatus', 'id', 'status') + ->select(['name', 'language_key', 'id'])->where('status', 1); + } + +} diff --git a/app/Notifications/DirectPushNotificationUser.php b/app/Notifications/DirectPushNotificationUser.php new file mode 100644 index 0000000..3a95c90 --- /dev/null +++ b/app/Notifications/DirectPushNotificationUser.php @@ -0,0 +1,80 @@ +param = $param; + } + + public function via($notifyRoutes) + { + return [OneSignalChannel::class]; + } + + public function toOneSignal($notifyRoutes) + { + + /*$param = [ + 'subject' => 'Otel Bilgileri Güncelleme', + 'message' => $params['property_info']['name']. ' Otel Bilgileri Güncellendi.', + 'oneSignalPlayerIds' => ['5931bd16-daac-43f4-824c-c953b8902e96'] + + ]; + Notification::route('OneSignal', null)->notify(new DirectPushNotificationUser($param));*/ + + $oneSignalService = App::make("App\Core\Service\OneSignalService"); + + $restUrl = 'https://onesignal.com/api/v1/notifications'; + + if (!empty($this->param['oneSignalPlayerIds'])) { + + $payloadJson = [ + 'include_player_ids' => $this->param['oneSignalPlayerIds'], + 'headings' => [ + 'en' => $this->param['subject'] + ], + 'contents' => [ + 'en' => $this->param['message'] + ] + ]; + + if (isset($this->param['subtitle'])) { + $payloadJson['subtitle'] = [ + 'en' => $this->param['subtitle'] + ]; + } + + if (isset($this->param['url'])) { + //$payloadJson['url'] = $this->param['url']; + $payloadJson['data']['url'] = $this->param['url']; + } + + + $sendOneSignalPushNotification = $oneSignalService->sendOneSignalPushNotification($restUrl, $payloadJson); + + } + + } +} + + diff --git a/app/Notifications/NewUserNotification.php b/app/Notifications/NewUserNotification.php new file mode 100644 index 0000000..c524721 --- /dev/null +++ b/app/Notifications/NewUserNotification.php @@ -0,0 +1,50 @@ +notificationParam = $notificationParam; + + } + + public function via($notificationReceiver) + { + return ['mail']; + } + + public function toMail($notificationReceiver) + { + + $notificationParam = $this->notificationParam; + + $MailMessage = (new MailMessage) + ->subject('Extranetwork New User') + ->from(config('mail.from.address'), config('mail.from.name')) + ->greeting('Extranetwork App New User') + ->line(new HtmlString('
Name Surname: '.$notificationParam['name'].' '.$notificationParam['surname'])) + ->line(new HtmlString('Property Name: '.$notificationParam['property_name'])) + ->line(new HtmlString('E-Mail: '.$notificationParam['email'])) + ->line(new HtmlString('Phone: '.$notificationParam['phone'])) + ->line(new HtmlString('Language: '.$notificationParam['language'])) + ->line(new HtmlString('Time: '. Carbon::now()->format('d.m.Y H:i:s'))); + + //echo $MailMessage->render(); die(); + + return $MailMessage; + } + + +} diff --git a/app/Notifications/PushNotificationPropertyUser.php b/app/Notifications/PushNotificationPropertyUser.php new file mode 100644 index 0000000..7df6b16 --- /dev/null +++ b/app/Notifications/PushNotificationPropertyUser.php @@ -0,0 +1,91 @@ +param = $param; + } + + public function via($notifyRoutes) + { + return [OneSignalChannel::class]; + } + + public function toOneSignal($notifyRoutes) + { + + $oneSignalService = App::make("App\Core\Service\OneSignalService"); + $userPropertyMappingService = App::make("App\Core\Service\UserPropertyMappingService"); + + $restUrl = 'https://onesignal.com/api/v1/notifications'; + + if (isset($this->param['property_id']) && !empty($this->param['property_id'])) { + + $propertyUserListRequest = [ + 'criteria' => [ + ['field' => 'status', 'condition' => '=', 'value' => 1], + ['field' => 'property_id', 'condition' => '=', 'value' => $this->param['property_id']], + ], + 'with' => ['user'] + ]; + $propertyUserList = $userPropertyMappingService->select($propertyUserListRequest); + + if ($propertyUserList['status'] == 'success') { + $propertyUserListCollection = collect($propertyUserList['data']); + $oneSignalPlayerIds = $propertyUserListCollection->where('user.onesignal_key', '!=', null)->pluck('user.onesignal_key')->toArray(); + + if (!empty($oneSignalPlayerIds)) { + + $payloadJson = [ + 'include_player_ids' => $oneSignalPlayerIds, + 'headings' => [ + 'en' => $this->param['subject'] + ], + 'contents' => [ + 'en' => $this->param['message'] + ] + ]; + + if (isset($this->param['subtitle'])) { + $payloadJson['subtitle'] = [ + 'en' => $this->param['subtitle'] + ]; + } + + if (isset($this->param['url'])) { + //$payloadJson['url'] = $this->param['url']; + $payloadJson['data']['url'] = $this->param['url']; + } + + $sendOneSignalPushNotification = $oneSignalService->sendOneSignalPushNotification($restUrl, $payloadJson); + + } + + + } + + + } + + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..bdce120 --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,20 @@ +app['auth']->viaRequest('api', function ($request) { + if ($request->input('api_token')) { + return Users::where('api_token', $request->input('api_token'))->first(); + } + }); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php new file mode 100644 index 0000000..95b029b --- /dev/null +++ b/app/Providers/EventServiceProvider.php @@ -0,0 +1,19 @@ + [ + 'App\Listeners\ExampleListener', + ], + ]; +} diff --git a/artisan b/artisan new file mode 100644 index 0000000..35c9141 --- /dev/null +++ b/artisan @@ -0,0 +1,35 @@ +#!/usr/bin/env php +make( + 'Illuminate\Contracts\Console\Kernel' +); + +exit($kernel->handle(new ArgvInput, new ConsoleOutput)); diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000..5b1720f --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,133 @@ +bootstrap(); + +/* +|-------------------------------------------------------------------------- +| Create The Application +|-------------------------------------------------------------------------- +| +| Here we will load the environment and create the application instance +| that serves as the central piece of this framework. We'll use this +| application as an "IoC" container and router for this framework. +| +*/ + +$app = new Laravel\Lumen\Application( + dirname(__DIR__) +); + +//$app->withFacades(false); +$app->withFacades(true, [ + 'Illuminate\Support\Facades\Event' => 'LumenEvent', +]); + +$app->configure('app'); +$app->configure('mail'); +$app->configure('languageSupport'); +$app->configure('language-pack-en'); +$app->configure('language-pack-tr'); +$app->configure('language-pack-de'); +$app->make('queue'); + + +$app->withEloquent(); + +/* +|-------------------------------------------------------------------------- +| Register Container Bindings +|-------------------------------------------------------------------------- +| +| Now we will register a few bindings in the service container. We will +| register the exception handler and the console kernel. You may add +| your own bindings here if you like or you can make another file. +| +*/ + +$app->singleton( + Illuminate\Contracts\Debug\ExceptionHandler::class, + App\Exceptions\Handler::class +); + +$app->singleton( + Illuminate\Contracts\Console\Kernel::class, + App\Console\Kernel::class +); + +/* +|-------------------------------------------------------------------------- +| Register Middleware +|-------------------------------------------------------------------------- +| +| Next, we will register the middleware with the application. These can +| be global middleware that run before and after each request into a +| route or middleware that'll be assigned to some specific routes. +| +*/ + +$app->middleware([ + 'cors' => App\Http\Middleware\CorsMiddleware::class, +]); + +$app->routeMiddleware([ + 'jwt.auth' => App\Http\Middleware\JwtMiddleware::class, + 'cors' => App\Http\Middleware\CorsMiddleware::class, + 'userRoutePermissionAuthorize' => App\Http\Middleware\UserRoutePermissionAuthorize::class, + 'property' => App\Http\Middleware\PropertyMiddleware::class, + 'contentWizard' => App\Http\Middleware\ContentWizardMiddleware::class, + 'checkPropertyChannelConnection' => App\Http\Middleware\CheckPropertyChannelConnectionMiddleware::class, + 'LanguageSetting' => App\Http\Middleware\LanguageSettingMiddleware::class, + 'myWebToken' => App\Http\Middleware\MyWebTokenMiddleware::class, + 'bookingEngineToken' => App\Http\Middleware\BookingEngineTokenMiddleware::class, + +]); + +/* +|-------------------------------------------------------------------------- +| Register Service Providers +|-------------------------------------------------------------------------- +| +| Here we will register all of the application's service providers which +| are used to bind services into the container. Service providers are +| totally optional, so you are not required to uncomment this line. +| +*/ + +// $app->register(App\Providers\AppServiceProvider::class); +// $app->register(App\Providers\AuthServiceProvider::class); +// $app->register(App\Providers\EventServiceProvider::class); + +/* +|-------------------------------------------------------------------------- +| Load The Application Routes +|-------------------------------------------------------------------------- +| +| Next we will include the routes file so that they can all be added to +| the application. This will provide all of the URLs the application +| can respond to, as well as the controllers that may handle them. +| +*/ + +$app->register(Illuminate\Redis\RedisServiceProvider::class); +$app->register(Intervention\Image\ImageServiceProviderLumen::class); +$app->register(Illuminate\Mail\MailServiceProvider::class); +$app->register(Illuminate\Notifications\NotificationServiceProvider::class); +$app->register(Maatwebsite\Excel\ExcelServiceProvider::class); + + +$app->alias('mailer', Illuminate\Mail\Mailer::class); +$app->alias('mailer', Illuminate\Contracts\Mail\Mailer::class); +$app->alias('mailer', Illuminate\Contracts\Mail\MailQueue::class); +$app->alias('Excel', Maatwebsite\Excel\Facades\Excel::class); + +$app->router->group([ + 'namespace' => 'App\Http\Controllers', +], function ($router) { + require __DIR__ . '/../routes/web.php'; +}); + +return $app; diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..2046570 --- /dev/null +++ b/composer.json @@ -0,0 +1,60 @@ +{ + "name": "laravel/lumen", + "description": "The Laravel Lumen Framework.", + "keywords": ["framework", "laravel", "lumen"], + "license": "MIT", + "type": "project", + "require": { + "php": ">=7.1.3", + "barryvdh/laravel-dompdf": "^0.8.7", + "firebase/php-jwt": "^5.0", + "google/cloud-vision": "^0.24.0", + "illuminate/mail": "5.8.*", + "illuminate/notifications": "^5.5", + "illuminate/redis": "5.8.*", + "intervention/image": "^2.5", + "laravel/lumen-framework": "5.8.*", + "league/flysystem": "^1.0", + "maatwebsite/excel": "^3.1", + "predis/predis": "^1.1", + "ramsey/uuid": "^4.1", + "stripe/stripe-php": "^7.66", + "symfony/serializer": "^5.2", + "symfony/translation": "4.4.1" + }, + "require-dev": { + "fzaninotto/faker": "^1.4", + "phpunit/phpunit": "^7.0", + "mockery/mockery": "^1.0" + }, + "autoload": { + "classmap": [ + "database/seeds", + "database/factories" + ], + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Core/Helper/helpers.php", + "app/Core/Helper/compat_l5.php" + ] + }, + "autoload-dev": { + "classmap": [ + "tests/" + ] + }, + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ] + }, + "config": { + "preferred-install": "dist", + "sort-packages": true, + "optimize-autoloader": true + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000..a6d3ff6 --- /dev/null +++ b/config/app.php @@ -0,0 +1,53 @@ + '1.5.22', + 'name' => env('APP_NAME', 'ExtraNetWork'), + 'env' => env('APP_ENV', 'production'), + 'client_server' => env('CLIENT_SERVER', 'https://app.extranetwork.com'), + 'debug' => env('APP_DEBUG', false), + 'url' => env('APP_URL', 'https://api.extranetwork.com'), + 'webUrl' => env('WEB_URL', 'https://www.extranetwork.com'), + 'timezone' => env('APP_TIMEZONE', 'Europe/Istanbul'), + 'locale' => env('APP_LOCALE', 'en'), + 'fallback_locale' => 'en', + 'jwt' => [ + 'secret' => env('JWT_SECRET', 'JhbGciOiJIUzI1N0eXAiOiJKV1QiLC'), + ], + 'fileSystemDriver' => env('FILESYSTEM_DRIVER', '/Users/bulutkuru/Desktop/uploads/'), + 'imageUrl' => env('IMAGE_URL', 'http://cdn.extranetwork.com.test'), + 'googleApplicationCredentials' => env('GOOGLE_APPLICATION_CREDENTIALS', ''), + 'log_slack' => env('LOG_SLACK_WEBHOOK_URL', 'https://hooks.slack.com/services/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'), + 'appLanguageJsonStoredFolder' => env('APP_LANGUAGE_JSON_STORED_FOLDER', '/home/project/app.extranetwork.com/build_public'), // for application language filepath + 'myWebLanguageJsonStoredFolder' => env('MYWEB_LANGUAGE_JSON_STORED_FOLDER', '/home/project/myweb.extranetwork.com/resources/lang/'), // for myweb language filepath + 'wwwLanguageJsonStoredFolder' => env('WWW_LANGUAGE_JSON_STORED_FOLDER', '/home/project/www.extranetwork.com/resources/lang/'), // for www language filepath + 'bookingEngineLanguageJsonStoredFolder' => env('BOOKING_ENGINE_LANGUAGE_JSON_STORED_FOLDER', '/home/project/be.extranetwork.com/resources/lang/'), // for booking.engine language filepath + 'mainHostAddress' => env('MAIN_HOST_ADDRESS', 'https://myweb.extranetwork.com'), // main host address + 'getMyWebCNAME' => 'myweb.extranetwork.com', // MyWeb CNAME Hosting Alias + "myIP" => "88.247.112.165", + "bookingEngineUrl" => env('BOOKING_ENGINE_URL', 'https://be.extranetwork.com'), + "zipFilesUrl" => env('ZIP_FILES_URL', 'https://api.extranetwork.com/property-zip-files/'), + "propertyFilesUrl" => env('PROPERTY_FILES_URL', 'https://cdn.extranetwork.com/property-files/'), + "mailSenderAddress" => 'noreply@extranetwork.com', + "logMailAddress" => 'burhan@extranetwork.com', + "paymentFormLink" => env('PAYMENT_FORM_LINK', 'https://be.extranetwork.com/link/'), + "enwContactFormMailTo" => env('ENW_CONTACT_FORM_MAILTO', 'info@extranetwork.com'), + 'channelManager' => [ + 'reseliva' => [ + 'userId' => 'apiuser@extranetwork.com', + 'userPassword' => 'ExtrntWrk21*!' + ] + ], + 'stripeENWKey' => env('STRIPE_ENWKEY', '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'), + 'key' => 'mWx0BnBvK5rp95PXjIiLHxc29nxSkG0X', //z8IQfoLXSrIPk7gwnh3EbOos8wkzHIzY, vPdC6dXrK8LWbLxrYNxDXUGcggGg58ff + 'cipher' => 'AES-256-CBC', + 'taxOptions' => [ + 'TR' => [ + 'title' => 'Accommodation Tax', + 'language_key' => 'api-tax-accommodation_tax', + 'rate' => '%2', + ] + ], + 'openAISecretKey' => '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' +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..6291f86 --- /dev/null +++ b/config/cache.php @@ -0,0 +1,101 @@ + env('CACHE_DRIVER', 'redis'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + */ + + 'stores' => [ + + 'apc' => [ + 'driver' => 'apc', + ], + + 'array' => [ + 'driver' => 'array', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'cache', + 'connection' => null, + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'cache', + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing a RAM based store such as APC or Memcached, there might + | be other applications utilizing the same cache. So, we'll specify a + | value to get prefixed to all our keys so we can avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', 'extranetworkApiCache'), + +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..8de402c --- /dev/null +++ b/config/database.php @@ -0,0 +1,135 @@ + env('DB_CONNECTION', 'mysql'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => env('DB_PREFIX', ''), + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'host' => env('DB_HOST', 'mysql-master'), + 'port' => env('DB_PORT', 3306), + 'database' => env('DB_DATABASE', 'extranetwork'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', 'QgARrezer_1'), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => env('DB_PREFIX', ''), + 'strict' => env('DB_STRICT_MODE', true), + 'engine' => env('DB_ENGINE', null), + //'timezone' => env('DB_TIMEZONE', '+00:00'), + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', 5432), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => env('DB_PREFIX', ''), + 'schema' => env('DB_SCHEMA', 'public'), + 'sslmode' => env('DB_SSL_MODE', 'prefer'), + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', 1433), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => env('DB_PREFIX', ''), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run in the database. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer set of commands than a typical key-value systems + | such as APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'predis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'predis'), + 'prefix' => env('REDIS_PREFIX', 'api:'), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', 'redis'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => env('REDIS_DB', 0), + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', 'redis'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => env('REDIS_DB', 0), + ], + + ], + +]; diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 0000000..02880c1 --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,69 @@ + env('FILESYSTEM_DRIVER', 'local'), + + /* + |-------------------------------------------------------------------------- + | Default Cloud Filesystem Disk + |-------------------------------------------------------------------------- + | + | Many applications store files both locally and in the cloud. For this + | reason, you may specify a default "cloud" driver here. This driver + | will be bound as the Cloud disk implementation in the container. + | + */ + + 'cloud' => env('FILESYSTEM_CLOUD', 's3'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Here you may configure as many filesystem "disks" as you wish, and you + | may even configure multiple disks of the same driver. Defaults have + | been setup for each driver as an example of the required options. + | + | Supported Drivers: "local", "ftp", "sftp", "s3", "rackspace" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + ], + + 'public' => [ + 'driver' => 'local', + 'root' => public_path(), + 'url' => config('app.url'), + 'visibility' => 'public', + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + ], + + ], + +]; diff --git a/config/language-pack-de.php b/config/language-pack-de.php new file mode 100644 index 0000000..9545803 --- /dev/null +++ b/config/language-pack-de.php @@ -0,0 +1,90 @@ +"en", + "availableLanguages" => ["tr", "en", "de" ] + ]; \ No newline at end of file diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 0000000..4e47388 --- /dev/null +++ b/config/mail.php @@ -0,0 +1,125 @@ + env('MAIL_DRIVER', 'smtp'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Address + |-------------------------------------------------------------------------- + | + | Here you may provide the host address of the SMTP server used by your + | applications. A default option is provided that is compatible with + | the Mailgun mail service which will provide reliable deliveries. + | + */ + + 'host' => env('MAIL_HOST', 'smtp.gmail.com'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Port + |-------------------------------------------------------------------------- + | + | This is the SMTP port used by your application to deliver e-mails to + | users of the application. Like the host we have set this value to + | stay compatible with the Mailgun e-mail application by default. + | + */ + + 'port' => env('MAIL_PORT', 587), + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => ['address' => 'noreply@extranetwork.com', 'name' => 'ExtraNetWork'], + + /* + |-------------------------------------------------------------------------- + | E-Mail Encryption Protocol + |-------------------------------------------------------------------------- + | + | Here you may specify the encryption protocol that should be used when + | the application send e-mail messages. A sensible default using the + | transport layer security protocol should provide great security. + | + */ + + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + + /* + |-------------------------------------------------------------------------- + | SMTP Server Username + |-------------------------------------------------------------------------- + | + | If your SMTP server requires a username for authentication, you should + | set it here. This will get used to authenticate with your server on + | connection. You may also set the "password" value below this one. + | + */ + + 'username' => env('MAIL_USERNAME','noreply@extranetwork.com'), + + /* + |-------------------------------------------------------------------------- + | SMTP Server Password + |-------------------------------------------------------------------------- + | + | Here you may set the password required by your SMTP server to send out + | messages from your application. This will be given to the server on + | connection so that the application will be able to send messages. + | + */ + + 'password' => env('MAIL_PASSWORD','jodgrovwibymcetb'), + //8KSLQzxuGg4vEfSFkaA4m + + /* + |-------------------------------------------------------------------------- + | Sendmail System Path + |-------------------------------------------------------------------------- + | + | When using the "sendmail" driver to send e-mails, we will need to know + | the path to where Sendmail lives on this server. A default path has + | been provided here, which will work well on most of your systems. + | + */ + + 'sendmail' => '/usr/sbin/sendmail -bs', + + /* + |-------------------------------------------------------------------------- + | Mail "Pretend" + |-------------------------------------------------------------------------- + | + | When this option is enabled, e-mail will not actually be sent over the + | web and will instead be written to your application's logs files so + | you may inspect the message. This is great for local development. + | + */ + + 'pretend' => false, + +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 0000000..f9bc39c --- /dev/null +++ b/config/queue.php @@ -0,0 +1,86 @@ + env('QUEUE_CONNECTION', 'database'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Lumen. You are free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => env('QUEUE_TABLE', 'jobs'), + 'queue' => 'default', + 'retry_after' => 90, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + 'retry_after' => 90, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('SQS_KEY', 'your-public-key'), + 'secret' => env('SQS_SECRET', 'your-secret-key'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'your-queue-name'), + 'region' => env('SQS_REGION', 'us-east-1'), + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('QUEUE_REDIS_CONNECTION', 'default'), + 'queue' => 'default', + 'retry_after' => 90, + 'block_for' => null, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + + 'failed' => [ + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => env('QUEUE_FAILED_TABLE', 'failed_jobs'), + ], + +]; diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php new file mode 100644 index 0000000..61cd36b --- /dev/null +++ b/database/factories/ModelFactory.php @@ -0,0 +1,21 @@ +define(App\Models\Users::class, function (Faker\Generator $faker) { + return [ + 'name' => $faker->name, + 'email' => $faker->unique()->email, + 'password' => Hash::make('12345'), + ]; +}); diff --git a/database/migrations/.gitkeep b/database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/database/migrations/2019_09_17_103201_create_user_table.php b/database/migrations/2019_09_17_103201_create_user_table.php new file mode 100644 index 0000000..a150d82 --- /dev/null +++ b/database/migrations/2019_09_17_103201_create_user_table.php @@ -0,0 +1,51 @@ +increments('id'); + $table->string('gender',1); + $table->string('name', 255); + $table->string('surname',255); + $table->char('email',128); + $table->string('password',255); + $table->string('phone',100)->nullable(); + $table->integer('user_type')->default(1); + $table->string('locale',2)->default('en'); + $table->tinyInteger('status')->default(0)->index(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('user', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique('email'); + }); + } + + + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user'); + } +} diff --git a/database/migrations/2019_09_17_103202_create_language_table.php b/database/migrations/2019_09_17_103202_create_language_table.php new file mode 100644 index 0000000..4b4a93c --- /dev/null +++ b/database/migrations/2019_09_17_103202_create_language_table.php @@ -0,0 +1,68 @@ +increments('id'); + $table->string('code', 2); + $table->string('name', 45); + $table->tinyInteger('status')->default(1)->index(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('language', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique('code'); + }); + + + Schema::create('language_locale', function (Blueprint $table) { + $table->increments('id'); + $table->string('language_code',2); + $table->string('name', 45); + $table->string('locale',2); + $table->tinyInteger('status')->default(1)->index(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + }); + + Schema::table('language_locale', function (Blueprint $table) { + $table->foreign('language_code')->references('code')->on('language'); + $table->foreign('locale')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['language_code','locale']); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('language_locale'); + Schema::dropIfExists('language'); + } +} diff --git a/database/migrations/2019_09_17_103203_create_property_chain_table.php b/database/migrations/2019_09_17_103203_create_property_chain_table.php new file mode 100644 index 0000000..ebeb8b1 --- /dev/null +++ b/database/migrations/2019_09_17_103203_create_property_chain_table.php @@ -0,0 +1,42 @@ +increments('id'); + $table->string('name',100); + $table->string('loyalty',30)->nullable(); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_chain',function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_chain'); + } +} diff --git a/database/migrations/2019_09_17_103204_create_currency_table.php b/database/migrations/2019_09_17_103204_create_currency_table.php new file mode 100644 index 0000000..4477df6 --- /dev/null +++ b/database/migrations/2019_09_17_103204_create_currency_table.php @@ -0,0 +1,66 @@ +increments('id'); + $table->string('code', 3)->unique(); + $table->string('name', 45); + + $table->tinyInteger('status')->default(1)->index(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('currency', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('currency_locale', function (Blueprint $table) { + $table->increments('id'); + $table->string('currency_code', 3); + $table->string('name', 45); + $table->string('locale', 2); + + $table->tinyInteger('status')->default(1)->index(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + }); + Schema::table('currency_locale', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('currency_code')->references('code')->on('currency'); + $table->foreign('locale')->references('code')->on('language'); + $table->unique(['currency_code','locale']); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('currency_locale'); + Schema::dropIfExists('currency'); + } +} diff --git a/database/migrations/2019_09_17_103205_create_property_type_table.php b/database/migrations/2019_09_17_103205_create_property_type_table.php new file mode 100644 index 0000000..e4f27ed --- /dev/null +++ b/database/migrations/2019_09_17_103205_create_property_type_table.php @@ -0,0 +1,64 @@ +increments('id'); + $table->string('name',200); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + + Schema::create('property_type_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('type_id'); + $table->string('name',200); + $table->string('locale',2); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + /** + * Table Relations + */ + Schema::table('property_type',function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::table('property_type_locale',function (Blueprint $table) { + $table->foreign('type_id')->references('id')->on('property_type'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_type_locale'); + Schema::dropIfExists('property_type'); + } +} diff --git a/database/migrations/2019_09_17_103610_create_property_table.php b/database/migrations/2019_09_17_103610_create_property_table.php new file mode 100644 index 0000000..637e0c8 --- /dev/null +++ b/database/migrations/2019_09_17_103610_create_property_table.php @@ -0,0 +1,55 @@ +increments('id'); + $table->string('name',255); + $table->unsignedInteger('destination_id')->nullable(); + $table->unsignedInteger('property_type_id')->nullable(); + $table->unsignedInteger('chain_id')->nullable(); + $table->double('rating')->nullable(); + $table->string('official_name',255)->nullable(); + $table->string('tax_office',255)->nullable(); + $table->string('tax_number',255)->nullable(); + $table->string('currency_type', 3)->nullable(); + $table->string('checkin_time', 50)->nullable(); + $table->string('checkout_time', 50)->nullable(); + $table->tinyInteger('status')->default(1)->index(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property', function (Blueprint $table) { + + $table->foreign('property_type_id')->references('id')->on('property_type'); + $table->foreign('chain_id')->references('id')->on('property_chain'); + $table->foreign('currency_type')->references('code')->on('currency'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property'); + } +} diff --git a/database/migrations/2019_09_17_110258_create_temp_property_table.php b/database/migrations/2019_09_17_110258_create_temp_property_table.php new file mode 100644 index 0000000..cd67f15 --- /dev/null +++ b/database/migrations/2019_09_17_110258_create_temp_property_table.php @@ -0,0 +1,41 @@ +integerIncrements('id'); + $table->string('name',255); + $table->integer('destination_id'); + //TODO: Giata_id int mi olacak string mi olacak. + $table->string('giata_id',50); + $table->tinyInteger('status')->default(1)->index(); + $table->unsignedInteger('created_by')->nullable(); + $table->unsignedInteger('updated_by')->nullable(); + $table->integer('created_at')->nullable(); + $table->integer('updated_at')->nullable(); + }); + + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('temp_property'); + } +} diff --git a/database/migrations/2019_09_17_121431_create_user_property_mapping_table.php b/database/migrations/2019_09_17_121431_create_user_property_mapping_table.php new file mode 100644 index 0000000..02c0584 --- /dev/null +++ b/database/migrations/2019_09_17_121431_create_user_property_mapping_table.php @@ -0,0 +1,46 @@ +increments('id'); + $table->unsignedInteger('user_id'); + $table->unsignedInteger('property_id'); + $table->tinyInteger('status')->default(1)->index(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + + Schema::table('user_property_mapping', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('user_id')->references('id')->on('user'); + $table->foreign('property_id')->references('id')->on('property'); + $table->unique(['user_id','property_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_property_mapping'); + } +} diff --git a/database/migrations/2019_09_25_112056_create_property_content_category_table.php b/database/migrations/2019_09_25_112056_create_property_content_category_table.php new file mode 100644 index 0000000..ce7e25e --- /dev/null +++ b/database/migrations/2019_09_25_112056_create_property_content_category_table.php @@ -0,0 +1,65 @@ +increments('id'); + $table->string('name'); + $table->tinyInteger('status'); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_content_category', function (Blueprint $table) { + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('property_content_category_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('content_category_id'); + $table->string('name'); + $table->string('locale',2); + $table->tinyInteger('status'); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_content_category_locale', function (Blueprint $table) { + + $table->foreign('content_category_id')->references('id')->on('property_content_category'); + $table->foreign('locale')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['content_category_id', 'locale'],'language_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_content_category_locale'); + + Schema::dropIfExists('property_content_category'); + } +} diff --git a/database/migrations/2019_09_26_123443_create_property_content_table.php b/database/migrations/2019_09_26_123443_create_property_content_table.php new file mode 100644 index 0000000..a5e490c --- /dev/null +++ b/database/migrations/2019_09_26_123443_create_property_content_table.php @@ -0,0 +1,52 @@ +increments('id'); + $table->unsignedInteger('property_id'); + $table->unsignedInteger('content_category_id'); + $table->mediumText('content')->nullable(); + $table->string('locale',2); + $table->tinyInteger('status'); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_content', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('content_category_id')->references('id')->on('property_content_category'); + $table->foreign('locale')->references('code')->on('language'); + $table->unique(['property_id', 'content_category_id', 'locale'],'content_unique'); + }); + + + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + Schema::dropIfExists('property_content'); + } +} diff --git a/database/migrations/2019_09_30_130301_create_property_fact_table.php b/database/migrations/2019_09_30_130301_create_property_fact_table.php new file mode 100644 index 0000000..32c9142 --- /dev/null +++ b/database/migrations/2019_09_30_130301_create_property_fact_table.php @@ -0,0 +1,68 @@ +increments('id'); + $table->string('name','250'); + $table->integer('parent_id',false,true)->nullable(); + $table->tinyInteger('type')->default(0); + $table->tinyInteger('order_number')->default(0); + $table->string('icon')->nullable(); + $table->tinyInteger('status',false,true)->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('property_fact_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('fact_id'); + $table->string('name'); + $table->string('locale',2); + $table->tinyInteger('status'); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + /** + * Table Relations + */ + Schema::table('property_fact',function (Blueprint $table) { + $table->foreign('parent_id')->references('id')->on('property_fact'); + }); + + Schema::table('property_fact_locale',function (Blueprint $table) { + $table->foreign('fact_id')->references('id')->on('property_fact'); + $table->foreign('locale')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['fact_id', 'locale'],'pfl_language_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_fact_locale'); + Schema::dropIfExists('property_fact'); + } +} diff --git a/database/migrations/2019_09_30_130303_create_property_unit_table.php b/database/migrations/2019_09_30_130303_create_property_unit_table.php new file mode 100644 index 0000000..1dee2d3 --- /dev/null +++ b/database/migrations/2019_09_30_130303_create_property_unit_table.php @@ -0,0 +1,60 @@ +increments('id'); + $table->string('name',100); + $table->string('short_name',45); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('property_unit_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('unit_id'); + $table->string('name'); + $table->string('locale',2); + $table->tinyInteger('status'); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + /** + * Table Relation + */ + Schema::table('property_unit_locale',function (Blueprint $table) { + $table->foreign('unit_id')->references('id')->on('property_unit'); + $table->foreign('locale')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['unit_id', 'locale'],'pul_language_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_unit_locale'); + Schema::dropIfExists('property_unit'); + } +} diff --git a/database/migrations/2019_09_30_130304_create_property_fact_attribute_table.php b/database/migrations/2019_09_30_130304_create_property_fact_attribute_table.php new file mode 100644 index 0000000..dfb1240 --- /dev/null +++ b/database/migrations/2019_09_30_130304_create_property_fact_attribute_table.php @@ -0,0 +1,69 @@ +increments('id'); + $table->string('name','250'); + $table->integer('unit_id',false,true)->default(0); + $table->tinyInteger('type')->default(0); + $table->tinyInteger('order_number')->default(0); + $table->tinyInteger('status',false,true)->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + + Schema::create('property_fact_attribute_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('attribute_id'); + $table->string('name'); + $table->string('locale',2); + $table->tinyInteger('status'); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + /** + * Table Relations + */ + + Schema::table('property_fact_attribute',function (Blueprint $table) { + $table->foreign('unit_id')->references('id')->on('property_unit'); + }); + + Schema::table('property_fact_attribute_locale',function (Blueprint $table) { + $table->foreign('attribute_id')->references('id')->on('property_fact_attribute'); + $table->foreign('locale')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['attribute_id', 'locale'],'pfal_language_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_fact_attribute_locale'); + Schema::dropIfExists('property_fact_attribute'); + } +} diff --git a/database/migrations/2019_09_30_131650_create_property_fact_attribute_mapping_table.php b/database/migrations/2019_09_30_131650_create_property_fact_attribute_mapping_table.php new file mode 100644 index 0000000..e190fd9 --- /dev/null +++ b/database/migrations/2019_09_30_131650_create_property_fact_attribute_mapping_table.php @@ -0,0 +1,41 @@ +increments('id'); + $table->integer('fact_id')->unsigned(); + $table->integer('attribute_id')->unsigned(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_fact_attribute_mapping',function (Blueprint $table) { + $table->foreign('fact_id')->references('id')->on('property_fact'); + $table->foreign('attribute_id')->references('id')->on('property_fact_attribute'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_fact_attribute_mapping'); + } +} diff --git a/database/migrations/2019_09_30_132133_create_property_fact_mapping_table.php b/database/migrations/2019_09_30_132133_create_property_fact_mapping_table.php new file mode 100644 index 0000000..ea2c7ef --- /dev/null +++ b/database/migrations/2019_09_30_132133_create_property_fact_mapping_table.php @@ -0,0 +1,47 @@ +increments('id'); + $table->integer('property_id')->unsigned(); + $table->integer('fact_id')->unsigned(); + $table->integer('attribute_id')->unsigned()->nullable(); + $table->integer('unit_id')->unsigned()->nullable(); + $table->string('value',100)->nullable()->default(null); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_fact_mapping',function (Blueprint $table) { + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('fact_id')->references('id')->on('property_fact'); + $table->foreign('attribute_id')->references('id')->on('property_fact_attribute'); + $table->foreign('unit_id')->references('id')->on('property_unit'); + $table->unique(['property_id', 'fact_id','attribute_id'],'pfam_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_fact_mapping'); + } +} diff --git a/database/migrations/2019_10_02_111700_create_property_contact_table.php b/database/migrations/2019_10_02_111700_create_property_contact_table.php new file mode 100644 index 0000000..8bbc966 --- /dev/null +++ b/database/migrations/2019_10_02_111700_create_property_contact_table.php @@ -0,0 +1,56 @@ +increments('id'); + $table->integer('property_id')->unsigned(); + $table->string('phone',50)->nullable(); + $table->string('phone_code',10)->nullable(); + $table->string('mobile',50)->nullable(); + $table->string('mobile_code',10)->nullable(); + $table->string('fax',50)->nullable(); + $table->string('fax_code',10)->nullable(); + $table->string('email',100)->nullable(); + $table->integer('destination_id')->unsigned(); + $table->string('web',100)->nullable(); + $table->string('zip_code',20)->nullable(); + $table->string('address',200)->nullable(); + $table->double('latitude',10,6)->nullable(); + $table->double('longitude',10,6)->nullable(); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_contact',function (Blueprint $table) { + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['property_id'],'pc_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_contact'); + } +} diff --git a/database/migrations/2019_10_02_120109_create_property_executive_type_table.php b/database/migrations/2019_10_02_120109_create_property_executive_type_table.php new file mode 100644 index 0000000..cbcb26c --- /dev/null +++ b/database/migrations/2019_10_02_120109_create_property_executive_type_table.php @@ -0,0 +1,64 @@ +increments('id'); + $table->string('name',200); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('property_executive_type_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('executive_id'); + $table->string('name',200); + $table->string('locale',2); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + /** + * Table Relations + */ + Schema::table('property_executive_type',function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::table('property_executive_type_locale',function (Blueprint $table) { + $table->foreign('executive_id')->references('id')->on('property_executive_type'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_executive_type_locale'); + Schema::dropIfExists('property_executive_type'); + } +} diff --git a/database/migrations/2019_10_02_120724_create_property_executive_table.php b/database/migrations/2019_10_02_120724_create_property_executive_table.php new file mode 100644 index 0000000..bb338e7 --- /dev/null +++ b/database/migrations/2019_10_02_120724_create_property_executive_table.php @@ -0,0 +1,53 @@ +increments('id'); + $table->unsignedInteger('property_id'); + $table->unsignedInteger('executive_type_id'); + $table->string('name_surname',200)->nullable(); + $table->string('email',100)->nullable(); + $table->string('phone_code',10)->nullable(); + $table->string('phone',50)->nullable(); + $table->string('mobile_code',10)->nullable(); + $table->string('mobile',50)->nullable(); + $table->string('fax_code',10)->nullable(); + $table->string('fax',50)->nullable(); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_executive',function (Blueprint $table) { + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('executive_type_id')->references('id')->on('property_executive_type'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['property_id', 'executive_type_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_executive'); + } +} diff --git a/database/migrations/2019_10_02_133201_create_property_additional_info_key_table.php b/database/migrations/2019_10_02_133201_create_property_additional_info_key_table.php new file mode 100644 index 0000000..1e56f40 --- /dev/null +++ b/database/migrations/2019_10_02_133201_create_property_additional_info_key_table.php @@ -0,0 +1,63 @@ +increments('id'); + $table->string('additional_info_key',100); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('property_additional_info_key_locale', function (Blueprint $table) { + + $table->increments('id'); + $table->unsignedInteger('additional_info_key_id'); + $table->string('additional_info_key',200); + $table->string('locale',2); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_additional_info_key', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['additional_info_key'],'padilk_unique'); + }); + + Schema::table('property_additional_info_key_locale', function (Blueprint $table) { + $table->foreign('additional_info_key_id','paikl_additional_info_key_id_fk')->references('id')->on('property_additional_info_key'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['additional_info_key_id','locale'],'padilkl_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_additional_info_key_locale'); + Schema::dropIfExists('property_additional_info_key'); + } +} diff --git a/database/migrations/2019_10_02_134621_create_property_additional_info_table.php b/database/migrations/2019_10_02_134621_create_property_additional_info_table.php new file mode 100644 index 0000000..c352673 --- /dev/null +++ b/database/migrations/2019_10_02_134621_create_property_additional_info_table.php @@ -0,0 +1,46 @@ +increments('id'); + $table->unsignedInteger('property_id'); + $table->unsignedInteger('additional_info_key_id'); + $table->string('value',100)->nullable(); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_additional_info',function (Blueprint $table){ + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('additional_info_key_id')->references('id')->on('property_additional_info_key'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['property_id','additional_info_key_id' ],'pai_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_additional_info'); + } +} diff --git a/database/migrations/2019_10_07_141400_create_property_photo_table.php b/database/migrations/2019_10_07_141400_create_property_photo_table.php new file mode 100644 index 0000000..653c816 --- /dev/null +++ b/database/migrations/2019_10_07_141400_create_property_photo_table.php @@ -0,0 +1,135 @@ +increments('id'); + $table->unsignedInteger('property_id'); + $table->unsignedInteger('property_photo_category_id')->nullable(); + $table->string('photo_path'); + $table->string('photo_name',200); + $table->double('photo_rank')->nullable(); + $table->string('file_size')->nullable(); + $table->string('file_ext')->nullable(); + $table->string('photo_resolution')->nullable(); + $table->integer('photo_order')->nullable(); + $table->tinyInteger('is_default')->default(0); + $table->tinyInteger('is_temp')->default(1); + $table->tinyInteger('status',false,true)->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('property_photo_category', function (Blueprint $table) { + $table->increments('id'); + $table->string('category_name',50)->unique(); + $table->tinyInteger('status',false,true)->default(0); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('property_photo_category_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('property_photo_category_id'); + $table->string('category_name'); + $table->string('locale',2); + $table->tinyInteger('status',false,true)->default(0); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('photo_google_label', function (Blueprint $table) { + $table->increments('id'); + $table->string('label', 50)->unique(); + $table->tinyInteger('status',false,true)->default(0); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('property_photo_category_label_mapping', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('property_photo_category_id'); + $table->unsignedInteger('photo_google_label_id'); + $table->tinyInteger('status',false,true)->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + + /** + * Table Relations + */ + Schema::table('property_photo',function (Blueprint $table) { + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('property_photo_category_id','pp_property_photo_category_id')->references('id')->on('property_photo_category'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + + }); + + /** + * Table Relations + */ + Schema::table('property_photo_category_label_mapping',function (Blueprint $table) { + $table->foreign('property_photo_category_id','ppclm_property_photo_category_id')->references('id')->on('property_photo_category'); + $table->foreign('photo_google_label_id','ppclm_photo_google_label_id')->references('id')->on('photo_google_label'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + /** + * Table Relations + */ + Schema::table('property_photo_category',function (Blueprint $table) { + $table->unique('category_name','pr_ph_ctg_name'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + /** + * Table Relations + */ + Schema::table('property_photo_category_locale',function (Blueprint $table) { + $table->foreign('property_photo_category_id','ppcl_property_photo_category_id')->references('id')->on('property_photo_category'); + $table->unique(['property_photo_category_id', 'locale'],'ppcl_category_id_locale'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_photo'); + Schema::dropIfExists('property_photo_category_label_mapping'); + Schema::dropIfExists('property_photo_category_locale'); + Schema::dropIfExists('property_photo_category'); + Schema::dropIfExists('photo_google_label'); + } +} diff --git a/database/migrations/2019_10_11_121418_create_property_config_table.php b/database/migrations/2019_10_11_121418_create_property_config_table.php new file mode 100644 index 0000000..7883cbf --- /dev/null +++ b/database/migrations/2019_10_11_121418_create_property_config_table.php @@ -0,0 +1,46 @@ +increments('id'); + $table->unsignedInteger('property_id'); + $table->string('config_key',64); + $table->text('config_value_json') ; + $table->tinyInteger('status'); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + + Schema::table('property_config', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('property_id')->references('id')->on('property'); + $table->unique(['property_id', 'config_key'],'config_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_config'); + } +} diff --git a/database/migrations/2019_10_14_111402_create_property_module_table.php b/database/migrations/2019_10_14_111402_create_property_module_table.php new file mode 100644 index 0000000..446c485 --- /dev/null +++ b/database/migrations/2019_10_14_111402_create_property_module_table.php @@ -0,0 +1,64 @@ +increments('id'); + $table->string('name',200); + $table->tinyInteger('point')->nullable(); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_module',function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + + Schema::create('property_module_mapping', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('property_module_id'); + $table->unsignedInteger('property_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_module_mapping',function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('property_module_id')->references('id')->on('property_module'); + + $table->unique(['property_module_id','property_id'], 'mapping_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_module_mapping'); + Schema::dropIfExists('property_module'); + } +} diff --git a/database/migrations/2019_10_18_102754_create_permission_table.php b/database/migrations/2019_10_18_102754_create_permission_table.php new file mode 100644 index 0000000..cd79501 --- /dev/null +++ b/database/migrations/2019_10_18_102754_create_permission_table.php @@ -0,0 +1,133 @@ +increments('id'); + $table->string('name',128); + $table->string('code',128)->unique(); + $table->unsignedInteger('parent_id')->nullable(); + $table->tinyInteger('is_menu')->nullable()->default(0); + $table->string('menu_detail', 255)->nullable(); + $table->tinyInteger('menu_order')->nullable()->default(0); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('permission',function (Blueprint $table) { + $table->foreign('parent_id')->references('id')->on('permission'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + + }); + + Schema::create('permission_group', function (Blueprint $table) { + $table->increments('id'); + $table->string('name',128); + $table->tinyInteger('is_admin')->nullable()->default(0); + $table->string('parameter_rules', 255)->nullable(); + $table->tinyInteger('is_private_permission')->nullable(); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('permission_group',function (Blueprint $table) { + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + + }); + + Schema::create('permission_group_mapping', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('permission_group_id'); + $table->unsignedInteger('permission_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('permission_group_mapping',function (Blueprint $table) { + $table->foreign('permission_group_id')->references('id')->on('permission_group'); + $table->foreign('permission_id')->references('id')->on('permission'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['permission_group_id', 'permission_id'], 'pgm_unique'); + }); + + Schema::create('permission_group_user_mapping', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('property_id'); + $table->unsignedInteger('user_id'); + $table->unsignedInteger('permission_group_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('permission_group_user_mapping',function (Blueprint $table) { + $table->foreign('permission_group_id')->references('id')->on('permission_group'); + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('user_id')->references('id')->on('user'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['permission_group_id', 'property_id', 'user_id'], 'pgu_unique'); + + }); + + Schema::create('permission_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('permission_id'); + $table->string('name',128); + $table->string('locale',2); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('permission_locale', function (Blueprint $table) { + $table->foreign('permission_id')->references('id')->on('permission'); + $table->foreign('locale')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['permission_id', 'locale'],'language_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('permission_group_user_mapping'); + Schema::dropIfExists('permission_group_mapping'); + Schema::dropIfExists('permission_group'); + Schema::dropIfExists('permission_locale'); + Schema::dropIfExists('permission'); + } +} diff --git a/database/migrations/2019_11_06_103124_create_api_access_token_table.php b/database/migrations/2019_11_06_103124_create_api_access_token_table.php new file mode 100644 index 0000000..0a163b3 --- /dev/null +++ b/database/migrations/2019_11_06_103124_create_api_access_token_table.php @@ -0,0 +1,40 @@ +<bigIncrements('id'); + $table->string('token','64')->unique(); + $table->integer('expire_date'); + $table->unsignedInteger('user_id'); + $table->tinyInteger('invalidate'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('api_access_token',function (Blueprint $table) { + $table->foreign('user_id')->references('id')->on('user'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('api_access_token'); + } +} diff --git a/database/migrations/2019_11_14_174036_add_hash_key_to_user.php b/database/migrations/2019_11_14_174036_add_hash_key_to_user.php new file mode 100644 index 0000000..b9affd3 --- /dev/null +++ b/database/migrations/2019_11_14_174036_add_hash_key_to_user.php @@ -0,0 +1,34 @@ +string('hash_key', 255)->after('locale'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('user', function (Blueprint $table) { + $table->dropColumn(['hash_key']); + }); + } +} diff --git a/database/migrations/2019_11_15_151514_create_jobs_table.php b/database/migrations/2019_11_15_151514_create_jobs_table.php new file mode 100644 index 0000000..bfe09dc --- /dev/null +++ b/database/migrations/2019_11_15_151514_create_jobs_table.php @@ -0,0 +1,47 @@ +bigIncrements('id'); + $table->string('queue'); + $table->longText('payload'); + $table->tinyInteger('attempts')->unsigned(); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + $table->index(['queue', 'reserved_at']); + }); + + Schema::create('failed_jobs', function (Blueprint $table) { + $table->increments('id'); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('jobs'); + Schema::dropIfExists('failed_jobs'); + } +} diff --git a/database/migrations/2019_12_18_104857_create_site_config_table.php b/database/migrations/2019_12_18_104857_create_site_config_table.php new file mode 100644 index 0000000..757f020 --- /dev/null +++ b/database/migrations/2019_12_18_104857_create_site_config_table.php @@ -0,0 +1,45 @@ +increments('id'); + $table->string('config_key',64); + $table->text('config_value_json') ; + $table->string('key_comment',64)->nullable(); + $table->tinyInteger('status'); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + + Schema::table('site_config', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['config_key'],'config_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('site_config'); + } +} diff --git a/database/migrations/2019_12_24_101312_create_property_room_type_table.php b/database/migrations/2019_12_24_101312_create_property_room_type_table.php new file mode 100644 index 0000000..e0b00d6 --- /dev/null +++ b/database/migrations/2019_12_24_101312_create_property_room_type_table.php @@ -0,0 +1,68 @@ +increments('id'); + $table->string('name',255); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + + Schema::create('property_room_type_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('room_type_id'); + $table->string('name',255); + $table->string('locale',2); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + + /*************** + * Table Relations + ****************/ + Schema::table('property_room_type',function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::table('property_room_type_locale',function (Blueprint $table) { + $table->foreign('room_type_id')->references('id')->on('property_room_type'); + $table->foreign('locale')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['room_type_id','locale'],'prt_locale_unique'); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_type_locale'); + Schema::dropIfExists('property_room_type'); + } +} diff --git a/database/migrations/2019_12_24_162318_create_property_room_table.php b/database/migrations/2019_12_24_162318_create_property_room_table.php new file mode 100644 index 0000000..042e569 --- /dev/null +++ b/database/migrations/2019_12_24_162318_create_property_room_table.php @@ -0,0 +1,50 @@ +bigIncrements('id'); + $table->unsignedInteger('property_id'); + $table->string('name', 200); + $table->mediumText('description')->nullable(); + $table->unsignedInteger('room_type_id'); + $table->integer('max_occupancy'); + $table->integer('max_adult'); + $table->integer('max_child'); + + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('room_type_id')->references('id')->on('property_room_type'); + $table->foreign('property_id')->references('id')->on('property'); + }); + + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room'); + } +} diff --git a/database/migrations/2019_12_25_173749_create_property_room_bed_type_table.php b/database/migrations/2019_12_25_173749_create_property_room_bed_type_table.php new file mode 100644 index 0000000..04fd556 --- /dev/null +++ b/database/migrations/2019_12_25_173749_create_property_room_bed_type_table.php @@ -0,0 +1,58 @@ +increments('id'); + $table->string('name',200); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('property_room_bed_type_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('bed_type_id'); + $table->string('name',255); + $table->string('locale',2); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('bed_type_id')->references('id')->on('property_room_bed_type'); + $table->foreign('locale')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['bed_type_id','locale'],'pbt_locale_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_bed_type_locale'); + Schema::dropIfExists('property_room_bed_type'); + } +} diff --git a/database/migrations/2019_12_26_094857_create_property_room_bed_table.php b/database/migrations/2019_12_26_094857_create_property_room_bed_table.php new file mode 100644 index 0000000..97ae903 --- /dev/null +++ b/database/migrations/2019_12_26_094857_create_property_room_bed_table.php @@ -0,0 +1,45 @@ +bigIncrements('id'); + $table->unsignedBigInteger('room_id'); + $table->tinyInteger('bed_group'); + $table->tinyInteger('count'); + $table->unsignedInteger('bed_type_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('room_id')->references('id')->on('property_room'); + $table->foreign('bed_type_id')->references('id')->on('property_room_bed_type'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_bed'); + } +} diff --git a/database/migrations/2019_12_27_111441_create_property_channel_category_table.php b/database/migrations/2019_12_27_111441_create_property_channel_category_table.php new file mode 100644 index 0000000..eb7855b --- /dev/null +++ b/database/migrations/2019_12_27_111441_create_property_channel_category_table.php @@ -0,0 +1,59 @@ +increments('id'); + $table->string('name',200); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + + Schema::create('property_channel_category_locale', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('channel_category_id'); + $table->string('name',255); + $table->string('locale',2); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('channel_category_id')->references('id')->on('property_channel_category'); + $table->foreign('locale')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_channel_category_locale'); + Schema::dropIfExists('property_channel_category'); + } +} diff --git a/database/migrations/2019_12_27_135558_create_property_channel_table.php b/database/migrations/2019_12_27_135558_create_property_channel_table.php new file mode 100644 index 0000000..a7adc46 --- /dev/null +++ b/database/migrations/2019_12_27_135558_create_property_channel_table.php @@ -0,0 +1,47 @@ +increments('id'); + $table->string('name', 200); + $table->mediumText('description'); + $table->string('type', 5); + $table->unsignedInteger('channel_category_id'); + $table->string('country_code', 5)->nullable(); + $table->string('currency_code', 3); + + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('channel_category_id')->references('id')->on('property_channel_category'); + $table->foreign('currency_code')->references('code')->on('currency'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_channel'); + } +} diff --git a/database/migrations/2019_12_27_171704_create_property_channel_mapping_table.php b/database/migrations/2019_12_27_171704_create_property_channel_mapping_table.php new file mode 100644 index 0000000..2e8db43 --- /dev/null +++ b/database/migrations/2019_12_27_171704_create_property_channel_mapping_table.php @@ -0,0 +1,44 @@ +increments('id'); + $table->unsignedInteger('property_id'); + $table->unsignedInteger('channel_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('channel_id')->references('id')->on('property_channel'); + $table->unique(['property_id', 'channel_id'], 'property_channel_unique'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_channel_mapping'); + } +} diff --git a/database/migrations/2019_12_30_160355_create_property_room_rate_table.php b/database/migrations/2019_12_30_160355_create_property_room_rate_table.php new file mode 100644 index 0000000..f10388b --- /dev/null +++ b/database/migrations/2019_12_30_160355_create_property_room_rate_table.php @@ -0,0 +1,45 @@ +bigIncrements('id'); + $table->unsignedInteger('property_id'); + $table->string('name',200); + $table->mediumText('description'); + $table->tinyInteger('min_stay'); + $table->tinyInteger('is_manually_input'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('property_id')->references('id')->on('property'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_rate'); + } +} diff --git a/database/migrations/2019_12_31_104604_create_property_room_rate_mapping_table.php b/database/migrations/2019_12_31_104604_create_property_room_rate_mapping_table.php new file mode 100644 index 0000000..0ddea8e --- /dev/null +++ b/database/migrations/2019_12_31_104604_create_property_room_rate_mapping_table.php @@ -0,0 +1,50 @@ +bigIncrements('id'); + $table->unsignedBigInteger('room_id'); + $table->unsignedBigInteger('room_rate_id'); + $table->mediumText('description'); + $table->double('rack_rate',10,2); + $table->tinyInteger('included_occupancy'); + $table->tinyInteger('independent_availability'); + $table->tinyInteger('default_stop_sell'); + $table->tinyInteger('booking_on_request'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('room_id')->references('id')->on('property_room'); + $table->foreign('room_rate_id')->references('id')->on('property_room_rate'); + $table->unique(['room_id', 'room_rate_id'], 'room_room_rate_mapping'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_rate_mapping'); + } +} diff --git a/database/migrations/2019_12_31_134435_create_property_room_rate_inclusion_mapping_table.php b/database/migrations/2019_12_31_134435_create_property_room_rate_inclusion_mapping_table.php new file mode 100644 index 0000000..d39266e --- /dev/null +++ b/database/migrations/2019_12_31_134435_create_property_room_rate_inclusion_mapping_table.php @@ -0,0 +1,43 @@ +bigIncrements('id'); + $table->unsignedBigInteger('room_rate_id'); + $table->unsignedInteger('fact_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('room_rate_id')->references('id')->on('property_room_rate'); + $table->foreign('fact_id')->references('id')->on('property_fact'); + $table->unique(['room_rate_id', 'fact_id'], 'room_rate_fact_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_rate_inclusion_mapping'); + } +} diff --git a/database/migrations/2020_01_02_110521_create_property_room_rate_channel_mapping_table.php b/database/migrations/2020_01_02_110521_create_property_room_rate_channel_mapping_table.php new file mode 100644 index 0000000..3df0e4a --- /dev/null +++ b/database/migrations/2020_01_02_110521_create_property_room_rate_channel_mapping_table.php @@ -0,0 +1,44 @@ +bigIncrements('id'); + $table->unsignedInteger('channel_id'); + $table->unsignedBigInteger('room_rate_mapping_id'); + + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('channel_id')->references('id')->on('property_channel'); + $table->foreign('room_rate_mapping_id')->references('id')->on('property_room_rate_mapping'); + $table->unique(['channel_id', 'room_rate_mapping_id'], 'room_rate_channel_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_rate_channel_mapping'); + } +} diff --git a/database/migrations/2020_01_02_140905_create_property_room_rate_mapping_setup_table.php b/database/migrations/2020_01_02_140905_create_property_room_rate_mapping_setup_table.php new file mode 100644 index 0000000..a8ebedf --- /dev/null +++ b/database/migrations/2020_01_02_140905_create_property_room_rate_mapping_setup_table.php @@ -0,0 +1,47 @@ +bigIncrements('id'); + $table->unsignedBigInteger('room_rate_mapping_id'); + $table->string('setup_type', 100)->comment('EXT_ADULT, EXT_CHILD, DIS_GUEST'); + $table->string('value_type', 100)->comment('FIX, PER'); + $table->double('value', 10,2); + + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('room_rate_mapping_id')->references('id')->on('property_room_rate_mapping'); + $table->unique(['room_rate_mapping_id', 'setup_type'], 'room_rate_setup_unique'); + + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_rate_mapping_setup'); + } +} diff --git a/database/migrations/2020_01_02_170939_create_property_room_rate_price_table.php b/database/migrations/2020_01_02_170939_create_property_room_rate_price_table.php new file mode 100644 index 0000000..d1dd231 --- /dev/null +++ b/database/migrations/2020_01_02_170939_create_property_room_rate_price_table.php @@ -0,0 +1,57 @@ +bigIncrements('id'); + $table->unsignedInteger('property_id'); + $table->unsignedBigInteger('property_room_id'); + $table->unsignedBigInteger('room_rate_mapping_id'); + $table->unsignedBigInteger('offer_id')->nullable(); + $table->unsignedInteger('channel_id')->nullable(); + $table->tinyInteger('min_stay'); + $table->tinyInteger('max_stay'); + $table->tinyInteger('stop_sell'); + $table->tinyInteger('booking_on_request'); + $table->date('date'); + $table->double('amount',10,2); + $table->string('currency',3); + + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('property_room_id')->references('id')->on('property_room'); + $table->foreign('room_rate_mapping_id')->references('id')->on('property_room_rate_mapping'); + $table->foreign('channel_id')->references('id')->on('property_channel'); + $table->foreign('currency')->references('code')->on('currency'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_rate_price'); + } +} diff --git a/database/migrations/2020_01_03_124014_create_property_room_availability_table.php b/database/migrations/2020_01_03_124014_create_property_room_availability_table.php new file mode 100644 index 0000000..87516c1 --- /dev/null +++ b/database/migrations/2020_01_03_124014_create_property_room_availability_table.php @@ -0,0 +1,47 @@ +bigIncrements('id'); + $table->unsignedInteger('property_id'); + $table->unsignedBigInteger('property_room_id'); + $table->unsignedBigInteger('room_rate_mapping_id')->nullable(); + $table->unsignedBigInteger('offer_id')->nullable(); + $table->date('date'); + $table->integer('availability')->nullable(); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('property_room_id')->references('id')->on('property_room'); + $table->foreign('room_rate_mapping_id')->references('id')->on('property_room_rate_mapping'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_availability'); + } +} diff --git a/database/migrations/2020_01_07_122454_add_max_stay_to_property_room_rate.php b/database/migrations/2020_01_07_122454_add_max_stay_to_property_room_rate.php new file mode 100644 index 0000000..8387da6 --- /dev/null +++ b/database/migrations/2020_01_07_122454_add_max_stay_to_property_room_rate.php @@ -0,0 +1,32 @@ +tinyInteger('max_stay')->after('min_stay'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property_room_rate', function (Blueprint $table) { + $table->dropColumn(['max_stay']); + }); + } +} diff --git a/database/migrations/2020_01_15_165436_add_image_to_property_channel.php b/database/migrations/2020_01_15_165436_add_image_to_property_channel.php new file mode 100644 index 0000000..ce57550 --- /dev/null +++ b/database/migrations/2020_01_15_165436_add_image_to_property_channel.php @@ -0,0 +1,32 @@ +string('image', 255)->after('currency_code')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property_channel', function (Blueprint $table) { + $table->dropColumn(['image']); + }); + } +} diff --git a/database/migrations/2020_02_13_145243_drop_unique_to_property_executive.php b/database/migrations/2020_02_13_145243_drop_unique_to_property_executive.php new file mode 100644 index 0000000..5e14c0e --- /dev/null +++ b/database/migrations/2020_02_13_145243_drop_unique_to_property_executive.php @@ -0,0 +1,44 @@ +dropForeign('property_executive_ibfk_1'); + $table->dropForeign('property_executive_ibfk_2'); + $table->dropForeign('property_executive_ibfk_3'); + $table->dropForeign('property_executive_ibfk_4'); + $table->dropIndex('property_executive_updated_by_foreign'); + $table->dropIndex('property_executive_created_by_foreign'); + $table->dropIndex('property_executive_executive_type_id_foreign'); + $table->dropIndex('property_executive_property_id_executive_type_id_unique'); + }); + + Schema::table('property_executive',function (Blueprint $table) { + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('executive_type_id')->references('id')->on('property_executive_type'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + +} diff --git a/database/migrations/2020_02_18_113758_add_mobile2_to_property_contact.php b/database/migrations/2020_02_18_113758_add_mobile2_to_property_contact.php new file mode 100644 index 0000000..70b2599 --- /dev/null +++ b/database/migrations/2020_02_18_113758_add_mobile2_to_property_contact.php @@ -0,0 +1,36 @@ +string('mobile2_code',10)->after('mobile')->nullable(); + $table->string('mobile2',50)->after('mobile2_code')->nullable(); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property_contact', function (Blueprint $table) { + $table->dropColumn('mobile2_code'); + $table->dropColumn('mobile2'); + + }); + } +} diff --git a/database/migrations/2020_02_18_153248_add_social_media_addresses_to_property_contact.php b/database/migrations/2020_02_18_153248_add_social_media_addresses_to_property_contact.php new file mode 100644 index 0000000..27393b8 --- /dev/null +++ b/database/migrations/2020_02_18_153248_add_social_media_addresses_to_property_contact.php @@ -0,0 +1,32 @@ +string('social_media_addresses',512)->after('web')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property_contact', function (Blueprint $table) { + $table->dropColumn('social_media_addresses'); + }); + } +} diff --git a/database/migrations/2020_05_18_154634_create_offer_table.php b/database/migrations/2020_05_18_154634_create_offer_table.php new file mode 100644 index 0000000..e2cec8a --- /dev/null +++ b/database/migrations/2020_05_18_154634_create_offer_table.php @@ -0,0 +1,236 @@ +bigIncrements('id'); + $table->unsignedInteger('property_id'); + $table->string('title',200); + $table->mediumText('description')->nullable(); + $table->date('expire_date'); + $table->string('language',2); + $table->string('currency',3); + $table->double('total',10,2)->nullable(); + $table->integer('payment_type_id')->nullable(); // TODO İlişki Kurulmadı + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('offer', function (Blueprint $table) { + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + + + Schema::create('offer_accommodation_mapping', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('offer_id'); + $table->unsignedInteger('property_accommodation_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->dateTime('created_at'); + $table->dateTime('updated_at'); + }); + + Schema::table('offer_accommodation_mapping', function (Blueprint $table) { + $table->foreign('offer_id')->references('id')->on('offer'); + $table->foreign('property_accommodation_id')->references('id')->on('property_fact'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('offer_cancellation_policy', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('offer_id'); + $table->date('start_date'); + $table->date('end_date'); + $table->integer('days_before'); + $table->string('type',3); + $table->double('value',10,2); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('offer_cancellation_policy', function (Blueprint $table) { + $table->foreign('offer_id')->references('id')->on('offer'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('offer_contact_mapping', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('offer_id'); + $table->unsignedInteger('property_executive_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('offer_contact_mapping', function (Blueprint $table) { + $table->foreign('offer_id')->references('id')->on('offer'); + $table->foreign('property_executive_id')->references('id')->on('property_executive'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('offer_fact_mapping', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('offer_id'); + $table->unsignedInteger('category_id'); + $table->unsignedInteger('property_fact_parent_id'); + $table->unsignedInteger('property_fact_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('offer_fact_mapping', function (Blueprint $table) { + $table->foreign('offer_id')->references('id')->on('offer'); + $table->foreign('category_id')->references('id')->on('property_fact'); + $table->foreign('property_fact_parent_id')->references('parent_id')->on('property_fact'); + $table->foreign('property_fact_id')->references('id')->on('property_fact'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('offer_important_notes', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('offer_id'); + $table->mediumText('note')->nullable(); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('offer_important_notes', function (Blueprint $table) { + $table->foreign('offer_id')->references('id')->on('offer'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('offer_payment_type', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->string('name',200); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('offer_payment_type', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('offer_photo_mapping', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('offer_id'); + $table->unsignedInteger('property_photo_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('offer_photo_mapping', function (Blueprint $table) { + $table->foreign('offer_id')->references('id')->on('offer'); + $table->foreign('property_photo_id')->references('id')->on('property_photo'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('offer_price', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('offer_id'); + $table->date('date'); + $table->unsignedBigInteger('property_room_id'); + $table->unsignedInteger('property_accommodation_id'); + $table->unsignedInteger('number_of_rooms'); + $table->string('currency',3); + $table->double('amount',10,2); + $table->double('total_amount',10,2); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('offer_price', function (Blueprint $table) { + $table->foreign('offer_id')->references('id')->on('offer'); + $table->foreign('property_room_id')->references('id')->on('property_room'); + $table->foreign('property_accommodation_id')->references('id')->on('property_fact'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::create('offer_room_mapping', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('offer_id'); + $table->unsignedBigInteger('property_room_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('offer_room_mapping', function (Blueprint $table) { + $table->foreign('offer_id')->references('id')->on('offer'); + $table->foreign('property_room_id')->references('id')->on('property_room'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + Schema::dropIfExists('offer_room_mapping'); + Schema::dropIfExists('offer_price'); + Schema::dropIfExists('offer_photo_mapping'); + Schema::dropIfExists('offer_payment_type'); + Schema::dropIfExists('offer_important_notes'); + Schema::dropIfExists('offer_fact_mapping'); + Schema::dropIfExists('offer_contact_mapping'); + Schema::dropIfExists('offer_cancellation_policy'); + Schema::dropIfExists('offer_accommodation_mapping'); + Schema::dropIfExists('offer'); + + } +} diff --git a/database/migrations/2020_05_27_174534_create_property_room_fact_mapping_table.php b/database/migrations/2020_05_27_174534_create_property_room_fact_mapping_table.php new file mode 100644 index 0000000..336612e --- /dev/null +++ b/database/migrations/2020_05_27_174534_create_property_room_fact_mapping_table.php @@ -0,0 +1,43 @@ +bigIncrements('id'); + $table->integer('property_id')->unsigned(); + $table->bigInteger('room_id')->unsigned(); + $table->integer('fact_id')->unsigned(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('room_id')->references('id')->on('property_room'); + $table->foreign('fact_id')->references('id')->on('property_fact'); + $table->unique(['property_id', 'fact_id','room_id'],'pfr_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_fact_mapping'); + } +} diff --git a/database/migrations/2020_05_28_123105_create_property_room_photo_mapping_table.php b/database/migrations/2020_05_28_123105_create_property_room_photo_mapping_table.php new file mode 100644 index 0000000..47e43ac --- /dev/null +++ b/database/migrations/2020_05_28_123105_create_property_room_photo_mapping_table.php @@ -0,0 +1,48 @@ +statement("ALTER TABLE `offer_photo_mapping`DROP FOREIGN KEY `offer_photo_mapping_property_photo_id_foreign`;"); + DB::connection()->statement("ALTER TABLE `property_photo` MODIFY COLUMN `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT FIRST"); + DB::connection()->statement("ALTER TABLE `offer_photo_mapping` MODIFY COLUMN `property_photo_id` bigint(20) UNSIGNED NOT NULL"); + DB::connection()->statement("ALTER TABLE `offer_photo_mapping` ADD CONSTRAINT `offer_photo_mapping_property_photo_id_foreign` FOREIGN KEY (`property_photo_id`) REFERENCES `property_photo` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT"); + + Schema::create('property_room_photo_mapping', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->integer('property_id')->unsigned(); + $table->bigInteger('room_id')->unsigned(); + $table->bigInteger('photo_id')->unsigned(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('room_id')->references('id')->on('property_room'); + $table->foreign('photo_id')->references('id')->on('property_photo'); + $table->unique(['property_id', 'photo_id', 'room_id'],'ppr_unique'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_photo_mapping'); + } +} diff --git a/database/migrations/2020_05_28_203313_create_property_brand_table.php b/database/migrations/2020_05_28_203313_create_property_brand_table.php new file mode 100644 index 0000000..bec3431 --- /dev/null +++ b/database/migrations/2020_05_28_203313_create_property_brand_table.php @@ -0,0 +1,48 @@ +increments('id'); + $table->integer('property_id')->unsigned(); + $table->string('title', 250); + $table->string('color_code_1', 50); + $table->string('color_code_2', 50); + $table->string('color_code_3', 50); + $table->string('logo_path'); + $table->string('logo_name', 200); + + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + $table->unique(['property_id'],'property_unique'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_brand'); + } +} diff --git a/database/migrations/2020_06_01_201830_change_offer_accommodation_mapping_date_format.php b/database/migrations/2020_06_01_201830_change_offer_accommodation_mapping_date_format.php new file mode 100644 index 0000000..4e4d4fe --- /dev/null +++ b/database/migrations/2020_06_01_201830_change_offer_accommodation_mapping_date_format.php @@ -0,0 +1,44 @@ +bigIncrements('id'); + $table->unsignedBigInteger('offer_id'); + $table->unsignedInteger('property_accommodation_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + Schema::table('offer_accommodation_mapping', function (Blueprint $table) { + $table->foreign('offer_id')->references('id')->on('offer'); + $table->foreign('property_accommodation_id')->references('id')->on('property_fact'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_06_05_114115_add_column_offer_table_offer_code.php b/database/migrations/2020_06_05_114115_add_column_offer_table_offer_code.php new file mode 100644 index 0000000..0f9963d --- /dev/null +++ b/database/migrations/2020_06_05_114115_add_column_offer_table_offer_code.php @@ -0,0 +1,23 @@ +string('offer_code',50)->unique()->nullable(); + }); + } + + + public function down() + { + Schema::table('offer', function (Blueprint $table) { + $table->dropColumn('offer_code'); + }); + } +} diff --git a/database/migrations/2020_06_05_162856_add_column_offer_table_accept_status_column.php b/database/migrations/2020_06_05_162856_add_column_offer_table_accept_status_column.php new file mode 100644 index 0000000..cd4a31a --- /dev/null +++ b/database/migrations/2020_06_05_162856_add_column_offer_table_accept_status_column.php @@ -0,0 +1,24 @@ +tinyInteger('accept_status')->after('status')->default(0)->comment(' 0 pending, 1 accepted, 2 canceled'); + + }); + } + + + public function down() + { + Schema::table('offer', function (Blueprint $table) { + $table->dropColumn('accept_status'); + }); + } +} diff --git a/database/migrations/2020_06_09_095128_add_is_cover_photo_to_offer_photo_mapping.php b/database/migrations/2020_06_09_095128_add_is_cover_photo_to_offer_photo_mapping.php new file mode 100644 index 0000000..858283a --- /dev/null +++ b/database/migrations/2020_06_09_095128_add_is_cover_photo_to_offer_photo_mapping.php @@ -0,0 +1,34 @@ +tinyInteger('is_cover')->after('property_photo_id')->default(0)->comment(' 0 false, 1 true (use for cover photo)'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('offer_photo_mapping', function (Blueprint $table) { + $table->dropColumn('is_cover'); + + }); + } +} diff --git a/database/migrations/2020_06_09_135659_change_offer_table_status_and_accept_status_fields.php b/database/migrations/2020_06_09_135659_change_offer_table_status_and_accept_status_fields.php new file mode 100644 index 0000000..e611928 --- /dev/null +++ b/database/migrations/2020_06_09_135659_change_offer_table_status_and_accept_status_fields.php @@ -0,0 +1,31 @@ +where('id',1)->update(['icon' => '']); // General Amenities + DB::table('property_fact')->where('id',257)->update(['icon' => 'fas fa-hot-tub']); // Spa Wellness + DB::table('property_fact')->where('id',2)->update(['icon' => '']); // Payment Type + DB::table('property_fact')->where('id',258)->update(['icon' => 'fas fa-spa']); // Spa, Wellness & Care + DB::table('property_fact')->where('id',9)->update(['icon' => '']); // Accommodation Type + DB::table('property_fact')->where('id',10)->update(['icon' => 'fas fa-hotel']); // All inclusive + DB::table('property_fact')->where('id',16)->update(['icon' => '']); // Accommodation Services + DB::table('property_fact')->where('id',17)->update(['icon' => 'fas fa-concierge-bell']); // Reception + DB::table('property_fact')->where('id',26)->update(['icon' => 'fas fa-tv']); // Internet and TV + DB::table('property_fact')->where('id',27)->update(['icon' => 'fas fa-wifi']); // Wireless Internet + DB::table('property_fact')->where('id',30)->update(['icon' => '']); // Working Area + DB::table('property_fact')->where('id',31)->update(['icon' => 'fas fa-briefcase']); // Business Center + DB::table('property_fact')->where('id',292)->update(['icon' => '']); // Room Amenities + DB::table('property_fact')->where('id',37)->update(['icon' => 'fas fa-shuttle-van']); // Transportation Services + DB::table('property_fact')->where('id',293)->update(['icon' => '']); // Common Amenities of Rooms + DB::table('property_fact')->where('id',38)->update(['icon' => 'fas fa-car']); // Rent a car + DB::table('property_fact')->where('id',44)->update(['icon' => '']); // Facility Amenities + DB::table('property_fact')->where('id',45)->update(['icon' => '']); // Activities + DB::table('property_fact')->where('id',114)->update(['icon' => '']); // Entertainment on Facility + DB::table('property_fact')->where('id',115)->update(['icon' => 'fas fa-sun']); // Outdoor Activities + DB::table('property_fact')->where('id',142)->update(['icon' => 'fas fa-swimmer']); // Pool + DB::table('property_fact')->where('id',143)->update(['icon' => 'fas fa-swimming-pool']); // Aqua Park + DB::table('property_fact')->where('id',154)->update(['icon' => 'fas fa-utensils']); // Food and Beverage, Restaurant + DB::table('property_fact')->where('id',182)->update(['icon' => '']); // Public Areas and Facilitiesa + DB::table('property_fact')->where('id',183)->update(['icon' => 'fas fa-arrows-alt-v']); // Elevator + DB::table('property_fact')->where('id',222)->update(['icon' => 'fas fa-wheelchair']); // Children and Disabled + DB::table('property_fact')->where('id',223)->update(['icon' => 'fas fa-baby']); // Baby and Child + + DB::table('property_fact')->where('id',27)->update(['icon' => 'fas fa-wifi']); + DB::table('property_fact')->where('id',64)->update(['icon' => 'fas fa-bullseye']); + DB::table('property_fact')->where('id',66)->update(['icon' => 'fas fa-golf-ball']); + DB::table('property_fact')->where('id',65)->update(['icon' => 'fas fa-ship']); + DB::table('property_fact')->where('id',158)->update(['icon' => 'fas fa-glass-martini']); + DB::table('property_fact')->where('id',156)->update(['icon' => 'fas fa-beer']); + DB::table('property_fact')->where('id',166)->update(['icon' => 'fas fa-wine-bottle']); + DB::table('property_fact')->where('id',160)->update(['icon' => 'fas fa-glass-whiskey']); + DB::table('property_fact')->where('id',162)->update(['icon' => 'fas fa-cocktail']); + DB::table('property_fact')->where('id',148)->update(['icon' => 'fas fa-umbrella-beach']); + DB::table('property_fact')->where('id',226)->update(['icon' => 'fas fa-child']); + DB::table('property_fact')->where('id',227)->update(['icon' => 'fas fa-swimming-pool']); + DB::table('property_fact')->where('id',303)->update(['icon' => 'fas fa-border-none']); + DB::table('property_fact')->where('id',320)->update(['icon' => 'fas fa-washer']); + DB::table('property_fact')->where('id',313)->update(['icon' => 'fas fa-tshirt']); + DB::table('property_fact')->where('id',346)->update(['icon' => 'fas fa-border-all']); + DB::table('property_fact')->where('id',335)->update(['icon' => 'fas fa-bolt']); + DB::table('property_fact')->where('id',372)->update(['icon' => 'fas fa-swimming-pool']); + DB::table('property_fact')->where('id',186)->update(['name' => 'Coffee / Tea Maker']); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_06_16_093408_drop_column_from_offer_cancellation_policy.php b/database/migrations/2020_06_16_093408_drop_column_from_offer_cancellation_policy.php new file mode 100644 index 0000000..f457403 --- /dev/null +++ b/database/migrations/2020_06_16_093408_drop_column_from_offer_cancellation_policy.php @@ -0,0 +1,34 @@ +dropColumn('start_date'); + $table->dropColumn('end_date'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('offer_cancellation_policy', function (Blueprint $table) { + $table->date('start_date')->default('2020-01-01'); + $table->date('end_date')->default('2020-01-01');; + }); + } +} diff --git a/database/migrations/2020_06_16_171417_update_fact_order.php b/database/migrations/2020_06_16_171417_update_fact_order.php new file mode 100644 index 0000000..ce8f484 --- /dev/null +++ b/database/migrations/2020_06_16_171417_update_fact_order.php @@ -0,0 +1,46 @@ +statement("UPDATE property_fact SET order_number = 1 WHERE id = 9"); + DB::connection()->statement("UPDATE property_fact SET order_number = 2 WHERE id = 16"); + DB::connection()->statement("UPDATE property_fact SET order_number = 3 WHERE id = 26"); + DB::connection()->statement("UPDATE property_fact SET order_number = 4 WHERE id = 30 "); + DB::connection()->statement("UPDATE property_fact SET order_number = 5 WHERE id = 37 "); + DB::connection()->statement("UPDATE property_fact SET order_number = 6 WHERE id = 243"); + DB::connection()->statement("UPDATE property_fact SET order_number = 7 WHERE id = 2 "); + + // facility + DB::connection()->statement("UPDATE property_fact SET order_number = 1 WHERE id = 45 "); + DB::connection()->statement("UPDATE property_fact SET order_number = 2 WHERE id = 154 "); + DB::connection()->statement("UPDATE property_fact SET order_number = 3 WHERE id = 114 "); + DB::connection()->statement("UPDATE property_fact SET order_number = 4 WHERE id = 142"); + DB::connection()->statement("UPDATE property_fact SET order_number = 5 WHERE id = 257 "); + DB::connection()->statement("UPDATE property_fact SET order_number = 6 WHERE id = 182 "); + DB::connection()->statement("UPDATE property_fact SET order_number = 7 WHERE id = 223"); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_06_17_145049_update_fact_parent.php b/database/migrations/2020_06_17_145049_update_fact_parent.php new file mode 100644 index 0000000..b668516 --- /dev/null +++ b/database/migrations/2020_06_17_145049_update_fact_parent.php @@ -0,0 +1,46 @@ +statement("UPDATE property_fact SET parent_id = 44 WHERE id = 223"); + DB::connection()->statement("UPDATE property_fact SET parent_id = 1 WHERE id = 243"); + DB::connection()->statement("UPDATE property_fact SET type = 0 WHERE id = 243"); + DB::connection()->statement("UPDATE property_fact SET parent_id = 44 WHERE id = 257"); + DB::connection()->statement("DELETE FROM offer_fact_mapping WHERE property_fact_id = 222"); + DB::connection()->statement("DELETE FROM offer_fact_mapping WHERE property_fact_parent_id = 222"); + DB::connection()->statement("DELETE FROM offer_fact_mapping WHERE category_id = 222"); + DB::connection()->statement("DELETE FROM property_fact_attribute_mapping WHERE fact_id = 222"); + DB::connection()->statement("DELETE FROM property_fact_mapping WHERE fact_id = 222"); + DB::connection()->statement("DELETE FROM property_room_fact_mapping WHERE fact_id = 222"); + DB::connection()->statement("DELETE FROM property_fact_locale WHERE fact_id = 222"); + DB::connection()->statement("DELETE FROM property_fact WHERE id = 222"); + +$updateData = <<where('id',1)->update(['config_value_json' => $updateData]); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_06_17_150913_create_country_table.php b/database/migrations/2020_06_17_150913_create_country_table.php new file mode 100644 index 0000000..a0e161d --- /dev/null +++ b/database/migrations/2020_06_17_150913_create_country_table.php @@ -0,0 +1,37 @@ +increments('id'); + $table->string('name', 100)->unique(); + $table->string('country_code',5)->unique(); + $table->string('phone_code',10)->nullable(); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('country'); + } +} diff --git a/database/migrations/2020_06_18_103740_update_property_fact_data.php b/database/migrations/2020_06_18_103740_update_property_fact_data.php new file mode 100644 index 0000000..d19bdbf --- /dev/null +++ b/database/migrations/2020_06_18_103740_update_property_fact_data.php @@ -0,0 +1,36 @@ +statement("UPDATE property_fact SET parent_id = 44, order_number = 5 WHERE id = 258"); + DB::connection()->statement("DELETE FROM offer_fact_mapping WHERE property_fact_id = 257"); + DB::connection()->statement("DELETE FROM offer_fact_mapping WHERE property_fact_parent_id = 257"); + DB::connection()->statement("DELETE FROM offer_fact_mapping WHERE category_id = 257"); + DB::connection()->statement("DELETE FROM property_fact_attribute_mapping WHERE fact_id = 257"); + DB::connection()->statement("DELETE FROM property_fact_mapping WHERE fact_id = 257"); + DB::connection()->statement("DELETE FROM property_room_fact_mapping WHERE fact_id = 257"); + DB::connection()->statement("DELETE FROM property_fact_locale WHERE fact_id = 257"); + DB::connection()->statement("DELETE FROM property_fact WHERE id = 257"); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_06_18_132804_country_table_add_data.php b/database/migrations/2020_06_18_132804_country_table_add_data.php new file mode 100644 index 0000000..6b7ba81 --- /dev/null +++ b/database/migrations/2020_06_18_132804_country_table_add_data.php @@ -0,0 +1,272 @@ +statement("INSERT INTO `country` VALUES (1, 'Aruba', 'AW', '297', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (2, 'Afghanistan', 'AF', '93', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (3, 'Angola', 'AO', '244', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (4, 'Anguilla', 'AI', '1264', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (5, 'Ãland Islands', 'AX', '358', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (6, 'Albania', 'AL', '355', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (7, 'Andorra', 'AD', '376', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (8, 'United Arab Emirates', 'AE', '971', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (9, 'Argentina', 'AR', '54', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (10, 'Armenia', 'AM', '374', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (11, 'American Samoa', 'AS', '1684', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (12, 'Antigua and Barbuda', 'AG', '1268', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (13, 'Australia', 'AU', '61', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (14, 'Austria', 'AT', '43', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (15, 'Azerbaijan', 'AZ', '994', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (16, 'Burundi', 'BI', '257', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (17, 'Belgium', 'BE', '32', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (18, 'Benin', 'BJ', '229', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (19, 'Burkina Faso', 'BF', '226', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (20, 'Bangladesh', 'BD', '880', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (21, 'Bulgaria', 'BG', '359', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (22, 'Bahrain', 'BH', '973', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (23, 'Bahamas', 'BS', '1242', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (24, 'Bosnia and Herzegovina', 'BA', '387', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (25, 'Saint Barthélemy', 'BL', '590', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (26, 'Belarus', 'BY', '375', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (27, 'Belize', 'BZ', '501', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (28, 'Bermuda', 'BM', '1441', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (29, 'Bolivia', 'BO', '591', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (30, 'Brazil', 'BR', '55', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (31, 'Barbados', 'BB', '1246', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (32, 'Brunei', 'BN', '673', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (33, 'Bhutan', 'BT', '975', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (34, 'Botswana', 'BW', '267', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (35, 'Central African Republic', 'CF', '236', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (36, 'Canada', 'CA', '1', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (37, 'Cocos (Keeling) Islands', 'CC', '61', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (38, 'Switzerland', 'CH', '41', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (39, 'Chile', 'CL', '56', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (40, 'China', 'CN', '86', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (41, 'Ivory Coast', 'CI', '225', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (42, 'Cameroon', 'CM', '237', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (43, 'DR Congo', 'CD', '243', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (44, 'Republic of the Congo', 'CG', '242', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (45, 'Cook Islands', 'CK', '682', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (46, 'Colombia', 'CO', '57', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (47, 'Comoros', 'KM', '269', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (48, 'Cape Verde', 'CV', '238', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (49, 'Costa Rica', 'CR', '506', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (50, 'Cuba', 'CU', '53', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (51, 'Curaçao', 'CW', '5999', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (52, 'Christmas Island', 'CX', '61', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (53, 'Cayman Islands', 'KY', '1345', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (54, 'Cyprus', 'CY', '357', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (55, 'Czechia', 'CZ', '420', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (56, 'Germany', 'DE', '49', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (57, 'Djibouti', 'DJ', '253', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (58, 'Dominica', 'DM', '1767', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (59, 'Denmark', 'DK', '45', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (60, 'Dominican Republic', 'DO', '1809', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (61, 'Algeria', 'DZ', '213', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (62, 'Ecuador', 'EC', '593', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (63, 'Egypt', 'EG', '20', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (64, 'Eritrea', 'ER', '291', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (65, 'Western Sahara', 'EH', '212', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (66, 'Spain', 'ES', '34', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (67, 'Estonia', 'EE', '372', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (68, 'Ethiopia', 'ET', '251', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (69, 'Finland', 'FI', '358', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (70, 'Fiji', 'FJ', '679', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (71, 'Falkland Islands', 'FK', '500', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (72, 'France', 'FR', '33', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (73, 'Faroe Islands', 'FO', '298', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (74, 'Micronesia', 'FM', '691', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (75, 'Gabon', 'GA', '241', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (76, 'United Kingdom', 'GB', '44', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (77, 'Georgia', 'GE', '995', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (78, 'Guernsey', 'GG', '44', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (79, 'Ghana', 'GH', '233', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (80, 'Gibraltar', 'GI', '350', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (81, 'Guinea', 'GN', '224', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (82, 'Guadeloupe', 'GP', '590', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (83, 'Gambia', 'GM', '220', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (84, 'Guinea-Bissau', 'GW', '245', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (85, 'Equatorial Guinea', 'GQ', '240', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (86, 'Greece', 'GR', '30', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (87, 'Grenada', 'GD', '1473', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (88, 'Greenland', 'GL', '299', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (89, 'Guatemala', 'GT', '502', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (90, 'French Guiana', 'GF', '594', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (91, 'Guam', 'GU', '1671', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (92, 'Guyana', 'GY', '592', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (93, 'Hong Kong', 'HK', '852', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (94, 'Honduras', 'HN', '504', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (95, 'Croatia', 'HR', '385', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (96, 'Haiti', 'HT', '509', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (97, 'Hungary', 'HU', '36', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (98, 'Indonesia', 'ID', '62', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (99, 'Isle of Man', 'IM', '44', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (100, 'India', 'IN', '91', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (101, 'British Indian Ocean Territory', 'IO', '246', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (102, 'Ireland', 'IE', '353', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (103, 'Iran', 'IR', '98', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (104, 'Iraq', 'IQ', '964', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (105, 'Iceland', 'IS', '354', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (106, 'Israel', 'IL', '972', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (107, 'Italy', 'IT', '39', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (108, 'Jamaica', 'JM', '1876', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (109, 'Jersey', 'JE', '44', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (110, 'Jordan', 'JO', '962', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (111, 'Japan', 'JP', '81', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (112, 'Kazakhstan', 'KZ', '77', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (113, 'Kenya', 'KE', '254', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (114, 'Kyrgyzstan', 'KG', '996', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (115, 'Cambodia', 'KH', '855', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (116, 'Kiribati', 'KI', '686', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (117, 'Saint Kitts and Nevis', 'KN', '1869', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (118, 'South Korea', 'KR', '82', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (119, 'Kosovo', 'XK', '383', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (120, 'Kuwait', 'KW', '965', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (121, 'Laos', 'LA', '856', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (122, 'Lebanon', 'LB', '961', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (123, 'Liberia', 'LR', '231', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (124, 'Libya', 'LY', '218', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (125, 'Saint Lucia', 'LC', '1758', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (126, 'Liechtenstein', 'LI', '423', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (127, 'Sri Lanka', 'LK', '94', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (128, 'Lesotho', 'LS', '266', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (129, 'Lithuania', 'LT', '370', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (130, 'Luxembourg', 'LU', '352', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (131, 'Latvia', 'LV', '371', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (132, 'Macau', 'MO', '853', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (133, 'Saint Martin', 'MF', '590', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (134, 'Morocco', 'MA', '212', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (135, 'Monaco', 'MC', '377', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (136, 'Moldova', 'MD', '373', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (137, 'Madagascar', 'MG', '261', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (138, 'Maldives', 'MV', '960', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (139, 'Mexico', 'MX', '52', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (140, 'Marshall Islands', 'MH', '692', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (141, 'Macedonia', 'MK', '389', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (142, 'Mali', 'ML', '223', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (143, 'Malta', 'MT', '356', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (144, 'Myanmar', 'MM', '95', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (145, 'Montenegro', 'ME', '382', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (146, 'Mongolia', 'MN', '976', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (147, 'Northern Mariana Islands', 'MP', '1670', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (148, 'Mozambique', 'MZ', '258', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (149, 'Mauritania', 'MR', '222', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (150, 'Montserrat', 'MS', '1664', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (151, 'Martinique', 'MQ', '596', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (152, 'Mauritius', 'MU', '230', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (153, 'Malawi', 'MW', '265', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (154, 'Malaysia', 'MY', '60', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (155, 'Mayotte', 'YT', '262', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (156, 'Namibia', 'NA', '264', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (157, 'New Caledonia', 'NC', '687', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (158, 'Niger', 'NE', '227', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (159, 'Norfolk Island', 'NF', '672', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (160, 'Nigeria', 'NG', '234', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (161, 'Nicaragua', 'NI', '505', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (162, 'Niue', 'NU', '683', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (163, 'Netherlands', 'NL', '31', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (164, 'Norway', 'NO', '47', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (165, 'Nepal', 'NP', '977', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (166, 'Nauru', 'NR', '674', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (167, 'New Zealand', 'NZ', '64', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (168, 'Oman', 'OM', '968', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (169, 'Pakistan', 'PK', '92', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (170, 'Panama', 'PA', '507', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (171, 'Pitcairn Islands', 'PN', '64', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (172, 'Peru', 'PE', '51', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (173, 'Philippines', 'PH', '63', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (174, 'Palau', 'PW', '680', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (175, 'Papua New Guinea', 'PG', '675', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (176, 'Poland', 'PL', '48', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (177, 'Puerto Rico', 'PR', '1787', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (178, 'North Korea', 'KP', '850', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (179, 'Portugal', 'PT', '351', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (180, 'Paraguay', 'PY', '595', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (181, 'Palestine', 'PS', '970', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (182, 'French Polynesia', 'PF', '689', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (183, 'Qatar', 'QA', '974', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (184, 'Réunion', 'RE', '262', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (185, 'Romania', 'RO', '40', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (186, 'Russia', 'RU', '7', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (187, 'Rwanda', 'RW', '250', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (188, 'Saudi Arabia', 'SA', '966', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (189, 'Sudan', 'SD', '249', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (190, 'Senegal', 'SN', '221', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (191, 'Singapore', 'SG', '65', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (192, 'South Georgia', 'GS', '500', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (193, 'Svalbard and Jan Mayen', 'SJ', '4779', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (194, 'Solomon Islands', 'SB', '677', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (195, 'Sierra Leone', 'SL', '232', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (196, 'El Salvador', 'SV', '503', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (197, 'San Marino', 'SM', '378', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (198, 'Somalia', 'SO', '252', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (199, 'Saint Pierre and Miquelon', 'PM', '508', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (200, 'Serbia', 'RS', '381', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (201, 'South Sudan', 'SS', '211', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (202, 'São Tomé and Príncipe', 'ST', '239', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (203, 'Suriname', 'SR', '597', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (204, 'Slovakia', 'SK', '421', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (205, 'Slovenia', 'SI', '386', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (206, 'Sweden', 'SE', '46', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (207, 'Swaziland', 'SZ', '268', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (208, 'Sint Maarten', 'SX', '1721', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (209, 'Seychelles', 'SC', '248', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (210, 'Syria', 'SY', '963', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (211, 'Turks and Caicos Islands', 'TC', '1649', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (212, 'Chad', 'TD', '235', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (213, 'Togo', 'TG', '228', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (214, 'Thailand', 'TH', '66', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (215, 'Tajikistan', 'TJ', '992', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (216, 'Tokelau', 'TK', '690', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (217, 'Turkmenistan', 'TM', '993', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (218, 'Timor-Leste', 'TL', '670', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (219, 'Tonga', 'TO', '676', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (220, 'Trinidad and Tobago', 'TT', '1868', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (221, 'Tunisia', 'TN', '216', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (222, 'Turkey', 'TR', '90', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (223, 'Tuvalu', 'TV', '688', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (224, 'Taiwan', 'TW', '886', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (225, 'Tanzania', 'TZ', '255', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (226, 'Uganda', 'UG', '256', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (227, 'Ukraine', 'UA', '380', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (228, 'Uruguay', 'UY', '598', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (229, 'United States', 'US', '1', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (230, 'Uzbekistan', 'UZ', '998', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (231, 'Vatican City', 'VA', '379', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (232, 'Saint Vincent and the Grenadines', 'VC', '1784', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (233, 'Venezuela', 'VE', '58', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (234, 'British Virgin Islands', 'VG', '1284', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (235, 'United States Virgin Islands', 'VI', '1340', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (236, 'Vietnam', 'VN', '84', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (237, 'Vanuatu', 'VU', '678', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (238, 'Wallis and Futuna', 'WF', '681', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (239, 'Samoa', 'WS', '685', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (240, 'Yemen', 'YE', '967', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (241, 'South Africa', 'ZA', '27', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (242, 'Zambia', 'ZM', '260', 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `country` VALUES (243, 'Zimbabwe', 'ZW', '263', 1, 1, 1, 1)"); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + } +} diff --git a/database/migrations/2020_06_18_162035_drop_column_checkin_from_property_table.php b/database/migrations/2020_06_18_162035_drop_column_checkin_from_property_table.php new file mode 100644 index 0000000..56fd915 --- /dev/null +++ b/database/migrations/2020_06_18_162035_drop_column_checkin_from_property_table.php @@ -0,0 +1,60 @@ +dropColumn('checkin_time'); + $table->dropColumn('checkout_time'); + }); + + DB::table('property_additional_info_key')->insert( + [ + [ + 'additional_info_key' => 'checkin_time', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'additional_info_key' => 'checkout_time', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ] + ] + + ); + + DB::connection()->statement("UPDATE property_additional_info_key SET status = 0 WHERE additional_info_key = 'pet' "); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property', function (Blueprint $table) { + $table->string('checkin_time',50)->nullable(); + $table->string('checkout_time',50)->nullable(); + }); + } +} diff --git a/database/migrations/2020_06_18_162612_add_column_property_table_country_field.php b/database/migrations/2020_06_18_162612_add_column_property_table_country_field.php new file mode 100644 index 0000000..a05ab7d --- /dev/null +++ b/database/migrations/2020_06_18_162612_add_column_property_table_country_field.php @@ -0,0 +1,26 @@ +string('country',5)->after('destination_id')->nullable(); + + }); + + Schema::table('property', function (Blueprint $table) { + $table->foreign('country')->references('country_code')->on('country'); + }); + } + + + public function down() + { + + } +} diff --git a/database/migrations/2020_06_18_173416_add_column_country_table_status_field.php b/database/migrations/2020_06_18_173416_add_column_country_table_status_field.php new file mode 100644 index 0000000..dde2fd1 --- /dev/null +++ b/database/migrations/2020_06_18_173416_add_column_country_table_status_field.php @@ -0,0 +1,33 @@ +tinyInteger('status')->after('phone_code')->default(1); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('country', function (Blueprint $table) { + $table->dropColumn('status'); + }); + } +} diff --git a/database/migrations/2020_06_19_093503_change_columns_property_table.php b/database/migrations/2020_06_19_093503_change_columns_property_table.php new file mode 100644 index 0000000..cb690de --- /dev/null +++ b/database/migrations/2020_06_19_093503_change_columns_property_table.php @@ -0,0 +1,38 @@ +string('extension', 10)->after('phone')->nullable(); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property_executive', function (Blueprint $table) { + $table->dropColumn('extension'); + }); + } +} diff --git a/database/migrations/2020_06_19_105603_add_columns_to_user_table.php b/database/migrations/2020_06_19_105603_add_columns_to_user_table.php new file mode 100644 index 0000000..5d6909a --- /dev/null +++ b/database/migrations/2020_06_19_105603_add_columns_to_user_table.php @@ -0,0 +1,35 @@ +string('exclude_occupancy', 500)->after('max_child')->nullable(); + }); + } + + + public function down() + { + Schema::table('property_room', function (Blueprint $table) { + $table->dropColumn('exclude_occupancy'); + }); + } +} diff --git a/database/migrations/2020_06_25_162442_change_columns_property_executive_table.php b/database/migrations/2020_06_25_162442_change_columns_property_executive_table.php new file mode 100644 index 0000000..17ca8ad --- /dev/null +++ b/database/migrations/2020_06_25_162442_change_columns_property_executive_table.php @@ -0,0 +1,39 @@ +tinyInteger('is_application')->after('status')->default(0); + $table->tinyInteger('is_published')->after('is_application')->default(0); + }); + + DB::statement("ALTER TABLE language MODIFY COLUMN code VARCHAR(10) NOT NULL"); + + DB::statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::table('language')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + DB::connection()->statement("INSERT INTO `language` VALUES (1, 'af', 'Afrikaans', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (2, 'af_ZA', 'Afrikaans (South Africa)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (3, 'ar', 'Arabic', 1, 1, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (4, 'ar_AE', 'Arabic (U.A.E.)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (5, 'ar_BH', 'Arabic (Bahrain)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (6, 'ar_DZ', 'Arabic (Algeria)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (7, 'ar_EG', 'Arabic (Egypt)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (8, 'ar_IQ', 'Arabic (Iraq)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (9, 'ar_JO', 'Arabic (Jordan)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (10, 'ar_KW', 'Arabic (Kuwait)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (11, 'ar_LB', 'Arabic (Lebanon)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (12, 'ar_LY', 'Arabic (Libya)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (13, 'ar_MA', 'Arabic (Morocco)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (14, 'ar_OM', 'Arabic (Oman)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (15, 'ar_QA', 'Arabic (Qatar)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (16, 'ar_SA', 'Arabic (Saudi Arabia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (17, 'ar_SY', 'Arabic (Syria)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (18, 'ar_TN', 'Arabic (Tunisia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (19, 'ar_YE', 'Arabic (Yemen)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (20, 'az', 'Azeri (Latin)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (21, 'az_AZ', 'Azeri (Cyrillic) (Azerbaijan)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (22, 'be', 'Belarusian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (23, 'be_BY', 'Belarusian (Belarus)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (24, 'bg', 'Bulgarian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (25, 'bg_BG', 'Bulgarian (Bulgaria)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (26, 'bs_BA', 'Bosnian (Bosnia and Herzegovina)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (27, 'ca', 'Catalan', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (28, 'ca_ES', 'Catalan (Spain)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (29, 'cs', 'Czech', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (30, 'cs_CZ', 'Czech (Czech Republic)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (31, 'cy', 'Welsh', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (32, 'cy_GB', 'Welsh (United Kingdom)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (33, 'da', 'Danish', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (34, 'da_DK', 'Danish (Denmark)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (35, 'de', 'German', 1, 1, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (36, 'de_AT', 'German (Austria)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (37, 'de_CH', 'German (Switzerland)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (38, 'de_DE', 'German (Germany)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (39, 'de_LI', 'German (Liechtenstein)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (40, 'de_LU', 'German (Luxembourg)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (41, 'dv', 'Divehi', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (42, 'dv_MV', 'Divehi (Maldives)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (43, 'el', 'Greek', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (44, 'el_GR', 'Greek (Greece)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (45, 'en', 'English', 1, 1, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (46, 'en_AU', 'English (Australia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (47, 'en_BZ', 'English (Belize)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (48, 'en_CA', 'English (Canada)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (49, 'en_CB', 'English (Caribbean)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (50, 'en_GB', 'English (United Kingdom)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (51, 'en_IE', 'English (Ireland)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (52, 'en_JM', 'English (Jamaica)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (53, 'en_NZ', 'English (New Zealand)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (54, 'en_PH', 'English (Republic of the Philippines)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (55, 'en_TT', 'English (Trinidad and Tobago)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (56, 'en_US', 'English (United States)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (57, 'en_ZA', 'English (South Africa)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (58, 'en_ZW', 'English (Zimbabwe)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (59, 'eo', 'Esperanto', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (60, 'es', 'Spanish', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (61, 'es_AR', 'Spanish (Argentina)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (62, 'es_BO', 'Spanish (Bolivia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (63, 'es_CL', 'Spanish (Chile)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (64, 'es_CO', 'Spanish (Colombia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (65, 'es_CR', 'Spanish (Costa Rica)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (66, 'es_DO', 'Spanish (Dominican Republic)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (67, 'es_EC', 'Spanish (Ecuador)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (68, 'es_ES', 'Spanish (Spain)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (69, 'es_GT', 'Spanish (Guatemala)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (70, 'es_HN', 'Spanish (Honduras)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (71, 'es_MX', 'Spanish (Mexico)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (72, 'es_NI', 'Spanish (Nicaragua)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (73, 'es_PA', 'Spanish (Panama)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (74, 'es_PE', 'Spanish (Peru)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (75, 'es_PR', 'Spanish (Puerto Rico)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (76, 'es_PY', 'Spanish (Paraguay)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (77, 'es_SV', 'Spanish (El Salvador)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (78, 'es_UY', 'Spanish (Uruguay)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (79, 'es_VE', 'Spanish (Venezuela)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (80, 'et', 'Estonian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (81, 'et_EE', 'Estonian (Estonia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (82, 'eu', 'Basque', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (83, 'eu_ES', 'Basque (Spain)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (84, 'fa', 'Farsi', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (85, 'fa_IR', 'Farsi (Iran)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (86, 'fi', 'Finnish', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (87, 'fi_FI', 'Finnish (Finland)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (88, 'fo', 'Faroese', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (89, 'fo_FO', 'Faroese (Faroe Islands)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (90, 'fr', 'French', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (91, 'fr_BE', 'French (Belgium)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (92, 'fr_CA', 'French (Canada)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (93, 'fr_CH', 'French (Switzerland)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (94, 'fr_FR', 'French (France)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (95, 'fr_LU', 'French (Luxembourg)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (96, 'fr_MC', 'French (Principality of Monaco)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (97, 'gl', 'Galician', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (98, 'gl_ES', 'Galician (Spain)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (99, 'gu', 'Gujarati', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (100, 'gu_IN', 'Gujarati (India)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (101, 'he', 'Hebrew', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (102, 'he_IL', 'Hebrew (Israel)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (103, 'hi', 'Hindi', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (104, 'hi_IN', 'Hindi (India)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (105, 'hr', 'Croatian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (106, 'hr_BA', 'Croatian (Bosnia and Herzegovina)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (107, 'hr_HR', 'Croatian (Croatia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (108, 'hu', 'Hungarian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (109, 'hu_HU', 'Hungarian (Hungary)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (110, 'hy', 'Armenian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (111, 'hy_AM', 'Armenian (Armenia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (112, 'id', 'Indonesian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (113, 'id_ID', 'Indonesian (Indonesia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (114, 'is', 'Icelandic', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (115, 'is_IS', 'Icelandic (Iceland)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (116, 'it', 'Italian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (117, 'it_CH', 'Italian (Switzerland)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (118, 'it_IT', 'Italian (Italy)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (119, 'ja', 'Japanese', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (120, 'ja_JP', 'Japanese (Japan)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (121, 'ka', 'Georgian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (122, 'ka_GE', 'Georgian (Georgia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (123, 'kk', 'Kazakh', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (124, 'kk_KZ', 'Kazakh (Kazakhstan)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (125, 'kn', 'Kannada', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (126, 'kn_IN', 'Kannada (India)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (127, 'ko', 'Korean', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (128, 'ko_KR', 'Korean (Korea)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (129, 'kok', 'Konkani', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (130, 'kok_IN', 'Konkani (India)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (131, 'ky', 'Kyrgyz', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (132, 'ky_KG', 'Kyrgyz (Kyrgyzstan)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (133, 'lt', 'Lithuanian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (134, 'lt_LT', 'Lithuanian (Lithuania)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (135, 'lv', 'Latvian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (136, 'lv_LV', 'Latvian (Latvia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (137, 'mi', 'Maori', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (138, 'mi_NZ', 'Maori (New Zealand)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (139, 'mk', 'FYRO Macedonian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (140, 'mk_MK', 'FYRO Macedonian (Former Yugoslav Republic of ', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (141, 'mn', 'Mongolian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (142, 'mn_MN', 'Mongolian (Mongolia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (143, 'mr', 'Marathi', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (144, 'mr_IN', 'Marathi (India)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (145, 'ms', 'Malay', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (146, 'ms_BN', 'Malay (Brunei Darussalam)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (147, 'ms_MY', 'Malay (Malaysia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (148, 'mt', 'Maltese', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (149, 'mt_MT', 'Maltese (Malta)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (150, 'nb', 'Norwegian (Bokm?l)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (151, 'nb_NO', 'Norwegian (Bokm?l) (Norway)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (152, 'nl', 'Dutch', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (153, 'nl_BE', 'Dutch (Belgium)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (154, 'nl_NL', 'Dutch (Netherlands)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (155, 'nn_NO', 'Norwegian (Nynorsk) (Norway)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (156, 'ns', 'Northern Sotho', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (157, 'ns_ZA', 'Northern Sotho (South Africa)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (158, 'pa', 'Punjabi', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (159, 'pa_IN', 'Punjabi (India)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (160, 'pl', 'Polish', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (161, 'pl_PL', 'Polish (Poland)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (162, 'ps', 'Pashto', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (163, 'ps_AR', 'Pashto (Afghanistan)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (164, 'pt', 'Portuguese', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (165, 'pt_BR', 'Portuguese (Brazil)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (166, 'pt_PT', 'Portuguese (Portugal)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (167, 'qu', 'Quechua', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (168, 'qu_BO', 'Quechua (Bolivia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (169, 'qu_EC', 'Quechua (Ecuador)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (170, 'qu_PE', 'Quechua (Peru)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (171, 'ro', 'Romanian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (172, 'ro_RO', 'Romanian (Romania)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (173, 'ru', 'Russian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (174, 'ru_RU', 'Russian (Russia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (175, 'sa', 'Sanskrit', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (176, 'sa_IN', 'Sanskrit (India)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (177, 'se', 'Sami (Northern)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (178, 'se_FI', 'Sami (Northern) (Finland)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (179, 'se_NO', 'Sami (Northern) (Norway)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (180, 'se_SE', 'Sami (Northern) (Sweden)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (181, 'sk', 'Slovak', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (182, 'sk_SK', 'Slovak (Slovakia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (183, 'sl', 'Slovenian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (184, 'sl_SI', 'Slovenian (Slovenia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (185, 'sq', 'Albanian', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (186, 'sq_AL', 'Albanian (Albania)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (187, 'sr_BA', 'Serbian (Cyrillic) (Bosnia and Herzegovina)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (188, 'sr_SP', 'Serbian (Cyrillic) (Serbia and Montenegro)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (189, 'sv', 'Swedish', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (190, 'sv_FI', 'Swedish (Finland)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (191, 'sv_SE', 'Swedish (Sweden)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (192, 'sw', 'Swahili', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (193, 'sw_KE', 'Swahili (Kenya)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (194, 'syr', 'Syriac', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (195, 'syr_SY', 'Syriac (Syria)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (196, 'ta', 'Tamil', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (197, 'ta_IN', 'Tamil (India)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (198, 'te', 'Telugu', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (199, 'te_IN', 'Telugu (India)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (200, 'th', 'Thai', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (201, 'th_TH', 'Thai (Thailand)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (202, 'tl', 'Tagalog', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (203, 'tl_PH', 'Tagalog (Philippines)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (204, 'tn', 'Tswana', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (205, 'tn_ZA', 'Tswana (South Africa)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (206, 'tr', 'Turkish', 1, 1, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (207, 'tr_TR', 'Turkish (Turkey)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (208, 'ts', 'Tsonga', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (209, 'tt', 'Tatar', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (210, 'tt_RU', 'Tatar (Russia)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (211, 'uk', 'Ukrainian', 1, 1, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (212, 'uk_UA', 'Ukrainian (Ukraine)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (213, 'ur', 'Urdu', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (214, 'ur_PK', 'Urdu (Islamic Republic of Pakistan)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (215, 'uz', 'Uzbek (Latin)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (216, 'uz_UZ', 'Uzbek (Cyrillic) (Uzbekistan)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (217, 'vi', 'Vietnamese', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (218, 'vi_VN', 'Vietnamese (Viet Nam)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (219, 'xh', 'Xhosa', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (220, 'xh_ZA', 'Xhosa (South Africa)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (221, 'zh', 'Chinese', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (222, 'zh_CN', 'Chinese (S)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (223, 'zh_HK', 'Chinese (Hong Kong)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (224, 'zh_MO', 'Chinese (Macau)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (225, 'zh_SG', 'Chinese (Singapore)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (226, 'zh_TW', 'Chinese (T)', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (227, 'zu', 'Zulu', 1, 0, 0, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `language` VALUES (228, 'zu_ZA', 'Zulu (South Africa)', 1, 0, 0, 1, 1, 1, 1)"); + + Schema::create('language_base', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->string('key',100)->unique(); + $table->string('hashed',45)->nullable(); + $table->string('code',10); + $table->mediumText('text'); + $table->mediumText('description')->nullable(); + $table->tinyInteger('status')->default(0); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('language_base', function (Blueprint $table) { + $table->foreign('code')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + + Schema::create('language_translate', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('base_id'); + $table->string('key',100); + $table->string('code',10); + $table->mediumText('text')->nullable(); + $table->tinyInteger('status')->default(0); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('language_translate', function (Blueprint $table) { + $table->foreign('base_id')->references('id')->on('language_base'); + $table->foreign('key')->references('key')->on('language_base'); + $table->foreign('code')->references('code')->on('language'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_06_29_111217_add_data_to_property_additional_info_key.php b/database/migrations/2020_06_29_111217_add_data_to_property_additional_info_key.php new file mode 100644 index 0000000..06749e6 --- /dev/null +++ b/database/migrations/2020_06_29_111217_add_data_to_property_additional_info_key.php @@ -0,0 +1,48 @@ +insert( + [ + [ + 'additional_info_key' => 'locale_name_language', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'additional_info_key' => 'locale_name', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ] + ] + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::connection()->statement("DELETE FROM property_additional_info_key WHERE additional_info_key = 'locale_name_language' "); + DB::connection()->statement("DELETE FROM property_additional_info_key WHERE additional_info_key = 'locale_name' "); + } +} diff --git a/database/migrations/2020_06_30_171839_add_column_room_bed_type.php b/database/migrations/2020_06_30_171839_add_column_room_bed_type.php new file mode 100644 index 0000000..5b26bcd --- /dev/null +++ b/database/migrations/2020_06_30_171839_add_column_room_bed_type.php @@ -0,0 +1,32 @@ +string('icon',30)->after('name')->nullable(); + }); + + DB::connection()->statement("UPDATE property_room_bed_type SET icon = '1.png' WHERE id = 1"); + DB::connection()->statement("UPDATE property_room_bed_type SET icon = '1.png' WHERE id = 2"); + DB::connection()->statement("UPDATE property_room_bed_type SET icon = '1.png' WHERE id = 3"); + DB::connection()->statement("UPDATE property_room_bed_type SET icon = '1.png' WHERE id = 4"); + DB::connection()->statement("UPDATE property_room_bed_type SET icon = '1.png' WHERE id = 5"); + DB::connection()->statement("UPDATE property_room_bed_type SET icon = '1.png' WHERE id = 6"); + DB::connection()->statement("UPDATE property_room_bed_type SET icon = '1.png' WHERE id = 7"); + DB::connection()->statement("UPDATE property_room_bed_type SET icon = '1.png' WHERE id = 8"); + } + + + public function down() + { + Schema::table('property_room_bed_type', function (Blueprint $table) { + $table->dropColumn('icon'); + }); + } +} diff --git a/database/migrations/2020_06_30_174837_change_columns_executive_contact_name_and_property_id_fields.php b/database/migrations/2020_06_30_174837_change_columns_executive_contact_name_and_property_id_fields.php new file mode 100644 index 0000000..27f88da --- /dev/null +++ b/database/migrations/2020_06_30_174837_change_columns_executive_contact_name_and_property_id_fields.php @@ -0,0 +1,34 @@ +truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + Schema::table('property_executive', function (Blueprint $table) { + $table->unique(['property_id', 'name_surname']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_06_30_182422_update_data_to_site_config.php b/database/migrations/2020_06_30_182422_update_data_to_site_config.php new file mode 100644 index 0000000..8b1f779 --- /dev/null +++ b/database/migrations/2020_06_30_182422_update_data_to_site_config.php @@ -0,0 +1,37 @@ +where('config_key' , 'profile_rate_mapping')->update(['config_value_json' => $profileRateMapping]); + DB::table('site_config')->where('config_key' , 'hint_list')->update(['config_value_json' => $profileHint]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + } +} diff --git a/database/migrations/2020_07_02_092052_drop_locale_tables.php b/database/migrations/2020_07_02_092052_drop_locale_tables.php new file mode 100644 index 0000000..4a95c2c --- /dev/null +++ b/database/migrations/2020_07_02_092052_drop_locale_tables.php @@ -0,0 +1,45 @@ +dropColumn('destination_id'); + }); + Schema::dropIfExists('currency_locale'); + Schema::dropIfExists('language_locale'); + Schema::dropIfExists('permission_locale'); + Schema::dropIfExists('property_additional_info_key_locale'); + Schema::dropIfExists('property_channel_category_locale'); + Schema::dropIfExists('property_content_category_locale'); + Schema::dropIfExists('property_executive_type_locale'); + Schema::dropIfExists('property_fact_attribute_locale'); + Schema::dropIfExists('property_fact_locale'); + Schema::dropIfExists('property_photo_category_locale'); + Schema::dropIfExists('property_room_bed_type_locale'); + Schema::dropIfExists('property_room_type_locale'); + Schema::dropIfExists('property_type_locale'); + Schema::dropIfExists('property_unit_locale'); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_07_03_143603_add_language_key_to_tables.php b/database/migrations/2020_07_03_143603_add_language_key_to_tables.php new file mode 100644 index 0000000..bade28c --- /dev/null +++ b/database/migrations/2020_07_03_143603_add_language_key_to_tables.php @@ -0,0 +1,75 @@ +string('language_key',128)->after('name')->nullable(); + }); + Schema::table('language', function (Blueprint $table) { + $table->string('language_key',128)->after('name')->nullable(); + }); + Schema::table('property_channel_category', function (Blueprint $table) { + $table->string('language_key',255)->after('name')->nullable(); + }); + Schema::table('property_content_category', function (Blueprint $table) { + $table->string('language_key',255)->after('name')->nullable(); + }); + Schema::table('property_executive_type', function (Blueprint $table) { + $table->string('language_key',255)->after('name')->nullable(); + }); + Schema::table('property_fact_attribute', function (Blueprint $table) { + $table->string('language_key',255)->after('name')->nullable(); + }); + Schema::table('property_fact', function (Blueprint $table) { + $table->string('language_key',250)->after('name')->nullable(); + }); + Schema::table('property_photo_category', function (Blueprint $table) { + $table->string('language_key',80)->after('category_name')->nullable(); + }); + Schema::table('property_room_bed_type', function (Blueprint $table) { + $table->string('language_key',255)->after('name')->nullable(); + }); + Schema::table('property_room_type', function (Blueprint $table) { + $table->string('language_key',255)->after('name')->nullable(); + }); + Schema::table('property_type', function (Blueprint $table) { + $table->string('language_key',255)->after('name')->nullable(); + }); + Schema::table('property_unit', function (Blueprint $table) { + $table->string('language_key',128)->after('name')->nullable(); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('currency', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('language', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_channel_category', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_content_category', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_executive_type', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_fact_attribute', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_fact', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_photo_category', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_room_bed_type', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_room_type', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_type', function (Blueprint $table) { $table->dropColumn('language_key'); }); + Schema::table('property_unit', function (Blueprint $table) { $table->dropColumn('language_key'); }); + } +} diff --git a/database/migrations/2020_07_07_093527_create_table_property_room_view_table.php b/database/migrations/2020_07_07_093527_create_table_property_room_view_table.php new file mode 100644 index 0000000..93ee25f --- /dev/null +++ b/database/migrations/2020_07_07_093527_create_table_property_room_view_table.php @@ -0,0 +1,42 @@ +bigIncrements('id'); + $table->string('name',100); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_room_view_type', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_view_type'); + } +} diff --git a/database/migrations/2020_07_07_094356_create_property_room_view_mapping_table.php b/database/migrations/2020_07_07_094356_create_property_room_view_mapping_table.php new file mode 100644 index 0000000..087a5b7 --- /dev/null +++ b/database/migrations/2020_07_07_094356_create_property_room_view_mapping_table.php @@ -0,0 +1,45 @@ +bigIncrements('id'); + $table->unsignedBigInteger('room_id'); + $table->unsignedBigInteger('room_view_type_id'); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::table('property_room_view_mapping', function (Blueprint $table) { + $table->unique(['room_id','room_view_type_id']); + $table->foreign('room_id')->references('id')->on('property_room'); + $table->foreign('room_view_type_id')->references('id')->on('property_room_view_type'); + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_room_view_mapping'); + } +} diff --git a/database/migrations/2020_07_07_134958_add_data_property_type_table.php b/database/migrations/2020_07_07_134958_add_data_property_type_table.php new file mode 100644 index 0000000..e487383 --- /dev/null +++ b/database/migrations/2020_07_07_134958_add_data_property_type_table.php @@ -0,0 +1,327 @@ +truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + DB::table('property_type')->insert( + [ + [ + 'name' => '1 Star Hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => '2 Star Hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => '3 Star Hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => '4 Star Hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => '5 Star Hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Apart Hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Boutique Hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Bungolow', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Cabin', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Capsule hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Castle', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Chalet', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Condo', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Cottage', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Country house', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Farm stay', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Guest house', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Hostel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Lodge', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Love hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Motel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Pansion', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Private Holiday Home', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Ranch', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Residence', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Resort', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Ryokan', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Safari tentalow', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Suit Hotel', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Thermal Facility ', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Town House', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Tree House ', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ] + ] + + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::table('property_type')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + } +} + diff --git a/database/migrations/2020_07_07_143007_add_data_property_room_type_table.php b/database/migrations/2020_07_07_143007_add_data_property_room_type_table.php new file mode 100644 index 0000000..62541d2 --- /dev/null +++ b/database/migrations/2020_07_07_143007_add_data_property_room_type_table.php @@ -0,0 +1,255 @@ +truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + DB::table('property_room_type')->insert( + [ + [ + 'name' => 'Accessible Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Apartment', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Attic', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Bungalow', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Club Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Deluxe', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Economy', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Family Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Grand Suite', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Junior Suite', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'King suite', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Pent House', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Premium Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Standart Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Suite', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Superior', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Villa', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Connection Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Studio Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Roh Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Loft Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Cabana Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Dublex Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Honeymoon Room', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ] + ] + + ); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::table('property_room_type')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + } +} diff --git a/database/migrations/2020_07_07_144833_add_data_property_room_bed_type_table.php b/database/migrations/2020_07_07_144833_add_data_property_room_bed_type_table.php new file mode 100644 index 0000000..f574e90 --- /dev/null +++ b/database/migrations/2020_07_07_144833_add_data_property_room_bed_type_table.php @@ -0,0 +1,145 @@ +truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + DB::table('property_room_bed_type')->insert( + [ + [ + 'name' => 'Bunk Bed', + 'language_key' => '', + 'icon' => '1.png', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Double Bed', + 'language_key' => '', + 'icon' => '1.png', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'French Bed', + 'language_key' => '', + 'icon' => '1.png', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Futon', + 'language_key' => '', + 'icon' => '1.png', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'King Bed', + 'language_key' => '', + 'icon' => '1.png', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Queen Bed', + 'language_key' => '', + 'icon' => '1.png', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Single Bed', + 'language_key' => '', + 'icon' => '1.png', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Sofa Bed', + 'language_key' => '', + 'icon' => '1.png', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Twin Bed', + 'language_key' => '', + 'icon' => '1.png', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ] + + ] + + ); + + DB::statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::table('property_executive_type')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + $result = DB::table('property_executive_type')->insert( + [ + 'name' => 'Accounting', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ] + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::table('property_room_bed_type')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + } +} diff --git a/database/migrations/2020_07_07_145651_create_property_web_table.php b/database/migrations/2020_07_07_145651_create_property_web_table.php new file mode 100644 index 0000000..07f5dfd --- /dev/null +++ b/database/migrations/2020_07_07_145651_create_property_web_table.php @@ -0,0 +1,73 @@ +increments('id'); + $table->string('name', 128)->unique(); + $table->string('folder_name', 200); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + }); + + + Schema::create('property_web', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->integer('property_id')->unsigned(); + $table->string('domain', 50)->unique(); + $table->string('default_language', 10)->default('en_EN'); + $table->unsignedInteger('template_id')->default(1); + $table->string('token',128)->unique(); + $table->tinyInteger('status')->default(1); + $table->unsignedInteger('created_by'); + $table->unsignedInteger('updated_by'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->foreign('property_id')->references('id')->on('property'); + $table->foreign('template_id')->references('id')->on('property_web_template'); + + }); + + DB::table('property_web_template')->insert( + [ + [ + 'name' => 'Default Template', + 'folder_name' => 'default', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + ] + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('property_web'); + Schema::dropIfExists('property_web_template'); + } +} diff --git a/database/migrations/2020_07_07_152015_add_data_property_executive_type_table.php b/database/migrations/2020_07_07_152015_add_data_property_executive_type_table.php new file mode 100644 index 0000000..0a0ea6d --- /dev/null +++ b/database/migrations/2020_07_07_152015_add_data_property_executive_type_table.php @@ -0,0 +1,119 @@ +truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + DB::table('property_executive_type')->insert( + [ + [ + 'name' => 'Accounting', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Activity', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Finance', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Front Office', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Group', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Manager', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Reservation', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Sales and Marketing', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Others', + 'language_key' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ] + ] + + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::table('property_executive_type')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + } +} diff --git a/database/migrations/2020_07_08_102336_clear_unique_property_executive_table.php b/database/migrations/2020_07_08_102336_clear_unique_property_executive_table.php new file mode 100644 index 0000000..9a89466 --- /dev/null +++ b/database/migrations/2020_07_08_102336_clear_unique_property_executive_table.php @@ -0,0 +1,30 @@ +dropUnique(['property_id', 'name_surname']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_07_08_114601_change_property_chain_table.php b/database/migrations/2020_07_08_114601_change_property_chain_table.php new file mode 100644 index 0000000..f7b084a --- /dev/null +++ b/database/migrations/2020_07_08_114601_change_property_chain_table.php @@ -0,0 +1,3378 @@ +truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + Schema::table('property_chain', function (Blueprint $table) { + $table->integer('priority')->after('loyalty')->nullable(); + }); + + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1, 'Independent', 'null', 1, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2, '118 Hotel', 'null', 2, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3, '13 Coins Hotels & Resorts', 'null', 3, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (4, '1589 Hotels', 'null', 4, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (5, '1834 Hotels', 'null', 5, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (6, '1st Arabat hotel', 'null', 6, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (7, '1st for Orlando', 'null', 7, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (8, '1st Inn Hotel', 'null', 8, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (9, '226 Hospitality', 'null', 9, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (10, '22nd Residences', 'null', 10, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (11, '24 Guesthouse', 'null', 11, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (12, '2Home_JSC', 'null', 12, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (13, '2ndhomes_C', 'null', 13, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (14, '3 Tranches Home', 'null', 14, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (15, '5 Star Bali Villa', 'null', 15, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (16, '5 Star Villa Holidays_C', 'null', 16, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (17, '5 Yue Hotel Group', 'null', 17, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (18, '5Footway.Inn', 'null', 18, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (19, '7Heaven Vacation_C', 'null', 19, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (20, '8 Residence', 'null', 20, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (21, '85 Sky Tower CTraveller Suit', 'null', 21, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (22, '8Hotels Collection', 'null', 22, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (23, '9 square', 'null', 23, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (24, 'A and C housing', 'null', 24, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (25, 'A Perfect Stay', 'null', 25, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (26, 'A&EM Hotel Group', 'null', 28, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (27, 'A&O Hotels and Hostels', 'null', 29, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (28, 'A.T. Home', 'null', 26, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (29, 'Abadi Sewa Apartemen', 'null', 30, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (30, 'Abba Hoteles', 'null', 31, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (31, 'Abby Hotel', 'null', 32, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (32, 'ABC Accommodation', 'null', 33, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (33, 'abcds', 'null', 34, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (34, 'Abdi jaya Homestay', 'null', 35, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (35, 'Abdi Property Indonesia', 'null', 36, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (36, 'Abhijitanimation Property', 'null', 37, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (37, 'Abhinaya Wisata Bali', 'null', 38, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (38, 'Abidos Hotels', 'null', 39, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (39, 'Abode Homes', 'null', 40, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (40, 'Aboy', 'null', 41, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (41, 'AbraCasa Goa_C', 'null', 42, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (42, 'Absolute Resort & Hotels', 'null', 43, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (43, 'Abu Dhabi National Hotels', 'null', 44, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (44, 'Acappella', 'null', 45, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (45, 'Access', 'null', 46, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (46, 'Accom Noosa', 'null', 47, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (47, 'Accom Whitsundays', 'null', 48, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (48, 'Accomodation Sydney City', 'null', 49, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (49, 'Accor Hotels', 'null', 50, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (50, 'Ace Hotel', 'null', 51, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (51, 'Ace Residences', 'null', 52, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (52, 'ACHAT Hotels', 'null', 53, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (53, 'ACO Vacation Homes', 'null', 54, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (54, 'Acta Hotels', 'null', 55, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (55, 'Actual Paseo de Gracia', 'null', 56, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (56, 'Ada Waktu Villa', 'null', 57, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (57, 'Adaaran Resorts', 'null', 58, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (58, 'Adam Jyota', 'null', 59, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (59, 'Adam Villa Puncak', 'null', 60, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (60, 'Adamson', 'null', 61, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (61, 'Adaru', 'null', 62, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (62, 'Adelaide City Apartments', 'null', 63, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (63, 'Adi Hartawan Property', 'null', 64, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (64, 'Adi Kost', 'null', 65, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (65, 'Adimas Group', 'null', 66, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (66, 'Adina Apartments', 'null', 67, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (67, 'Adiwana Hotels & Resorts', 'null', 68, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (68, 'Advantage Vacation Homes_C', 'null', 69, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (69, 'Advena Hotels', 'null', 70, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (70, 'Adya Hotel', 'null', 71, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (71, 'Aero Hotels', 'null', 72, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (72, 'Aero Space', 'null', 73, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (73, 'AFP Apartment', 'null', 74, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (74, 'Afra Adjie', 'null', 75, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (75, 'Afribode', 'null', 76, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (76, 'Afribode Accomodation', 'null', 77, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (77, 'African Fiesta', 'null', 78, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (78, 'African Sky Hotels, Spa and Resorts', 'null', 79, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (79, 'Agency Bridge', 'null', 80, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (80, 'Agency Test', 'null', 81, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (81, 'Agnes Pro', 'null', 82, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (82, 'Agnis_JP', 'null', 83, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (83, 'Agus', 'null', 84, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (84, 'AHA', 'null', 85, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (85, 'Ahmad Ripai', 'null', 86, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (86, 'AIRHOST', 'null', 94, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (87, 'Ain House', 'null', 87, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (88, 'AinB Apartments', 'null', 88, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (89, 'Airbest', 'null', 89, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (90, 'AirBnB Osaka', 'null', 90, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (91, 'Airbnbkirinji_JP', 'null', 91, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (92, 'Airbnbnb_JP', 'null', 92, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (93, 'Airhome_C', 'null', 93, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (94, 'AirM8 Apartments', 'null', 95, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (95, 'Airmanaged_C', 'null', 96, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (96, 'AirProfit_JP', 'null', 97, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (97, 'AirSapo', 'null', 98, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (98, 'Airxpress', 'null', 99, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (99, 'Aitken Spence Hotels', 'null', 100, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (100, 'Aiueo Real Estate', 'null', 101, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (101, 'AJ InterBridge Inc.', 'null', 102, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (102, 'Akihiro Nakakita', 'null', 103, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (103, 'Akiyama', 'null', 104, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (104, 'Akkaranand Property', 'null', 105, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (105, 'AKOM Apartments', 'null', 106, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (106, 'Al Ansar Group', 'null', 107, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (107, 'Al Aseel Group', 'null', 108, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (108, 'Al Diar Hotels', 'null', 109, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (109, 'Al Diyafah Group', 'null', 110, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (110, 'Al Eairy Group', 'null', 111, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (111, 'Al Farhan Group', 'null', 112, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (112, 'Al Hamra Group', 'null', 113, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (113, 'Al Khoory Group', 'null', 114, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (114, 'Al Massa Group', 'null', 115, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (115, 'Al Mohandes Group', 'null', 116, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (116, 'Al Muhaideb Hotels', 'null', 117, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (117, 'Al Mukhtara Group', 'null', 118, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (118, 'Al Nabarees Group', 'null', 119, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (119, 'Al Nahdi Group', 'null', 120, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (120, 'Al Yamama Group', 'null', 121, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (121, 'Alabama', 'null', 122, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (122, 'Alagon Hotels and Spa Group', 'null', 123, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (123, 'Alan Pasotto', 'null', 124, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (124, 'Alang Alang Indonesia', 'null', 125, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (125, 'Alegher Kata', 'null', 126, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (126, 'Alex Pattaya Properties', 'null', 127, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (127, 'Alexandre Hotels', 'null', 128, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (128, 'Alfonso\'s Place', 'null', 129, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (129, 'ALH Group', 'null', 130, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (130, 'Ali Fahmi', 'null', 131, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (131, 'Alila', 'null', 132, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (132, 'Alina Apartment', 'null', 133, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (133, 'Alissie Property', 'null', 134, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (134, 'Alkoclar Hotels', 'null', 135, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (135, 'All about furano', 'null', 136, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (136, 'All Phuket Real Estate', 'null', 137, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (137, 'Allen House', 'null', 138, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (138, 'Allo Maisons', 'null', 139, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (139, 'Allurepark Thijmse Berg_C', 'null', 140, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (140, 'Almira Kost Eksklusif', 'null', 141, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (141, 'Aloha Saigon', 'null', 142, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (142, 'Alona Treats & Travel Corporation', 'null', 143, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (143, 'Alotz', 'null', 144, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (144, 'Alpha Hotel Management', 'null', 145, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (145, 'Alpha Hotels and Resorts', 'null', 146, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (146, 'Alpha Solid', 'null', 147, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (147, 'Altis Hotels', 'null', 148, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (148, 'Alur natura Apartment', 'null', 149, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (149, 'Alva', 'null', 150, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (150, 'Alysia Aman', 'null', 151, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (151, 'AM Japan', 'null', 152, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (152, 'AM Resorts', 'null', 153, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (153, 'Amami Viaggi_C', 'null', 154, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (154, 'Aman Resorts', 'null', 155, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (155, 'Amanda Property', 'null', 156, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (156, 'Amaranth s.r.o.', 'null', 157, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (157, 'Amaya Resorts', 'null', 158, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (158, 'Amaze Balls', 'null', 159, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (159, 'Amazing Homes', 'null', 160, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (160, 'Amazing Revolution', 'null', 161, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (161, 'Amber Hotels', 'null', 162, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (162, 'Amelly', 'null', 163, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (163, 'Ameri Tel Inns', 'null', 164, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (164, 'Amerian Hoteles', 'null', 165, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (165, 'AmericInn', 'null', 166, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (166, 'Ameristar Casinos', 'null', 167, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (167, 'Amhsa Marina Hotels & Resorts', 'null', 168, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (168, 'Amora Hotels & Resorts', 'null', 169, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (169, 'Amore Mio Travel', 'null', 170, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (170, 'Amra', 'null', 171, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (171, 'AMResorts', 'null', 172, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (172, 'Amsterdam Hospitality', 'null', 173, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (173, 'AN Apartment HCMC', 'null', 174, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (174, 'An Nguyen Building Hanoi', 'null', 175, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (175, 'An Nhien', 'null', 176, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (176, 'An Phuoc Hung', 'null', 177, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (177, 'An Villa', 'null', 178, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (178, 'ANA Crowne Plaza', 'null', 179, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (179, 'Anabuki Space Share', 'null', 180, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (180, 'Anang Property', 'null', 181, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (181, 'Ancasa', 'null', 182, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (182, 'Anchor Abode', 'null', 183, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (183, 'And Chill_C', 'null', 184, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (184, 'Andes', 'null', 185, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (185, 'Anemon Hotels', 'null', 186, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (186, 'Anest', 'null', 187, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (187, 'Aneto Iznajar', 'null', 188, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (188, 'Angel Fredericha', 'null', 189, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (189, 'Angela Ylagan', 'null', 190, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (190, 'Anggit Group', 'null', 191, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (191, 'Anggrek Room', 'null', 192, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (192, 'Anggun Bromo Homestay', 'null', 193, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (193, 'Angiri Resorts', 'null', 194, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (194, 'Aninuan Apartment', 'null', 195, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (195, 'Anjani', 'null', 196, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (196, 'Anlink', 'null', 197, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (197, 'Anna Maria Island Beach Rentals_C', 'null', 198, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (198, 'Antoine\'s Apartments', 'null', 199, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (199, 'Antony Lee Property', 'null', 200, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (200, 'Anwar', 'null', 201, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (201, 'Anyaa Property', 'null', 202, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (202, 'Aonanta Pool Villa', 'null', 203, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (203, 'APA Hotels', 'null', 204, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (204, 'Apartemen Gading Nias Residence', 'null', 205, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (205, 'Aparteon', 'null', 206, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (206, 'ApartLux', 'null', 207, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (207, 'Apartmani Nedjeljko_C', 'null', 208, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (208, 'Apartment Group by 4 Property', 'null', 209, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (209, 'Apartment Maller_C', 'null', 210, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (210, 'Apartment Rukonic', 'null', 211, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (211, 'Apartment Service', 'null', 212, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (212, 'Apartment Tamansari Panoramic Group', 'null', 213, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (213, 'Apartment2c', 'null', 214, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (214, 'Apartments Villa Mariel', 'null', 215, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (215, 'Apavou Hotels-Resorts & Spa', 'null', 216, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (216, 'Apex Hotels', 'null', 217, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (217, 'Apip', 'null', 218, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (218, 'Appart\'City', 'null', 219, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (219, 'Apple 1', 'null', 220, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (220, 'Apple Apartments Group', 'null', 221, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (221, 'Aqua Hotels and Resorts', 'null', 222, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (222, 'Aqua-Aston Hospitality', 'null', 223, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (223, 'Aqueen Hotel & Resorts', 'null', 224, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (224, 'Aquis Hotels and Resorts', 'null', 225, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (225, 'Ara & Maru', 'null', 226, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (226, 'Araia Room', 'null', 227, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (227, 'Aranwa Hotels Resorts & Spas', 'null', 228, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (228, 'Aravis Holidays_C', 'null', 229, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (229, 'Arc Avenue Hotels', 'null', 230, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (230, 'Arcadia Hotels', 'null', 231, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (231, 'Arcantis Hotels', 'null', 232, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (232, 'Arcat Houser SL - APKEYS', 'null', 233, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (233, 'Archipelago International', 'null', 234, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (234, 'Arcotel Hotels', 'null', 235, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (235, 'Arenaa', 'null', 236, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (236, 'ARENDAIZRAIL Apartments', 'null', 237, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (237, 'Arenty', 'null', 238, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (238, 'Arfan Maulana', 'null', 239, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (239, 'Arfe Room', 'null', 240, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (240, 'Argyle Group', 'null', 241, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (241, 'Ari', 'null', 242, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (242, 'Ari Wiryastini', 'null', 243, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (243, 'Ariasa Made', 'null', 244, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (244, 'Ario Yosinarta', 'null', 245, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (245, 'Ariyanti Tamansari Panorama', 'null', 246, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (246, 'Arnatt Property', 'null', 247, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (247, 'ARRA Accommodation Group', 'null', 248, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (248, 'Arsya Rooms', 'null', 249, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (249, 'Art Series Hotels', 'null', 250, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (250, 'Arthawidyan Homestay', 'null', 251, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (251, 'ARTIST', 'null', 253, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (252, 'Artis Suite & Villa', 'null', 252, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (253, 'Artotel', 'null', 254, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (254, 'Aryaduta Puncak Resort', 'null', 255, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (255, 'A\'s Azotea De Bohol', 'null', 27, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (256, 'As Usual', 'null', 256, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (257, 'Asa Property', 'null', 257, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (258, 'Asa Villas Property', 'null', 258, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (259, 'Ascott International', 'null', 259, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (260, 'Asep Rian', 'null', 260, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (261, 'Ashok Group', 'null', 261, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (262, 'Asia Holiday Retreats', 'null', 262, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (263, 'Asistee Business', 'null', 263, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (264, 'Aspen Parks', 'null', 264, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (265, 'Assetrive', 'null', 265, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (266, 'Assign Tax', 'null', 266, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (267, 'Assign Tax - India', 'null', 267, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (268, 'Assign Tax - Malay', 'null', 268, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (269, 'Assign Tax - SG', 'null', 269, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (270, 'Aston Hotels And Resorts', 'null', 270, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (271, 'Aston International', 'null', 271, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (272, 'Astotel', 'null', 272, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (273, 'ASURE Accommodation', 'null', 273, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (274, 'at Hotel Group', 'null', 274, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (275, 'Atahotels', 'null', 275, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (276, 'Atelier de Hoteles', 'null', 276, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (277, 'Athaya Homestay', 'null', 277, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (278, 'Atiram Hotels', 'null', 278, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (279, 'Atlas', 'null', 279, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (280, 'Atlas 46 Ramblas', 'null', 280, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (281, 'Atour Hotel', 'null', 281, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (282, 'atstushi nomoto', 'null', 282, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (283, 'Attitude Resorts', 'null', 283, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (284, 'Audrey Group', 'null', 284, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (285, 'Australian Home Away', 'null', 285, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (286, 'Australian Luxury Stays', 'null', 286, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (287, 'Austria Trend Hotels & Resorts', 'null', 287, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (288, 'Avantgarde Collection', 'null', 288, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (289, 'Avari Hotels', 'null', 289, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (290, 'AVE Hotels', 'null', 290, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (291, 'Avenida Tourist Homes SL', 'null', 291, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (292, 'Avilla Hospitality', 'null', 292, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (293, 'Avillion Group', 'null', 293, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (294, 'Axel Hotels', 'null', 294, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (295, 'Ayre Hoteles', 'null', 295, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (296, 'Ayu Destiana Group', 'null', 296, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (297, 'Ayu Group', 'null', 297, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (298, 'Ayu Place', 'null', 298, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (299, 'Azcarya Villa', 'null', 299, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (300, 'Azimut Hotels', 'null', 300, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (301, 'Azure Condotel Staycation', 'null', 301, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (302, 'Azure Online Marketing LTO', 'null', 302, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (303, 'B Vietnam', 'null', 303, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (304, 'B&B 22 House', 'null', 304, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (305, 'B&B Hotels', 'null', 305, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (306, 'B2 Hotel Group', 'null', 306, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (307, 'Baan Marwin Pool Villa Hua Hin', 'null', 307, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (308, 'Baan Natcha', 'null', 308, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (309, 'Baan Sabai Rama IV Service Apartment', 'null', 309, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (310, 'Baan Thai Lanna Pattaya', 'null', 310, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (311, 'Baan Tong Thip', 'null', 311, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (312, 'Baansawasdee', 'null', 312, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (313, 'Babylon Apartment', 'null', 313, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (314, 'Backpackers Haven', 'null', 314, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (315, 'Bacohome3', 'null', 315, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (316, 'Badra House', 'null', 316, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (317, 'Baguio Foggy Hills', 'null', 317, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (318, 'Bagus Home', 'null', 318, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (319, 'Bagus management', 'null', 319, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (320, 'Bahasa Villa', 'null', 320, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (321, 'Bahia Principe Hotels & Resorts', 'null', 321, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (322, 'Bai zhonghua', 'null', 322, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (323, 'Balai Condominiums', 'null', 323, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (324, 'Bali Dream Villa', 'null', 324, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (325, 'Bali Exception', 'null', 325, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (326, 'Bali Family Hospitality', 'null', 326, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (327, 'Bali Golden Living', 'null', 327, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (328, 'Bali Green Villa', 'null', 328, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (329, 'Bali Home', 'null', 329, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (330, 'Bali Home Holiday', 'null', 330, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (331, 'Bali Inside', 'null', 331, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (332, 'Bali Management Villas', 'null', 332, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (333, 'Bali Options', 'null', 333, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (334, 'Bali Sanur Home', 'null', 334, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (335, 'Bali Smart Management', 'null', 335, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (336, 'Bali Studio Apartment Group', 'null', 336, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (337, 'Bali Unique Stay', 'null', 337, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (338, 'Bali Villa Getaways', 'null', 338, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (339, 'Bali Villa Management', 'null', 339, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (340, 'Bali Villa Manager', 'null', 340, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (341, 'Bali Villas HVR', 'null', 341, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (342, 'Bali Villas R Us', 'null', 342, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (343, 'BaliOn Villas', 'null', 343, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (344, 'Baliwid Villa', 'null', 344, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (345, 'Balkondes Kembanglimus', 'null', 345, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (346, 'Balladins', 'null', 346, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (347, 'Balloon Group Global', 'null', 347, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (348, 'Balqis Group', 'null', 348, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (349, 'Bamboo Hotel Ubud', 'null', 349, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (350, 'Bamboo House Homestay', 'null', 350, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (351, 'Bamboo Nest', 'null', 351, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (352, 'Ban Tai Estate_C', 'null', 352, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (353, 'Banbridge Properties', 'null', 353, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (354, 'BangTaiestate', 'null', 354, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (355, 'Banhakeura', 'null', 355, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (356, 'Bans City', 'null', 356, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (357, 'Banyan Tree Hotels & Resorts', 'null', 357, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (358, 'Banyu Riris Villa', 'null', 358, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (359, 'Barceló Hotels & Resorts', 'null', 359, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (360, 'Barcelona Best Services_C', 'null', 360, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (361, 'BarcelonaForRent Apartments_C', 'null', 361, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (362, 'Barefoot Luxury Villas', 'null', 362, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (363, 'Baron Hotels & Resorts', 'null', 363, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (364, 'Bas Apartments', 'null', 364, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (365, 'Base Planning', 'null', 365, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (366, 'Basezero', 'null', 366, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (367, 'Basma Group', 'null', 367, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (368, 'Bastion Hotels', 'null', 368, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (369, 'Batiqa Hotels', 'null', 369, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (370, 'Batu Permai', 'null', 370, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (371, 'Batur Sunrise Guesthouse', 'null', 371, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (372, 'Bay Of Islands Holiday Homes', 'null', 372, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (373, 'Bay Point Golf Resort & Spa', 'null', 373, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (374, 'Bay Residence', 'null', 374, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (375, 'Bayu Pro', 'null', 375, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (376, 'Bayview International Hotels & Resorts', 'null', 376, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (377, 'BB', 'null', 377, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (378, 'BCN Stop', 'null', 378, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (379, 'BCN Urban Hotels', 'null', 379, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (380, 'Be happy', 'null', 380, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (381, 'Be Home', 'null', 381, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (382, 'Be Hostels', 'null', 382, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (383, 'BE LIVE HOTELS', 'null', 383, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (384, 'BEACH INN MAKASSAR', 'null', 384, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (385, 'Beach Retreats Vic', 'null', 385, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (386, 'Beachcomber Hotels', 'null', 386, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (387, 'Beachside Breaks', 'null', 387, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (388, 'Beds and Bars', 'null', 388, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (389, 'Bedsolving', 'null', 389, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (390, 'Beheermijnhuis_C', 'null', 390, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (391, 'Beinte Singko De Marso', 'null', 391, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (392, 'Bel Air Collection Resorts', 'null', 392, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (393, 'Bella Property', 'null', 393, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (394, 'Bella Villa Group', 'null', 394, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (395, 'Bella Vista Accommodation', 'null', 395, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (396, 'Bella Vista Waterfront', 'null', 396, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (397, 'Belmond', 'null', 397, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (398, 'Bengawan Residence 66', 'null', 398, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (399, 'Benikea', 'null', 399, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (400, 'Benx Napoli', 'null', 400, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (401, 'Berawa Beach Estate', 'null', 401, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (402, 'Berjaya Group', 'null', 402, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (403, 'Berjaya Hotels & Resorts', 'null', 403, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (404, 'Berlin Nawaputra', 'null', 404, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (405, 'Bernbon Condounit', 'null', 405, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (406, 'Bespoke Hotels', 'null', 406, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (407, 'Bespoke Residences', 'null', 407, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (408, 'Best Hotels', 'null', 408, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (409, 'Best inn Okinawa', 'null', 409, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (410, 'Best of Magnetic', 'null', 410, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (411, 'Best View Hotel', 'null', 411, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (412, 'Best Western International', 'null', 412, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (413, 'Bestbnb (Sogi)', 'null', 413, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (414, 'Beta Management', 'null', 414, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (415, 'Better Stay', 'null', 415, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (416, 'Better Vacations_C', 'null', 416, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (417, 'Bettoja Hotels', 'null', 417, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (418, 'Beyond a Room_C', 'null', 418, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (419, 'Bhadur Group', 'null', 419, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (420, 'BHMA Hotels and Resorts', 'null', 420, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (421, 'BHome Kim Ma', 'null', 421, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (422, 'BIG4 Holiday Parks', 'null', 424, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (423, 'Bicheno Holiday Rentals', 'null', 422, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (424, 'Big Apple Management, LLC_C', 'null', 423, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (425, 'Biliq Coliving', 'null', 425, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (426, 'Bill Knaggs Real Estate', 'null', 426, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (427, 'Billy PDS', 'null', 427, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (428, 'Biltmore Hotels', 'null', 428, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (429, 'Bin Majid Hotels & Resorts', 'null', 429, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (430, 'Bingo', 'null', 430, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (431, 'Binh An Home', 'null', 431, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (432, 'Bintan Property', 'null', 432, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (433, 'Bintang Property', 'null', 433, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (434, 'Bjorn', 'null', 434, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (435, 'BK-Property', 'null', 435, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (436, 'BK\'s Motel Group', 'null', 436, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (437, 'Black Pearl & Co', 'null', 437, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (438, 'BLDG 1587', 'null', 438, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (439, 'Blessing Mansion', 'null', 439, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (440, 'Blessing Property', 'null', 440, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (441, 'BLG Condo Rentals', 'null', 441, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (442, 'Bloq Apartments', 'null', 442, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (443, 'Blossom Hill Inn', 'null', 443, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (444, 'Blue Diamond Resorts', 'null', 444, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (445, 'Blue Group BV_C', 'null', 445, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (446, 'Blue Horizon', 'null', 446, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (447, 'Blue Mountains Getaways_C', 'null', 447, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (448, 'Blue Sands', 'null', 448, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (449, 'Blue Sapphire Property Management', 'null', 449, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (450, 'Blue Sea Hotels & Resorts', 'null', 450, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (451, 'Blue Seagull', 'null', 451, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (452, 'Blue Views Villas and Apartments', 'null', 452, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (453, 'BluelineBenidorm_C', 'null', 453, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (454, 'Blueseagull_JP', 'null', 454, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (455, 'BnB Lord_C', 'null', 455, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (456, 'BnB Manager_C', 'null', 456, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (457, 'Bnbbuddy_C', 'null', 457, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (458, 'BNBHost_C', 'null', 458, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (459, 'Bnbme1', 'null', 459, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (460, 'bnbprofits', 'null', 460, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (461, 'Boas Swiss Hotel', 'null', 461, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (462, 'BoerenBed_C', 'null', 462, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (463, 'Bogor Valley', 'null', 463, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (464, 'Bohemian Hostels', 'null', 464, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (465, 'Bonanova Ako', 'null', 465, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (466, 'Bonavista', 'null', 466, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (467, 'Bondi Group', 'null', 467, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (468, 'Bondia Hotels', 'null', 468, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (469, 'Bonsella Hotel Groups', 'null', 469, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (470, 'Bonzela', 'null', 470, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (471, 'Bookerator_C', 'null', 471, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (472, 'Booking Helpers_C', 'null', 472, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (473, 'Booking Lettings_C', 'null', 473, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (474, 'BookingTeam', 'null', 474, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (475, 'Boonchai Property', 'null', 475, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (476, 'Borges Chiado', 'null', 476, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (477, 'Boudl Hotels', 'null', 477, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (478, 'Bouganvillia Homes & Villas_C', 'null', 478, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (479, 'Boustead Hotels & Resorts', 'null', 479, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (480, 'Boutique Stays', 'null', 480, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (481, 'Brancom', 'null', 481, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (482, 'Brawa Groove Villa', 'null', 482, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (483, 'Breakfast_JP', 'null', 483, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (484, 'BreakPL Real Estate Consultancy', 'null', 484, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (485, 'Breezbay Hotel Group', 'null', 485, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (486, 'Bridal Tea House', 'null', 486, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (487, 'BridgeStreet', 'null', 487, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (488, 'Brighton Getaways Limited', 'null', 488, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (489, 'Brisas Hotels and Resorts', 'null', 489, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (490, 'Britannia Hotels', 'null', 490, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (491, 'BRO', 'null', 491, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (492, 'Bruces Hideout', 'null', 492, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (493, 'Bruny Island Accommodation Services', 'null', 493, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (494, 'Bruny Island Holiday Rentals', 'null', 494, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (495, 'Brustar', 'null', 495, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (496, 'BTP GROUP', 'null', 496, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (497, 'Bubica Zlatibor_C', 'null', 497, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (498, 'Budaraa', 'null', 498, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (499, 'Budget Motel Chain', 'null', 499, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (500, 'Budget Suites of America', 'null', 500, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (501, 'Budgetel', 'null', 501, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (502, 'Budi Susan Property', 'null', 502, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (503, 'Buenaventura Yape', 'null', 503, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (504, 'Buho Group', 'null', 504, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (505, 'Bui Vien Miss Home', 'null', 505, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (506, 'Bukit Vista', 'null', 506, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (507, 'Bunda Homestay', 'null', 507, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (508, 'Bundit properties', 'null', 508, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (509, 'Bunga Jabe Beach', 'null', 509, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (510, 'Bunk\'D Backpackers Villa', 'null', 510, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (511, 'Burj Holidays', 'null', 511, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (512, 'Butterfly Hospitality Group', 'null', 512, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (513, 'ButternutTreeTownhomes', 'null', 513, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (514, 'By Concierge', 'null', 514, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (515, 'Byblos Hotels', 'null', 515, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (516, 'Byron Bay Accom', 'null', 516, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (517, 'C Hotels', 'null', 517, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (518, 'Ca s\'amitger', 'null', 518, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (519, 'Cactus Space', 'null', 519, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (520, 'Caesar Park Hotels & Resorets Group', 'null', 520, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (521, 'Caesars Entertainment Corporation', 'null', 521, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (522, 'Café & Homestay 1986 Designer', 'null', 522, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (523, 'Calvin', 'null', 523, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (524, 'Camelia Bali Villa', 'null', 524, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (525, 'Camino Real Hotels', 'null', 525, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (526, 'CANDEO HOTELS', 'null', 526, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (527, 'Candra Group', 'null', 527, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (528, 'Cao Hang', 'null', 528, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (529, 'Cape & Kantary Hotels', 'null', 529, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (530, 'Cape Town Holiday Apartments', 'null', 530, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (531, 'Cape Town Life', 'null', 531, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (532, 'Capstone Hotels', 'null', 532, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (533, 'Carino Hotels and Resorts Worldwide', 'null', 533, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (534, 'Carla Group', 'null', 534, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (535, 'Carlton Hotel Mangement Group', 'null', 535, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (536, 'Casa Amore', 'null', 536, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (537, 'Casa Corsa_C', 'null', 537, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (538, 'Casa Floridian LLC', 'null', 538, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (539, 'Casa Melhor', 'null', 539, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (540, 'Casa Mitger_C', 'null', 540, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (541, 'casablanca homestay', 'null', 541, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (542, 'CasaGuest_C', 'null', 542, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (543, 'Casaterra', 'null', 543, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (544, 'Castle Resorts & Hotels', 'null', 544, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (545, 'CC House Khao Yai', 'null', 545, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (546, 'Ccresidences', 'null', 546, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (547, 'Cebu Rooms Condotels', 'null', 547, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (548, 'Cecylia Agustine', 'null', 548, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (549, 'Celebrity city hotel', 'null', 549, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (550, 'Celebrity Room', 'null', 550, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (551, 'Celestial Works', 'null', 551, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (552, 'Celyn Group of Hotels', 'null', 552, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (553, 'Centara Hotels & Resorts', 'null', 553, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (554, 'Centau Property', 'null', 554, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (555, 'Center Hotels', 'null', 555, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (556, 'Center Parcs', 'null', 556, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (557, 'Central Apartments & Hotels', 'null', 557, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (558, 'Central Hotels', 'null', 558, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (559, 'Central Stays', 'null', 559, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (560, 'Centre Point Hospitality', 'null', 560, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (561, 'Centro Hotels', 'null', 561, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (562, 'Centurion Hotels & Resorts', 'null', 562, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (563, 'Centurion Hotels International', 'null', 563, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (564, 'Century 21', 'null', 564, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (565, 'Chaaya Hotels & Resorts', 'null', 565, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (566, 'Chariton Hotel', 'null', 566, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (567, 'Charlie\'s Home', 'null', 567, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (568, 'Charming City Hotels Group', 'null', 568, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (569, 'Chateaux et Hotels Collection', 'null', 569, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (570, 'Chatra Property', 'null', 570, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (571, 'Chatrium Hotels and Residences', 'null', 571, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (572, 'Chattha', 'null', 572, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (573, 'Chaz Everitt Holiday Rentals', 'null', 573, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (574, 'Cherry Homes Residence', 'null', 574, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (575, 'Chez Bonbons', 'null', 575, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (576, 'Chez Hotels', 'null', 576, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (577, 'Chilling', 'null', 577, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (578, 'Chit Chat Café', 'null', 578, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (579, 'Chitpol', 'null', 579, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (580, 'CHM Hotels', 'null', 580, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (581, 'Choice Hotels', 'null', 581, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (582, 'Chrismar Hotels', 'null', 582, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (583, 'Chuan House', 'null', 583, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (584, 'Cibubur Village', 'null', 584, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (585, 'CibuburVillage', 'null', 585, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (586, 'Cijantung Residence', 'null', 586, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (587, 'Cilandak Residence', 'null', 587, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (588, 'Cinnamon Hotels and Resorts', 'null', 588, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (589, 'Cinque Terre Riviera', 'null', 589, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (590, 'Citihome', 'null', 590, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (591, 'Cititel Group', 'null', 591, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (592, 'Citra Prima', 'null', 592, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (593, 'Citradream Hotels', 'null', 593, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (594, 'Citrus Hotels', 'null', 594, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (595, 'Citrus Tree Holidays', 'null', 595, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (596, 'City 118', 'null', 596, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (597, 'City Apartment Saigon', 'null', 597, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (598, 'City Apartments Auckland', 'null', 598, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (599, 'City Edge', 'null', 599, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (600, 'City Express', 'null', 600, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (601, 'City House Saigon', 'null', 601, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (602, 'City Inn', 'null', 602, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (603, 'City Lights Apartment Group', 'null', 603, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (604, 'City Lodge', 'null', 604, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (605, 'City Premiere Group', 'null', 605, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (606, 'City Seasons Group of Hotels', 'null', 606, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (607, 'City Stay Hospitality', 'null', 607, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (608, 'City View', 'null', 608, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (609, 'Cityhotels', 'null', 609, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (610, 'Citymax Hotels', 'null', 610, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (611, 'Civitel Hotels & Resorts', 'null', 611, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (612, 'Clarks Group of Hotels', 'null', 612, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (613, 'Classic British Hotels', 'null', 613, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (614, 'Clavarly Gain', 'null', 614, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (615, 'Clearwater Samui', 'null', 615, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (616, 'Click&Flat', 'null', 616, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (617, 'Cloverph', 'null', 617, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (618, 'Club Balai Isabel Inc', 'null', 618, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (619, 'Club Hotels Israel', 'null', 619, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (620, 'Club Mahindra Holidays', 'null', 620, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (621, 'Club Med', 'null', 621, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (622, 'Club Quarters', 'null', 622, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (623, 'Coast and Country Getaways', 'null', 623, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (624, 'Coast Hotels & Resorts', 'null', 624, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (625, 'Coastal Properties Realty', 'null', 625, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (626, 'Coastbreakz Group', 'null', 626, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (627, 'Coastlands Hotel Group', 'null', 627, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (628, 'Coastlines International_C', 'null', 628, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (629, 'CoBnB', 'null', 629, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (630, 'Coco Stay', 'null', 630, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (631, 'Coconut Group', 'null', 631, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (632, 'CocoonR_C', 'null', 632, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (633, 'CocoRacco_JP', 'null', 633, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (634, 'Cocorolife_JP', 'null', 634, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (635, 'CoDE Hostels', 'null', 635, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (636, 'Cohaus.co', 'null', 636, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (637, 'Come Stay', 'null', 637, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (638, 'Comfort Hotel', 'null', 638, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (639, 'Comfy Clouds', 'null', 639, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (640, 'Commit', 'null', 640, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (641, 'Common Share', 'null', 641, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (642, 'Commundo Tagungshotels', 'null', 642, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (643, 'COMO Hotels and Resorts', 'null', 643, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (644, 'Company Vauban', 'null', 644, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (645, 'Compass Hospitality', 'null', 645, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (646, 'Concept Hospitality', 'null', 646, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (647, 'Condes/Espana/Monument', 'null', 647, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (648, 'Conlon & Co', 'null', 648, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (649, 'Constance Hotels Experience', 'null', 649, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (650, 'Container group', 'null', 650, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (651, 'Continental', 'null', 651, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (652, 'Cool Bali Villas', 'null', 652, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (653, 'CoolRooms', 'null', 653, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (654, 'Copenhagen Hospitality_C', 'null', 654, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (655, 'Coral Hotels & Resorts', 'null', 655, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (656, 'CORE', 'null', 656, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (657, 'Corinthia Hotels International', 'null', 657, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (658, 'Cornel Home', 'null', 658, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (659, 'Corus', 'null', 659, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (660, 'Cosmo Bridge', 'null', 660, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (661, 'Cosmopolitan', 'null', 661, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (662, 'Cosmos Hotel Management', 'null', 662, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (663, 'Cosmos Hotel& Resorts', 'null', 663, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (664, 'Costay', 'null', 664, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (665, 'Cottesloe Beach House Stays', 'null', 665, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (666, 'Country Garden Phoenix', 'null', 666, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (667, 'Court Hotels', 'null', 667, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (668, 'Cozrum Group', 'null', 668, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (669, 'Cozy Bali Holiday', 'null', 669, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (670, 'Cozy House', 'null', 670, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (671, 'Cozy Palm', 'null', 671, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (672, 'Cozy Property_C', 'null', 672, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (673, 'CP', 'null', 673, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (674, 'CPG Hotels', 'null', 674, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (675, 'CPI Hotels', 'null', 675, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (676, 'CPM', 'null', 676, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (677, 'Crazy Bee JP', 'null', 677, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (678, 'Creasion', 'null', 678, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (679, 'Crerar Hotels', 'null', 679, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (680, 'Cresta Hotels', 'null', 680, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (681, 'Crete Villas 4 U', 'null', 681, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (682, 'Cristina Dela Cruz House Rental', 'null', 682, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (683, 'Cristina L. Dela Cruz House Rental', 'null', 683, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (684, 'Cross hotels and resorts', 'null', 684, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (685, 'Cross Life', 'null', 685, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (686, 'Cross One', 'null', 686, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (687, 'Crossroad', 'null', 687, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (688, 'Crown & Champa Resorts', 'null', 688, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (689, 'Crown Regency Hotels & Resorts', 'null', 689, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (690, 'Crystal Crown', 'null', 690, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (691, 'Crystal Hotels', 'null', 691, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (692, 'Crystalbrook Collection', 'null', 692, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (693, 'CS corporation (HQ Coto)', 'null', 693, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (694, 'CSJ', 'null', 694, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (695, 'Cube', 'null', 695, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (696, 'Cube Hotels Group', 'null', 696, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (697, 'CY House', 'null', 697, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (698, 'Cynthia', 'null', 698, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (699, 'Cyprus Holiday Group_C', 'null', 699, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (700, 'D Collection', 'null', 700, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (701, 'D Lin Property', 'null', 701, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (702, 'D2 Villas', 'null', 707, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (703, 'Daebakinae', 'null', 708, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (704, 'Dafam Hotels', 'null', 709, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (705, 'Daiichi Hotel Group', 'null', 710, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (706, 'Dailyflats', 'null', 711, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (707, 'Daisen', 'null', 712, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (708, 'Daiwa Royal Hotel', 'null', 713, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (709, 'Daiwa Roynet Hotels', 'null', 714, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (710, 'Dalata Hotel Group', 'null', 715, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (711, 'Dalia May', 'null', 716, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (712, 'DAMAC Group', 'null', 717, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (713, 'Damar Emas', 'null', 718, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (714, 'Dan Hotels', 'null', 719, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (715, 'Danat Hotel & Resorts', 'null', 720, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (716, 'Dandeli', 'null', 721, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (717, 'Dandori', 'null', 722, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (718, 'Dang Nguyen Da Lat', 'null', 723, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (719, 'Dani Sulistyo Properties', 'null', 724, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (720, 'Danubius Hotels Group', 'null', 725, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (721, 'Dar Al Eiman Group', 'null', 726, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (722, 'Darayu Bali', 'null', 727, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (723, 'Das Wohnhause Property', 'null', 728, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (724, 'Dasiri Group', 'null', 729, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (725, 'Daylesford Accommodation Escapes', 'null', 730, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (726, 'Days Apartment', 'null', 731, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (727, 'Dazzle Sukhumvit 7', 'null', 732, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (728, 'D\'Cepeh', 'null', 702, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (729, 'De Art', 'null', 733, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (730, 'De Hostel', 'null', 734, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (731, 'De Inn', 'null', 735, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (732, 'De Kraal Estate', 'null', 736, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (733, 'De palma', 'null', 737, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (734, 'de Prisha', 'null', 738, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (735, 'De Tropen Jogja', 'null', 739, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (736, 'De Uptown', 'null', 740, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (737, 'Dealchaser Da Nang', 'null', 741, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (738, 'Debali Villas', 'null', 742, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (739, 'Dedeman Hotels', 'null', 743, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (740, 'Dedy Sandiarsa', 'null', 744, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (741, 'Deefly Hotels & Resorts', 'null', 745, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (742, 'Delaware North Companies Australia & New Zealand', 'null', 746, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (743, 'Delfos Group', 'null', 747, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (744, 'Della Silviana', 'null', 748, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (745, 'Delta Hotels & Resort', 'null', 749, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (746, 'Deluca Homes', 'null', 750, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (747, 'Deluxe Holiday Homes', 'null', 751, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (748, 'Demanus', 'null', 752, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (749, 'Denihan Hospitality Group (DHG)', 'null', 753, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (750, 'Derby Hotels Collection', 'null', 754, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (751, 'Deris Clarke', 'null', 755, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (752, 'Desa Bahasa Borobudur', 'null', 756, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (753, 'Desa Kemiren', 'null', 757, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (754, 'Desa Penglipuran', 'null', 758, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (755, 'Desa Wisata Gabugan', 'null', 759, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (756, 'Desa Wisata Kembang Arum', 'null', 760, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (757, 'Desa Wisata Pentingsari', 'null', 761, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (758, 'Design Hotels', 'null', 762, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (759, 'Design Suites Miami_C', 'null', 763, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (760, 'DesignerStay_C', 'null', 764, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (761, 'Destada Properties', 'null', 765, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (762, 'Destination Hotels & Resorts', 'null', 766, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (763, 'Destiny Student', 'null', 767, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (764, 'Deutsche Hospitality', 'null', 768, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (765, 'Devara Pool Villa', 'null', 769, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (766, 'Device Agency', 'null', 770, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (767, 'Deville Hotels', 'null', 771, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (768, 'Dewa Made Widiana', 'null', 772, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (769, 'Dharma Gita Guest House', 'null', 773, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (770, 'Dhevatara', 'null', 774, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (771, 'DISCOVERY HOMESTAY', 'null', 790, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (772, 'Di Bandung Villa', 'null', 775, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (773, 'Diamond Apartments', 'null', 776, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (774, 'Diamond Resort Europe', 'null', 777, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (775, 'Diamond Resorts International', 'null', 778, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (776, 'Diamond Suite Group', 'null', 779, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (777, 'Diamond Villa', 'null', 780, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (778, 'Diana Group', 'null', 781, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (779, 'Diana\'s Guest House', 'null', 782, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (780, 'Dicky Andrian', 'null', 783, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (781, 'Dieng Kledung Pass', 'null', 784, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (782, 'Dimentiondots', 'null', 785, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (783, 'Dio Rama', 'null', 786, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (784, 'Dipro', 'null', 787, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (785, 'Direct Hotels', 'null', 788, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (786, 'Discovery Holiday Parks', 'null', 789, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (787, 'Discovery Hotels & Resorts', 'null', 791, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (788, 'Distinction Hotels NZ', 'null', 792, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (789, 'District One', 'null', 793, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (790, 'Dita Rent Apartment', 'null', 794, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (791, 'Divan Hotels', 'null', 795, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (792, 'Diyar Group', 'null', 796, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (793, 'Diyar Villas', 'null', 797, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (794, 'Djitu Hospitality', 'null', 798, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (795, 'Djitu Hospitality Solution', 'null', 799, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (796, 'Djohan Anggono', 'null', 800, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (797, 'DKost Baloi', 'null', 801, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (798, 'DMARC CONDOS', 'null', 802, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (799, 'DNU', 'null', 803, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (800, 'Do it Consulting', 'null', 804, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (801, 'DO NOT USE', 'null', 805, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (802, 'Do not use', 'null', 806, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (803, 'd\'Oasis', 'null', 703, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (804, 'Dolce Hotels and Resorts', 'null', 807, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (805, 'Doma Hotels', 'null', 808, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (806, 'Domo Management', 'null', 809, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (807, 'Domohotel_JP', 'null', 810, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (808, 'Dorint Hotels & Resorts', 'null', 811, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (809, 'DORMERO', 'null', 812, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (810, 'Dormitels PH', 'null', 813, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (811, 'Dormy Inn', 'null', 814, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (812, 'Dorsett Hospitality International', 'null', 815, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (813, 'Dossen', 'null', 816, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (814, 'Dot', 'null', 817, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (815, 'DownSouth Holiday Homes', 'null', 818, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (816, 'D\'Paragon', 'null', 704, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (817, 'D\'Phoenix', 'null', 705, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (818, 'd\'primahotel', 'null', 706, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (819, 'Dream Algarve_C', 'null', 819, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (820, 'Dream Apartments', 'null', 820, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (821, 'Dream Espana_C', 'null', 821, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (822, 'Dream Hotels & Resorts', 'null', 822, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (823, 'Dream House', 'null', 823, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (824, 'Dream Inn Holiday Homes', 'null', 824, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (825, 'Dream Property Samui', 'null', 825, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (826, 'DreamInn', 'null', 826, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (827, 'Dreamtime Resorts', 'null', 827, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (828, 'Driven Properties', 'null', 828, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (829, 'Drury Hotels', 'null', 829, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (830, 'DT Hotelier.org', 'null', 830, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (831, 'Dubai Apartments', 'null', 831, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (832, 'Dubai Grand Hotel by fortune', 'null', 832, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (833, 'Dubai Stay', 'null', 833, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (834, 'Dunes Express Inn', 'null', 834, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (835, 'Dunsborough Holiday Homes', 'null', 835, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (836, 'Duri Residence Group', 'null', 836, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (837, 'Dusit Hotels and Resorts', 'null', 837, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (838, 'Dusun Ubud Property', 'null', 838, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (839, 'E&T Holiday Homes', 'null', 840, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (840, 'Ease by Emaar', 'null', 841, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (841, 'Eastin Hotels', 'null', 842, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (842, 'Eastiny Hotel Group', 'null', 843, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (843, 'Easy BnB', 'null', 844, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (844, 'Easy Busy Guest House', 'null', 845, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (845, 'Easy Go', 'null', 846, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (846, 'easyHotel', 'null', 847, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (847, 'Eaton Hotels', 'null', 848, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (848, 'Eats and Retreats', 'null', 849, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (849, 'ECFA Hotels Group', 'null', 850, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (850, 'EchoDom', 'null', 851, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (851, 'ECJ Suites @ Horizons 101 Condo', 'null', 852, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (852, 'Eclectic Villas', 'null', 853, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (853, 'Eco Hotels', 'null', 854, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (854, 'Ecohomz', 'null', 855, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (855, 'ECS', 'null', 856, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (856, 'Eddy\'s Place & Travel Tours', 'null', 857, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (857, 'Eden Holiday Rentals', 'null', 858, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (858, 'Eden Hotel Group', 'null', 859, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (859, 'Eden Pelayo', 'null', 860, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (860, 'Edinburgh Collection', 'null', 861, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (861, 'Eever', 'null', 862, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (862, 'Ejoysleep', 'null', 863, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (863, 'Eki Mambrasar', 'null', 864, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (864, 'Ekka Baan', 'null', 865, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (865, 'El Cid Resorts', 'null', 866, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (866, 'EL Cielito Hotels', 'null', 867, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (867, 'El Luxury', 'null', 868, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (868, 'EL Nido Resorts', 'null', 869, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (869, 'Elaf Group', 'null', 870, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (870, 'Electus Home', 'null', 871, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (871, 'Elegancia Hotels', 'null', 872, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (872, 'Element Escapes', 'null', 873, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (873, 'Elite Club Vacanze', 'null', 874, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (874, 'Elite Estates Bali', 'null', 875, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (875, 'Elite Group', 'null', 876, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (876, 'Elite Havens', 'null', 877, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (877, 'Elite Heaven ( Non NHA)', 'null', 878, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (878, 'Elite Holiday Homes', 'null', 879, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (879, 'Elite Hospitality Group', 'null', 880, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (880, 'Elite Island Resorts', 'null', 881, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (881, 'Elite Virtual Brokerage_C', 'null', 882, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (882, 'Elite World Hotels', 'null', 883, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (883, 'ELMCAPITAL_JP', 'null', 884, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (884, 'Elmira Group Apartment', 'null', 885, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (885, 'Emaar Hospitality', 'null', 886, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (886, 'Embrace Place', 'null', 887, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (887, 'Empire Group', 'null', 888, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (888, 'Empire Hotel Group', 'null', 889, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (889, 'Emz Apartments', 'null', 890, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (890, 'Enagazalble', 'null', 891, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (891, 'Enchanted garden', 'null', 892, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (892, 'Encore Reunion_C', 'null', 893, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (893, 'Enderun Hotels', 'null', 894, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (894, 'Endo Chendiawan', 'null', 895, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (895, 'Endo Michi', 'null', 896, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (896, 'Enno Apartment', 'null', 897, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (897, 'Epiphany_JP', 'null', 898, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (898, 'Eppley Hotel Company', 'null', 899, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (899, 'Equinoxe', 'null', 900, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (900, 'ErdiGroup', 'null', 901, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (901, 'E-Red Hotel', 'null', 839, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (902, 'Eresin Hotels', 'null', 902, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (903, 'Erik Vokel Apartments', 'null', 903, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (904, 'ESA Serviced Apartments', 'null', 905, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (905, 'Escala Condominium Cebu', 'null', 906, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (906, 'ESCEMO', 'null', 907, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (907, 'Espace Holiday Homes', 'null', 908, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (908, 'Espahotels', 'null', 909, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (909, 'Espai Barcelona_C', 'null', 910, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (910, 'Espresso Apartments', 'null', 911, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (911, 'Esprit de France', 'null', 912, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (912, 'Es-sense', 'null', 904, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (913, 'Estate One', 'null', 913, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (914, 'Estay YJ Baoli Yintan', 'null', 914, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (915, 'Estelar', 'null', 915, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (916, 'Eton Collection', 'null', 916, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (917, 'Etrebe Holdings', 'null', 917, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (918, 'Euro Hotels', 'null', 918, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (919, 'Eurohotel', 'null', 919, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (920, 'Europea Luxury Residences', 'null', 920, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (921, 'European Hotels Private Collection', 'null', 921, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (922, 'Eurotel', 'null', 922, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (923, 'EV world', 'null', 923, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (924, 'Evaco Holiday Resorts', 'null', 924, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (925, 'Evenia Hotels', 'null', 925, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (926, 'Everly Group', 'null', 926, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (927, 'Evy Handayani', 'null', 927, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (928, 'EXCELLENCE GROUP LUXURY HOTELS & RESORTS', 'null', 928, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (929, 'Exclusive Hotels', 'null', 929, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (930, 'Exe Hotels', 'null', 930, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (931, 'Executive Villas Florida', 'null', 931, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (932, 'Exon', 'null', 932, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (933, 'Exotic Hideaway', 'null', 933, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (934, 'Expo & Torre', 'null', 934, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (935, 'Express Holiday Homes', 'null', 935, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (936, 'Extended Stay America', 'null', 936, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (937, 'Extraordinary', 'null', 937, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (938, 'F Home Danang', 'null', 938, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (939, 'FabHotels', 'null', 940, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (940, 'Fahrenheit', 'null', 941, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (941, 'Faircity Hotels', 'null', 942, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (942, 'Fairmont Raffles Hotels International', 'null', 943, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (943, 'Fairyland Hotels', 'null', 944, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (944, 'Fajr Group', 'null', 945, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (945, 'Fame Villa', 'null', 946, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (946, 'Faminect', 'null', 947, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (947, 'Fanqielaile', 'null', 948, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (948, 'FanssMoreJapan', 'null', 949, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (949, 'Far East Hospitality', 'null', 950, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (950, 'Far East Serviced Residences', 'null', 951, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (951, 'Far Home', 'null', 952, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (952, 'Farway Homes', 'null', 953, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (953, 'Fassbind Hotels', 'null', 954, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (954, 'Fathurrahman Imran', 'null', 955, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (955, 'FATMAWATI', 'null', 956, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (956, 'Fatmawati Group', 'null', 957, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (957, 'Fattal Hotels', 'null', 958, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (958, 'FAU', 'null', 959, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (959, 'Favstay', 'null', 960, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (960, 'Favstay_Local', 'null', 961, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (961, 'Faye & Lowell', 'null', 962, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (962, 'FC Sotetsu Fresa Inn', 'null', 963, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (963, 'FCAC_JP', 'null', 964, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (964, 'Febow', 'null', 965, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (965, 'Federal Group', 'null', 966, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (966, 'Feel Great Condotels', 'null', 967, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (967, 'Feel Japan', 'null', 968, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (968, 'Felda Residences', 'null', 969, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (969, 'Felice Group', 'null', 970, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (970, 'Felipe Martinval', 'null', 971, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (971, 'Feng Jia Window', 'null', 972, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (972, 'Feng Xi Property', 'null', 973, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (973, 'Fengchia Europe', 'null', 974, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (974, 'Fengchia Love Heart', 'null', 975, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (975, 'Fengchia Random', 'null', 976, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (976, 'Fenix Hotels', 'null', 977, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (977, 'FERGUS', 'null', 978, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (978, 'Fersal Hotel Group', 'null', 979, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (979, 'Festiva Resorts', 'null', 980, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (980, 'Festival', 'null', 981, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (981, 'fforward', 'null', 982, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (982, 'FG Properties', 'null', 983, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (983, 'Fiesta hotels', 'null', 984, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (984, 'Firmdale Hotels', 'null', 985, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (985, 'First Cabin', 'null', 986, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (986, 'Fiz Apartment', 'null', 987, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (987, 'Flamingo ShortLets', 'null', 988, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (988, 'Flatguest_C', 'null', 989, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (989, 'Flatotel Hotels', 'null', 990, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (990, 'Flats 4 U Moscow', 'null', 991, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (991, 'Flatsaway_C', 'null', 992, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (992, 'Fletcher Hotels', 'null', 993, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (993, 'Flexstay Hotel Management', 'null', 994, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (994, 'Flora Hospitality', 'null', 995, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (995, 'Florida Star Vacations_C', 'null', 996, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (996, 'Floris Hotel Collection', 'null', 997, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (997, 'Flow America Homes_C', 'null', 998, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (998, 'Flow Art', 'null', 999, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (999, 'Flower Group Of Hotels', 'null', 1000, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1000, 'Focus Hotels', 'null', 1001, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1001, 'Foliday of Fosun Group', 'null', 1002, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1002, 'Fontainebleau Resorts', 'null', 1003, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1003, 'Fontana Residencia', 'null', 1004, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1004, 'Forestdale Hotels', 'null', 1005, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1005, 'Fortune Group', 'null', 1006, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1006, 'Fortune Group of Hotels', 'null', 1007, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1007, 'Fortune Park Hotels', 'null', 1008, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1008, 'Four Seasons Hotels and Resorts', 'null', 1009, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1009, 'FPD Global Integrated Services Inc.', 'null', 1010, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1010, 'F-Plus', 'null', 939, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1011, 'Fragrance Hotel', 'null', 1011, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1012, 'Framework Family', 'null', 1012, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1013, 'France Patrimoine', 'null', 1013, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1014, 'Francesco Property', 'null', 1014, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1015, 'Frank Porter_C', 'null', 1015, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1016, 'Franklin Ginanjar', 'null', 1016, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1017, 'Franko', 'null', 1017, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1018, 'Fraser Hospitality', 'null', 1018, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1019, 'FreeSpirit Holiday Parks', 'null', 1019, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1020, 'Friendly & Alex Group Co., Ltd', 'null', 1020, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1021, 'Frost Villas', 'null', 1021, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1022, 'Fu Yuning Property', 'null', 1022, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1023, 'Fujinaga', 'null', 1023, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1024, 'Fujita Kanko Inc Hotels & Resorts', 'null', 1024, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1025, 'fukuoka f connect', 'null', 1025, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1026, 'Fukuoka Hotel', 'null', 1026, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1027, 'Fukuoka Properties', 'null', 1027, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1028, 'Fulcrum Lettings Management (Thailand) Co., Ltd', 'null', 1028, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1029, 'Full House Homestay Saigon', 'null', 1029, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1030, 'Full Rooms Phuket', 'null', 1030, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1031, 'Fulmar Residence Group', 'null', 1031, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1032, 'Fun_world_RealEstate', 'null', 1032, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1033, 'Funkey', 'null', 1033, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1034, 'Furama Hotels International', 'null', 1034, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1035, 'Furnished Properties', 'null', 1035, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1036, 'FX Hotels Group', 'null', 1036, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1037, 'G Feel', 'null', 1037, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1038, 'G1 Holiday Properties', 'null', 1038, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1039, 'G7 Residence', 'null', 1039, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1040, 'Gacayan Condotel', 'null', 1040, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1041, 'Gading Nias', 'null', 1041, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1042, 'Galactic', 'null', 1042, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1043, 'Galaxi Group', 'null', 1043, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1044, 'Galaxy Entertainment', 'null', 1044, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1045, 'Gama Hotels', 'null', 1045, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1046, 'Gandhi Hospitality Management', 'null', 1046, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1047, 'Gangnam Best View', 'null', 1047, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1048, 'Garden Palace Hotels', 'null', 1048, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1049, 'Garden Studio', 'null', 1049, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1050, 'Gardena Agency', 'null', 1050, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1051, 'Gargallo', 'null', 1051, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1052, 'Garnet', 'null', 1052, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1053, 'Gaudi Group', 'null', 1053, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1054, 'Gaylord Hotels', 'null', 1054, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1055, 'Gaynorch Property', 'null', 1055, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1056, 'GCH Hotel Group', 'null', 1056, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1057, 'Gcloud', 'null', 1057, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1058, 'Gemini Hospitality', 'null', 1058, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1059, 'Gemtrad', 'null', 1059, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1060, 'Generator Hotel and Hostel', 'null', 1060, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1061, 'Genesis', 'null', 1061, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1062, 'Gentong Kost', 'null', 1062, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1063, 'GerberaHome', 'null', 1063, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1064, 'Getaway Merimbula', 'null', 1064, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1065, 'Getaways SA', 'null', 1065, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1066, 'GetLavanda', 'null', 1066, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1067, 'Giang Tran', 'null', 1067, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1068, 'Giannoulis Hotels and Resorts', 'null', 1068, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1069, 'Gilles Mangin', 'null', 1069, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1070, 'Ginger Hotels', 'null', 1070, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1071, 'Gita Ayu Villa', 'null', 1071, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1072, 'Glacier Park Company', 'null', 1072, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1073, 'GLI (Global Live Investment Inc.)', 'null', 1073, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1074, 'Global Coms Japan', 'null', 1074, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1075, 'Global International Hotel', 'null', 1075, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1076, 'Global Top Group', 'null', 1076, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1077, 'Global Villas', 'null', 1077, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1078, 'Gloria Hotels & Resorts', 'null', 1078, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1079, 'GO Hotels', 'null', 1079, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1080, 'GODOGAISYA JESSICA', 'null', 1080, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1081, 'Gojo Paradiso', 'null', 1081, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1082, 'Goki', 'null', 1082, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1083, 'Gold Coast Holiday Houses', 'null', 1083, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1084, 'Gold Coast Holiday Rentals', 'null', 1084, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1085, 'Gold Coast Luxury Resorts', 'null', 1085, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1086, 'Gold Group Hotels', 'null', 1086, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1087, 'Golden Chain', 'null', 1087, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1088, 'Golden Leaf Hotels & Residences', 'null', 1088, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1089, 'Golden Roof Group', 'null', 1089, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1090, 'Golden Stay Dubai', 'null', 1090, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1091, 'Good Choi', 'null', 1091, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1092, 'Good Night Inn', 'null', 1092, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1093, 'Good Rest Property', 'null', 1093, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1094, 'Good Smile Nico', 'null', 1094, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1095, 'Good Stay', 'null', 1095, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1096, 'Goodday Kyoto', 'null', 1096, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1097, 'Gooderson Leisure', 'null', 1097, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1098, 'GoodHope', 'null', 1098, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1099, 'Goodstay Hotel', 'null', 1099, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1100, 'Gorgeou Group Accommodation', 'null', 1100, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1101, 'Grace Cup by Compass', 'null', 1101, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1102, 'Graduate Hotels', 'null', 1102, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1103, 'Graha Pande Residence', 'null', 1103, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1104, 'Graha Pastika', 'null', 1104, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1105, 'Gran Prix Hotels', 'null', 1105, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1106, 'Grand Hotels International (GHI)', 'null', 1106, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1107, 'Grand Isabella Residences', 'null', 1107, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1108, 'Grand Khalifah Guesthouse', 'null', 1108, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1109, 'Grand Seaview Realty & Dev\'t Corp', 'null', 1109, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1110, 'Grand Skylight Hotels', 'null', 1110, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1111, 'Grandboutique Inn Pro', 'null', 1111, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1112, 'Grandouce_JP', 'null', 1112, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1113, 'Grandroomservices', 'null', 1113, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1114, 'Grange Hotels', 'null', 1114, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1115, 'Granvista Hotels & Resorts', 'null', 1115, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1116, 'Grapp', 'null', 1116, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1117, 'Great Hotels of the World', 'null', 1117, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1118, 'Great Southern Hotels', 'null', 1118, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1119, 'Great Stay', 'null', 1119, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1120, 'Great Wolf Resorts', 'null', 1120, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1121, 'GreatVillas Asia', 'null', 1121, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1122, 'Green Future', 'null', 1122, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1123, 'Green Homestay', 'null', 1123, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1124, 'Green Lake View Apartments', 'null', 1124, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1125, 'Green Pramuka City', 'null', 1125, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1126, 'Green Pramuka City by Novi', 'null', 1126, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1127, 'Green Rich Hotels', 'null', 1127, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1128, 'Green Star', 'null', 1128, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1129, 'Green Tree Inns', 'null', 1129, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1130, 'Greenland International Hotels Group', 'null', 1130, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1131, 'GreenLine Hotels', 'null', 1131, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1132, 'Greens Hotels', 'null', 1132, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1133, 'Griya Gribig', 'null', 1133, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1134, 'Griya Lidya', 'null', 1134, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1135, 'Griya Malioboro', 'null', 1135, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1136, 'Griya Syariah', 'null', 1136, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1137, 'Group -Liberty', 'null', 1137, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1138, 'Groupe Frontenac', 'null', 1138, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1139, 'Groupe Sogepar', 'null', 1139, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1140, 'GROUPO PINERO', 'null', 1140, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1141, 'GRT Hotels & Resorts', 'null', 1141, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1142, 'GRUPO HOTELERO SANTA FE', 'null', 1142, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1143, 'Grupo Roisa', 'null', 1143, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1144, 'Grupotel', 'null', 1144, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1145, 'Gruppo Trevi', 'null', 1145, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1146, 'Guangzhou Merci bnb', 'null', 1146, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1147, 'Guest Easy', 'null', 1147, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1148, 'Guest House Hata Group JP', 'null', 1148, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1149, 'Guest House Miss Anis', 'null', 1149, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1150, 'Guest Houser_C', 'null', 1150, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1151, 'Guest Ready_C', 'null', 1151, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1152, 'GuestHouse KOTO Fushimi Inari', 'null', 1152, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1153, 'Gulf Hotels Group', 'null', 1153, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1154, 'Gusde House', 'null', 1154, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1155, 'Gusman', 'null', 1155, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1156, 'Guvon Hotels', 'null', 1156, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1157, 'Guzman', 'null', 1157, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1158, 'GV Hotels', 'null', 1158, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1159, 'Gyza Pro', 'null', 1159, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1160, 'H Boutique', 'null', 1160, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1161, 'H hotel', 'null', 1161, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1162, 'H Residence', 'null', 1162, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1163, 'H10 Hotels', 'null', 1165, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1164, 'Ha Thao', 'null', 1166, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1165, 'Habitat Apartments', 'null', 1167, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1166, 'Habitat Italy_C', 'null', 1168, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1167, 'Hacienda', 'null', 1169, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1168, 'Haka Tourism Group', 'null', 1170, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1169, 'Hakone Ichinoyu Group', 'null', 1171, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1170, 'Halldis_C', 'null', 1172, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1171, 'Hallmark Hotels', 'null', 1173, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1172, 'Hamdan Jayadi', 'null', 1174, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1173, 'Hamilton Island Enterprises', 'null', 1175, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1174, 'Hanafubuki', 'null', 1176, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1175, 'Handgrowing', 'null', 1177, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1176, 'Handys', 'null', 1178, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1177, 'Hanging Kite Apartments', 'null', 1179, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1178, 'Hankyu-Hanshin-Daiichi Hotel Group', 'null', 1180, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1179, 'Hanmadan', 'null', 1181, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1180, 'Hans Group', 'null', 1182, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1181, 'Hansen Gateway', 'null', 1183, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1182, 'Hanting Inns & Hotels', 'null', 1184, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1183, 'Happy 8 Retreat Group', 'null', 1185, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1184, 'Happy Hoildays', 'null', 1186, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1185, 'Happy People', 'null', 1187, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1186, 'Happy Place Condo', 'null', 1188, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1187, 'Happy Rooms_JP', 'null', 1189, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1188, 'Happy Tokyo Office', 'null', 1190, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1189, 'Happy Zleepy', 'null', 1191, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1190, 'Harbour Plaza Hotels & Resorts', 'null', 1192, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1191, 'Hardage Group', 'null', 1193, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1192, 'Harjinder Property', 'null', 1194, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1193, 'Harlequin Hotels & Resorts', 'null', 1195, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1194, 'HAS', 'null', 1196, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1195, 'Hase', 'null', 1197, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1196, 'Hatena Solutions', 'null', 1198, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1197, 'Hayatudin', 'null', 1199, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1198, 'H-Bridge_JP', 'null', 1163, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1199, 'HCC Hotels', 'null', 1200, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1200, 'Heartland Inn', 'null', 1201, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1201, 'Hearton Hotel', 'null', 1202, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1202, 'Heide Emigre Thailand', 'null', 1203, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1203, 'Heiwa Fukushi Yukokan', 'null', 1204, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1204, 'Hello Aparments', 'null', 1205, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1205, 'Hello Apartments New York', 'null', 1206, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1206, 'Hello Guest_C', 'null', 1207, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1207, 'Helmsley & Harley Hotels', 'null', 1208, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1208, 'Help2Rent', 'null', 1209, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1209, 'Henann Group of Resorts', 'null', 1210, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1210, 'Hendra Management', 'null', 1211, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1211, 'Hendry Pro', 'null', 1212, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1212, 'Hengky Cendana', 'null', 1213, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1213, 'Henn na Hotel', 'null', 1214, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1214, 'Henry\'s Apartment', 'null', 1215, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1215, 'Hercules Property', 'null', 1216, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1216, 'Here House', 'null', 1217, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1217, 'Heritage Collection', 'null', 1218, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1218, 'Heritage Group', 'null', 1219, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1219, 'Heritage Hotels (New Zealand)', 'null', 1220, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1220, 'Herla', 'null', 1221, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1221, 'Hesperia Hotels & Resorts', 'null', 1222, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1222, 'HeyMi', 'null', 1223, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1223, 'Hh GUEST HOUSE', 'null', 1224, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1224, 'HIG Group', 'null', 1229, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1225, 'HIM Hospitality', 'null', 1235, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1226, 'Hi5 Apartments', 'null', 1225, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1227, 'Hide Out Okinawa', 'null', 1226, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1228, 'Hien Thao TA', 'null', 1227, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1229, 'Hifumi', 'null', 1228, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1230, 'High Tech Hoteles', 'null', 1230, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1231, 'Highgate Hotels', 'null', 1231, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1232, 'HiGuests', 'null', 1232, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1233, 'hiii-Homtel', 'null', 1233, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1234, 'Hilton Worldwide', 'null', 1234, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1235, 'Hintown_C', 'null', 1236, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1236, 'Hiro and Saki JP', 'null', 1237, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1237, 'Historic Hotels of Europe', 'null', 1238, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1238, 'HK CTS Hotels', 'null', 1239, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1239, 'HK Hotels', 'null', 1240, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1240, 'HKG-Connectivity-test Chain', 'null', 1241, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1241, 'HM Hotels', 'null', 1242, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1242, 'HMH Hotel Group', 'null', 1243, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1243, 'HMI Hotel Group', 'null', 1244, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1244, 'Hoa Tran', 'null', 1245, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1245, 'Hoang kim Group', 'null', 1246, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1246, 'Hoang Son', 'null', 1247, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1247, 'Hoasun', 'null', 1248, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1248, 'HOC Apartment Thailand', 'null', 1249, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1249, 'Hokkaido Jutaku Shukuhaku', 'null', 1250, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1250, 'Hokkaido Style', 'null', 1251, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1251, 'Holiday Accommodation Pty Ltd', 'null', 1253, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1252, 'Holiday Great Ocean Road', 'null', 1254, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1253, 'Holiday Homes QLD', 'null', 1255, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1254, 'Holiday Hotels & Resorts', 'null', 1256, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1255, 'Holiday Nelson', 'null', 1257, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1256, 'Holiday Rentals', 'null', 1258, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1257, 'Holiday Rentals Pattaya', 'null', 1259, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1258, 'Holiday Velvet', 'null', 1260, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1259, 'Holiday Villa', 'null', 1261, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1260, 'Holiday Villa Hotels & Resorts', 'null', 1262, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1261, 'Holiday Villa Network', 'null', 1263, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1262, 'Holiday Villa Thailand Co., Ltd', 'null', 1264, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1263, 'Holidays2Malaga', 'null', 1265, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1264, 'HoliHouse_C', 'null', 1266, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1265, 'Holi-Rent', 'null', 1252, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1266, 'Hollyear Hotels', 'null', 1267, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1267, 'Holy Cow Phuket_C', 'null', 1268, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1268, 'Holyguest - Luxury rentals_C', 'null', 1269, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1269, 'Holyguest_C', 'null', 1270, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1270, 'Homayoon Villa', 'null', 1271, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1271, 'Home Away From Home', 'null', 1272, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1272, 'Home Connect_C', 'null', 1273, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1273, 'Home From Home', 'null', 1274, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1274, 'Home from Home_C', 'null', 1275, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1275, 'Home Inn', 'null', 1276, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1276, 'Home n Fun', 'null', 1277, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1277, 'Home Peace Home', 'null', 1278, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1278, 'Home Select', 'null', 1279, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1279, 'Home Sweet Home Samui', 'null', 1280, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1280, 'Homearound', 'null', 1281, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1281, 'HomeHost Accommodation Group', 'null', 1282, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1282, 'Homes In Hua Hin', 'null', 1283, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1283, 'Homes in India', 'null', 1284, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1284, 'Homes Livemax', 'null', 1285, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1285, 'Homes Local Agencies', 'null', 1286, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1286, 'Homestay Baguio', 'null', 1287, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1287, 'Homestay Nagoya Hill', 'null', 1288, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1288, 'Homestay Seaview Vung Tau', 'null', 1289, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1289, 'Homestay Tembi', 'null', 1290, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1290, 'Hometel', 'null', 1291, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1291, 'Hometel Hanoi', 'null', 1292, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1292, 'Homie', 'null', 1293, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1293, 'Homie Homestay&Villa', 'null', 1294, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1294, 'Homing Place', 'null', 1295, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1295, 'Homy Group', 'null', 1296, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1296, 'HON MAN T.', 'null', 1297, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1297, 'Hong Lan TA', 'null', 1298, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1298, 'Hong Minh', 'null', 1299, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1299, 'Hoostia', 'null', 1300, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1300, 'HOP INN Hotels', 'null', 1301, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1301, 'Hori Takeo', 'null', 1302, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1302, 'Horison Hotels Group', 'null', 1303, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1303, 'Horizon Homes Samui', 'null', 1304, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1304, 'Hoshino Resort', 'null', 1305, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1305, 'Hospes', 'null', 1306, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1306, 'Hospitality Innovators Inc', 'null', 1307, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1307, 'Hospitality Operations', 'null', 1308, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1308, 'Hospo Alliance', 'null', 1309, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1309, 'Host and Lodger', 'null', 1310, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1310, 'Host Helper', 'null', 1311, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1311, 'Host Kick_C', 'null', 1312, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1312, 'Host Maker_C', 'null', 1313, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1313, 'Hostal Live', 'null', 1314, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1314, 'Hostals Castilla', 'null', 1315, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1315, 'HostAStay', 'null', 1316, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1316, 'Hostel Furoya', 'null', 1317, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1317, 'Hostel One', 'null', 1318, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1318, 'Hostemplo', 'null', 1319, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1319, 'Hostmaker_C', 'null', 1320, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1320, 'Hotel 1-2-3', 'null', 1321, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1321, 'Hotel 81', 'null', 1322, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1322, 'Hotel 88', 'null', 1323, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1323, 'Hotel 99', 'null', 1324, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1324, 'Hotel Areaone', 'null', 1325, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1325, 'Hotel Aryaduta', 'null', 1326, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1326, 'Hotel Clover', 'null', 1327, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1327, 'Hotel De La Paix', 'null', 1328, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1328, 'Hotel du Vin', 'null', 1329, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1329, 'Hotel Equatorial Group', 'null', 1330, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1330, 'Hotel Fine Group', 'null', 1331, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1331, 'Hotel Goonline Management', 'null', 1332, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1332, 'Hotel Help', 'null', 1333, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1333, 'Hotel Hokke Club Group', 'null', 1334, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1334, 'Hotel Keihan', 'null', 1335, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1335, 'Hotel Livemax', 'null', 1336, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1336, 'Hotel Monterey Group', 'null', 1337, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1337, 'Hotel Revenue', 'null', 1338, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1338, 'Hotel Royal Group', 'null', 1339, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1339, 'Hotel San Francisco', 'null', 1340, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1340, 'Hotel San Marco', 'null', 1341, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1341, 'Hotel Sentral', 'null', 1342, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1342, 'Hotel Sentral Group', 'null', 1343, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1343, 'Hotel Seri Malaysia', 'null', 1344, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1344, 'Hotel Skypark', 'null', 1345, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1345, 'Hotel Sogo', 'null', 1346, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1346, 'Hotel Tetora Group', 'null', 1347, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1347, 'Hotel Trend', 'null', 1348, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1348, 'Hotel Trusty', 'null', 1349, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1349, 'Hotel WBF', 'null', 1350, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1350, 'Hotel Wing International', 'null', 1351, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1351, 'Hotel Α-1 Group', 'null', 1352, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1352, 'Hotelarius', 'null', 1353, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1353, 'Hoteles Catalonia', 'null', 1354, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1354, 'Hoteles Mision', 'null', 1355, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1355, 'Hoteles R-H', 'null', 1356, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1356, 'Hoteles Santos', 'null', 1357, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1357, 'Hoteles Silken', 'null', 1358, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1358, 'Hotello', 'null', 1359, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1359, 'Hotels & Preference', 'null', 1360, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1360, 'Hotels by Fassbind', 'null', 1361, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1361, 'Hotels de Paris Rive Gauche', 'null', 1362, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1362, 'Hotels du Roy', 'null', 1363, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1363, 'Hotels Group', 'null', 1364, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1364, 'Hotels Mision', 'null', 1365, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1365, 'Hotels Solutions Direct', 'null', 1366, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1366, 'Hotelz Group', 'null', 1367, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1367, 'Hotusa Hotels', 'null', 1368, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1368, 'House in Bandung', 'null', 1369, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1369, 'House in Danang', 'null', 1370, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1370, 'House of Asia', 'null', 1371, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1371, 'House of Chandra', 'null', 1372, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1372, 'House of Memory', 'null', 1373, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1373, 'House Sarah', 'null', 1374, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1374, 'HPL', 'null', 1375, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1375, 'HPW Group', 'null', 1376, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1376, 'HQ Anest', 'null', 1377, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1377, 'HQ Japan ParkHome', 'null', 1378, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1378, 'HQ Kokusai Kikaku', 'null', 1379, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1379, 'HQ Riverestate', 'null', 1380, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1380, 'H-Top Hotels Group', 'null', 1164, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1381, 'Hua Hin Fever', 'null', 1381, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1382, 'Hua Hin Property Online', 'null', 1382, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1383, 'Hua Shan Art Inn', 'null', 1383, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1384, 'HuahinCondo4Rent', 'null', 1384, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1385, 'Huazhu Hotels Group', 'null', 1385, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1386, 'Human Company', 'null', 1386, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1387, 'Humus Hospitality', 'null', 1387, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1388, 'Hunguest Hotels', 'null', 1388, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1389, 'Huri Saryan', 'null', 1389, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1390, 'Husa Hoteles', 'null', 1390, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1391, 'HW House', 'null', 1391, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1392, 'Hyatt Hotels', 'null', 1392, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1393, 'Hyatt Place Hotels', 'null', 1393, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1394, 'I Fukawa', 'null', 1394, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1395, 'I Jackson Aberia', 'null', 1395, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1396, 'I Ogaki', 'null', 1396, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1397, 'I Putu Gede Suriyantana', 'null', 1397, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1398, 'I SU Sang Un', 'null', 1398, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1399, 'I Sunsun house', 'null', 1399, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1400, 'I Wayan Yudiarta', 'null', 1400, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1401, 'Iabsakul Property', 'null', 1402, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1402, 'Ibeng Group', 'null', 1403, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1403, 'Iberostar Hotels & Resorts', 'null', 1404, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1404, 'Ibrahim', 'null', 1405, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1405, 'Ichigo Ichie', 'null', 1407, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1406, 'Iconic Hotels', 'null', 1408, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1407, 'Ida Octaviani Property', 'null', 1409, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1408, 'Idea Hotels', 'null', 1410, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1409, 'IGB', 'null', 1411, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1410, 'IHQ 414929278@qq.com', 'null', 1413, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1411, 'IHQ Fukuoka Yoshino968', 'null', 1414, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1412, 'IHQ Kato-11', 'null', 1415, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1413, 'IHQ Kyonokatadomari', 'null', 1416, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1414, 'IHQ Kyoto Eikosha Otomari', 'null', 1417, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1415, 'IHQ Mintkingen', 'null', 1418, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1416, 'IHQ Ohori House', 'null', 1419, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1417, 'IHQ Osaka', 'null', 1420, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1418, 'IHQ Osaka Chunyingchi', 'null', 1421, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1419, 'IHQ Osaka Fun World', 'null', 1422, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1420, 'IHQ Osaka Iwamoto', 'null', 1423, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1421, 'IHQ Osaka Kazuki Masai', 'null', 1424, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1422, 'IHQ Osaka Nami', 'null', 1425, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1423, 'IHQ Osaka Song', 'null', 1426, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1424, 'IHQ Osaka Toshihiro Boku (Earth Group)', 'null', 1427, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1425, 'IHQ Osaka Yoko.chameleo', 'null', 1428, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1426, 'IHQ Ryota Nakanishi', 'null', 1429, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1427, 'IHQ Sapporo', 'null', 1430, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1428, 'IHQ TM', 'null', 1431, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1429, 'IHQ TMIH', 'null', 1432, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1430, 'IHQ Tokyo', 'null', 1433, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1431, 'IHQ Tokyo Happy Train Shinjuku', 'null', 1434, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1432, 'IHQ Tokyo Yunting3542', 'null', 1435, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1433, 'Ihwan Roosyanto', 'null', 1436, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1434, 'Ilunion Hotels', 'null', 1437, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1435, 'Imagine Bali Villa', 'null', 1438, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1436, 'Immogroom', 'null', 1439, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1437, 'Imperial College', 'null', 1440, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1438, 'Imperial Group of Hotels', 'null', 1441, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1439, 'Imperial Hotels Group', 'null', 1442, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1440, 'Impiana', 'null', 1443, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1441, 'Impressco', 'null', 1444, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1442, 'In The Hood Hospitality', 'null', 1445, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1443, 'Indar Jo', 'null', 1446, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1444, 'Indi Phuket', 'null', 1447, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1445, 'Indigo Suites', 'null', 1448, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1446, 'Individual Bali Villas', 'null', 1449, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1447, 'Infinity Property', 'null', 1450, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1448, 'Ingenia', 'null', 1451, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1449, 'Ini Vie Hospitality', 'null', 1452, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1450, 'Iniciativas Guscar', 'null', 1453, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1451, 'Ink Accommodation_C', 'null', 1454, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1452, 'Inna Hotel Group', 'null', 1456, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1453, 'Inna Maulina', 'null', 1457, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1454, 'InnApartment', 'null', 1458, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1455, 'Innotality', 'null', 1459, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1456, 'Innovation Style', 'null', 1460, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1457, 'Insail Hotels Group', 'null', 1461, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1458, 'Inside', 'null', 1462, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1459, 'IntercityHotel', 'null', 1463, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1460, 'InterContinental Hotels Group', 'null', 1464, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1461, 'Interhome', 'null', 1465, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1462, 'Interindo Group', 'null', 1466, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1463, 'IntiWhiz Hotels', 'null', 1467, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1464, 'Intour Group', 'null', 1468, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1465, 'IPR', 'null', 1469, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1466, 'Ira Prisma Group', 'null', 1471, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1467, 'Irfan Hasbi Basma', 'null', 1472, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1468, 'Irma Aprita', 'null', 1473, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1469, 'Irong Group', 'null', 1474, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1470, 'Irooms Apartment', 'null', 1475, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1471, 'IRS Royal Apartments', 'null', 1476, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1472, 'Isak Mongan', 'null', 1477, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1473, 'Ishin Hotels', 'null', 1478, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1474, 'ISJ', 'null', 1479, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1475, 'Islands Stay Hotels', 'null', 1480, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1476, 'Isrentals_C', 'null', 1481, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1477, 'Isrotel', 'null', 1482, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1478, 'Ista', 'null', 1483, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1479, 'Isvara', 'null', 1484, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1480, 'ITC Welcomgroup Hotels Palaces and Resorts', 'null', 1485, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1481, 'ITI Hotels', 'null', 1486, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1482, 'Itoen Hotels', 'null', 1487, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1483, 'ITX', 'null', 1488, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1484, 'IVL Property', 'null', 1489, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1485, 'Ivy Villa', 'null', 1490, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1486, 'Iwasaki Maki', 'null', 1491, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1487, 'Izumi Hotels', 'null', 1492, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1488, 'iCheck Inn Gropus', 'null', 1406, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1489, 'iHome Homestay', 'null', 1412, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1490, 'i-Hotel', 'null', 1401, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1491, 'inn-tellingence Hotels', 'null', 1455, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1492, 'iproperti', 'null', 1470, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1493, 'J&I home', 'null', 1493, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1494, 'JA Hotels & Resort', 'null', 1494, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1495, 'Jackson Homestay', 'null', 1495, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1496, 'Jacky', 'null', 1496, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1497, 'Jaelani Qadir', 'null', 1497, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1498, 'Jakarta Property', 'null', 1498, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1499, 'JAL Hotels', 'null', 1499, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1500, 'James Kok', 'null', 1500, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1501, 'Jang Paza', 'null', 1501, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1502, 'Janjao Property', 'null', 1502, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1503, 'Jannah Group', 'null', 1503, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1504, 'Japan Association of Secluded Hot Spring Inns', 'null', 1504, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1505, 'Japan International Solutions', 'null', 1505, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1506, 'Japan La Casa', 'null', 1506, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1507, 'Japan Style West', 'null', 1507, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1508, 'Japan Tourism Corporation', 'null', 1508, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1509, 'Japan World Wide Planning Rebirth', 'null', 1509, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1510, 'Japan@home', 'null', 1510, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1511, 'Japango_JP', 'null', 1511, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1512, 'Japanian', 'null', 1512, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1513, 'Japaning hotel Tenjin', 'null', 1513, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1514, 'Jasmine Residence', 'null', 1514, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1515, 'Jasmine Suite', 'null', 1515, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1516, 'Jason Properties', 'null', 1516, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1517, 'Jayakarta Hotels & Resorts', 'null', 1517, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1518, 'Jayarahayu Group', 'null', 1518, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1519, 'Jazz Apt', 'null', 1519, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1520, 'JB Condo Rentals', 'null', 1520, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1521, 'Jebel Ali International Hotels', 'null', 1521, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1522, 'JENDELA360', 'null', 1522, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1523, 'Jenny Properties', 'null', 1523, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1524, 'Jenny\'s Retreats', 'null', 1524, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1525, 'Jessy Guest House', 'null', 1525, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1526, 'Jet Set Let Ltd_C', 'null', 1526, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1527, 'Jethro Property', 'null', 1527, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1528, 'Jetta Enterprise', 'null', 1528, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1529, 'Jetwing Hotels', 'null', 1529, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1530, 'Jimmy Homestay', 'null', 1530, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1531, 'Jinjiang International', 'null', 1531, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1532, 'Jinling Hotels & Resorts', 'null', 1532, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1533, 'JJ Hospitality', 'null', 1533, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1534, 'JL RICH RESOURCES ENTERPRISE', 'null', 1534, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1535, 'JM Hoteles', 'null', 1535, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1536, 'Jo Condo Unit', 'null', 1536, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1537, 'Joany Villa', 'null', 1537, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1538, 'Joe Yohanes Kartika', 'null', 1538, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1539, 'Joglo Villa Bali', 'null', 1539, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1540, 'John Boutique Villa', 'null', 1540, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1541, 'John D. Condotel & Staycation ', 'null', 1541, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1542, 'John NgChengSwee', 'null', 1542, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1543, 'Jolly Budiharti', 'null', 1543, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1544, 'Jopi Rooms', 'null', 1544, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1545, 'Joy', 'null', 1545, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1546, 'Joy Supanat Property', 'null', 1546, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1547, 'JP airbbbbb(DNU)', 'null', 1547, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1548, 'JP Airdream.com', 'null', 1548, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1549, 'JP angel-r', 'null', 1549, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1550, 'JP Bigsunli', 'null', 1550, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1551, 'JP Broworks2016', 'null', 1551, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1552, 'JP Casa Viento Stay', 'null', 1552, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1553, 'JP Chunhedesign', 'null', 1553, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1554, 'JP cityhotel.osaka', 'null', 1554, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1555, 'JP fuku5034', 'null', 1555, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1556, 'JP Hospo Agoda(DNU)', 'null', 1556, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1557, 'JP IHQ Chenglei19820419', 'null', 1557, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1558, 'JP IHQ Osaka rddbk1688', 'null', 1558, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1559, 'JP Shirakabanoyado', 'null', 1559, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1560, 'JP Tokyosharehouse.com', 'null', 1560, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1561, 'jp xiangxiang.zhang324', 'null', 1561, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1562, 'JP Yokosokansai', 'null', 1562, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1563, 'JP_Acardia_AH', 'null', 1563, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1564, 'JP_Aikansha', 'null', 1564, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1565, 'JP_AOCA', 'null', 1565, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1566, 'JP_BNBCARES', 'null', 1566, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1567, 'JP_FukuokaNow', 'null', 1567, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1568, 'JP_Japango', 'null', 1568, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1569, 'JP_Kelly_House', 'null', 1569, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1570, 'JP_LBL', 'null', 1570, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1571, 'JP_Mdex', 'null', 1571, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1572, 'JP_MrKinjo', 'null', 1572, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1573, 'JP_PJP', 'null', 1573, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1574, 'JP_Sai Industries (Ideal Resort)', 'null', 1574, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1575, 'JP_SHI(s-hotel)', 'null', 1575, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1576, 'JP_Sumuka_Life', 'null', 1576, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1577, 'JP_Tomarunen', 'null', 1577, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1578, 'JPT', 'null', 1578, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1579, 'JQ Innovation', 'null', 1579, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1580, 'JR Hotel Group', 'null', 1580, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1581, 'JSB Property', 'null', 1581, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1582, 'JSM', 'null', 1582, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1583, 'JUCY', 'null', 1583, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1584, 'Julfan Basma Group', 'null', 1584, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1585, 'Julia Real', 'null', 1585, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1586, 'July Pattaya', 'null', 1586, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1587, 'Jumeirah', 'null', 1587, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1588, 'Jung Soo Lee', 'null', 1588, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1589, 'Jupiter Hotels', 'null', 1589, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1590, 'Juragan Property', 'null', 1590, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1591, 'Jurys Inn', 'null', 1591, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1592, 'Justa Hotels & Resorts', 'null', 1592, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1593, 'Justin Vinhomes', 'null', 1593, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1594, 'Juvy\'s Place', 'null', 1594, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1595, 'K Squared_C', 'null', 1595, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1596, 'K&K', 'null', 1599, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1597, 'Kaani Hotels', 'null', 1600, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1598, 'Kafuna.A', 'null', 1601, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1599, 'Kagum Group', 'null', 1602, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1600, 'Kai Chen Properties', 'null', 1603, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1601, 'Kaikon', 'null', 1604, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1602, 'Kaikoura Holiday Homes', 'null', 1605, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1603, 'Kaimoo Resorts & Hotels', 'null', 1606, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1604, 'Kamenoi Hotel', 'null', 1607, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1605, 'Kampung Wisata Kali Code', 'null', 1608, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1606, 'Kana\'s Room', 'null', 1609, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1607, 'Kanematsu', 'null', 1610, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1608, 'Kanpo no Yado', 'null', 1611, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1609, 'Kaohsiung Siziwan', 'null', 1612, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1610, 'Karabao', 'null', 1613, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1611, 'Karaksa Hotel', 'null', 1614, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1612, 'Karang Kedemple', 'null', 1615, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1613, 'KAREBA GROUP', 'null', 1616, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1614, 'KARISMA HOTELS', 'null', 1617, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1615, 'Kariyushi Hotels', 'null', 1618, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1616, 'Kase Group', 'null', 1619, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1617, 'Kauai Real Estate_C', 'null', 1620, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1618, 'Kaya Hotels & Resorts', 'null', 1621, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1619, 'Kayana Tour', 'null', 1622, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1620, 'Kayu Putih Bali', 'null', 1623, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1621, 'Kazuhiko Isozaki', 'null', 1624, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1622, 'KC Hotels & Resorts', 'null', 1625, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1623, 'K-carve life', 'null', 1596, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1624, 'Keahotels', 'null', 1626, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1625, 'Keetapat Property', 'null', 1627, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1626, 'Kei Villas', 'null', 1628, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1627, 'Keikyu EX Inn', 'null', 1629, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1628, 'Keio Presso Inn', 'null', 1630, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1629, 'Keita Homestay', 'null', 1631, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1630, 'Kejayaan 65 Residence', 'null', 1632, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1631, 'Kelly Inns', 'null', 1633, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1632, 'Kempinski', 'null', 1634, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1633, 'Kending Hotels', 'null', 1635, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1634, 'Kerpooh Property', 'null', 1636, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1635, 'KervanSaray Hotels', 'null', 1637, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1636, 'Kerzner International', 'null', 1638, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1637, 'Key One', 'null', 1639, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1638, 'Keypro', 'null', 1640, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1639, 'Keys Hotels', 'null', 1641, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1640, 'Keys Technology_JP', 'null', 1642, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1641, 'Keywii_C', 'null', 1643, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1642, 'Khaosan Tokyo Guest House', 'null', 1644, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1643, 'KHC (Kibutzim)', 'null', 1645, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1644, 'Kibbutz Hotels', 'null', 1646, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1645, 'Kieu Trang', 'null', 1647, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1646, 'Kiev Apartment Now', 'null', 1648, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1647, 'Kiev Apartments', 'null', 1649, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1648, 'Kiki', 'null', 1650, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1649, 'Kim Hyeonmi', 'null', 1651, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1650, 'Kim Properties', 'null', 1652, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1651, 'Kimpton Hotels & Restaurant Group', 'null', 1653, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1652, 'Kingdom Villas', 'null', 1654, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1653, 'Kingsland Studios', 'null', 1655, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1654, 'Kinifrog Property', 'null', 1656, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1655, 'Kiwi Holiday Parks', 'null', 1657, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1656, 'KL Shortstay', 'null', 1658, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1657, 'KL101', 'null', 1659, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1658, 'KLIH', 'null', 1660, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1659, 'KLS Resorts', 'null', 1661, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1660, 'KNP Management (Stay Samui)', 'null', 1662, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1661, 'Koalabeds Group', 'null', 1663, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1662, 'Koentari Home', 'null', 1664, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1663, 'Kohabi Home JP', 'null', 1665, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1664, 'Kokyonoyado', 'null', 1666, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1665, 'Komang Arjawa', 'null', 1667, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1666, 'Kona Coast Vacations_C', 'null', 1668, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1667, 'KOS', 'null', 1669, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1668, 'Kos Elsa', 'null', 1670, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1669, 'Kos Muwardi 34', 'null', 1671, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1670, 'Kos U9A', 'null', 1672, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1671, 'Kosciusko First National_C', 'null', 1673, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1672, 'Kosmopolito Hotels International', 'null', 1674, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1673, 'KOST AYU', 'null', 1675, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1674, 'KOST URIP', 'null', 1676, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1675, 'Kost Wily', 'null', 1677, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1676, 'Kowa-estate', 'null', 1678, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1677, 'KP Wong', 'null', 1679, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1678, 'Krabi Villa Management by Krabi Riviera Company Ltd.', 'null', 1680, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1679, 'Krabirents', 'null', 1681, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1680, 'K\'s House', 'null', 1598, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1681, 'KSK Japan', 'null', 1682, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1682, 'KT9house', 'null', 1683, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1683, 'K-Togashi Value build JP', 'null', 1597, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1684, 'Kubu Carik', 'null', 1684, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1685, 'Kufu', 'null', 1685, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1686, 'Kukuh Januardi', 'null', 1686, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1687, 'Kuretakeso', 'null', 1687, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1688, 'Kusuma Estate', 'null', 1688, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1689, 'Kuyakitoda_AsapOne_JP', 'null', 1689, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1690, 'Kyoritsu Resort', 'null', 1690, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1691, 'Kyoto machiya', 'null', 1691, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1692, 'Kyoto Vacation Rental Wonderland', 'null', 1692, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1693, 'Kyukamura Hotels', 'null', 1693, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1694, 'La Bella Casa Bali', 'null', 1697, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1695, 'La Cabane', 'null', 1698, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1696, 'La Comunity_C', 'null', 1699, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1697, 'La Joya & La Joya II Biu Biu', 'null', 1700, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1698, 'La Mer Homes_C', 'null', 1701, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1699, 'La Quinta Inns & Suites', 'null', 1702, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1700, 'La Villa Bali', 'null', 1703, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1701, 'L\'Abode Accommodation', 'null', 1694, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1702, 'Labor Land', 'null', 1704, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1703, 'Labranda Hotels & Resorts', 'null', 1705, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1704, 'Laddawan Property', 'null', 1706, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1705, 'LAE', 'null', 1707, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1706, 'Lafontaine Group', 'null', 1708, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1707, 'Laguna Phuket', 'null', 1709, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1708, 'Lakbayan Hotels', 'null', 1710, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1709, 'Lakeshores Holiday & Short Stay Accommodation', 'null', 1711, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1710, 'LaLiT Hotels Palaces and Resorts', 'null', 1712, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1711, 'Lalu Arjuna', 'null', 1713, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1712, 'Lam Ha', 'null', 1714, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1713, 'Lampin Yasuda', 'null', 1715, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1714, 'Land Japan', 'null', 1716, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1715, 'LANDEE', 'null', 1717, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1716, 'Landison', 'null', 1718, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1717, 'Landlord', 'null', 1719, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1718, 'Landmark Hotels & Suites', 'null', 1720, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1719, 'Langebaan Holiday Homes', 'null', 1721, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1720, 'Langham Hotels International', 'null', 1722, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1721, 'Lanson Place', 'null', 1723, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1722, 'LanzaroteLanzarote_C', 'null', 1724, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1723, 'Larkspur', 'null', 1725, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1724, 'Lavana', 'null', 1726, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1725, 'Lavanda', 'null', 1727, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1726, 'Lavender Apartment', 'null', 1728, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1727, 'Lavender Villa Spa', 'null', 1729, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1728, 'Lavie En Rose', 'null', 1730, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1729, 'LCB groups', 'null', 1731, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1730, 'LDG Serviced Apartments', 'null', 1732, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1731, 'LDK', 'null', 1733, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1732, 'Le Apple', 'null', 1734, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1733, 'Le Bleu - Nha Trong Xom', 'null', 1735, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1734, 'Le Green Hotel & Suite', 'null', 1736, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1735, 'LE Hotels Group', 'null', 1737, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1736, 'Le Sabot Bali', 'null', 1738, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1737, 'Le Soleil De Van', 'null', 1739, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1738, 'Leading', 'null', 1740, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1739, 'Leading Hotels of the World', 'null', 1741, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1740, 'LED', 'null', 1742, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1741, 'Lee', 'null', 1743, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1742, 'Leeu Collection', 'null', 1744, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1743, 'Legacy Hotels and Resorts', 'null', 1745, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1744, 'Legend Hotels International Corporation', 'null', 1746, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1745, 'Legend Japan', 'null', 1747, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1746, 'Legian Sunset Residence', 'null', 1748, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1747, 'Lemon Tree Hotels', 'null', 1749, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1748, 'Lenny Syam', 'null', 1750, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1749, 'Leo Group', 'null', 1751, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1750, 'Leogrand', 'null', 1752, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1751, 'Leon Gfeel', 'null', 1753, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1752, 'Leonardi Hotels', 'null', 1754, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1753, 'Leonardo Hotels', 'null', 1755, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1754, 'Les Hotels de Paris', 'null', 1756, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1755, 'Les Relais de Paris - Jardins de Paris', 'null', 1757, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1756, 'Let Me Inn', 'null', 1758, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1757, 'L\'Heritage Hotel', 'null', 1695, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1758, 'L\'Hotel', 'null', 1696, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1759, 'LIA KOST', 'null', 1760, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1760, 'Li Qin', 'null', 1759, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1761, 'Lianglinman Property', 'null', 1761, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1762, 'Lianjia Corporation', 'null', 1762, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1763, 'Libertel', 'null', 1763, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1764, 'Library Hotel Collection', 'null', 1764, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1765, 'Lidan rentals', 'null', 1765, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1766, 'Life Planning', 'null', 1766, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1767, 'Lifelulu_JP', 'null', 1767, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1768, 'Lifestyle Retreats', 'null', 1768, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1769, 'Likehome Apartment', 'null', 1769, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1770, 'Lilian Home', 'null', 1770, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1771, 'Lillie', 'null', 1771, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1772, 'Lily', 'null', 1772, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1773, 'LiLy Homestay', 'null', 1773, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1774, 'Lily\'s Condo', 'null', 1774, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1775, 'Lin Pro', 'null', 1775, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1776, 'Lineup', 'null', 1776, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1777, 'Ling Nan Hotel Group', 'null', 1777, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1778, 'Ling Properties', 'null', 1778, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1779, 'Linh Tran Group', 'null', 1779, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1780, 'Linzi', 'null', 1780, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1781, 'Lion Roars Hotels and Lodges', 'null', 1781, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1782, 'Lisbon Serviced Apartments', 'null', 1782, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1783, 'Little America Hotels', 'null', 1783, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1784, 'Little Hoi An Group', 'null', 1784, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1785, 'Liv Living', 'null', 1785, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1786, 'Live at home Bangna', 'null', 1786, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1787, 'Livin Africa', 'null', 1787, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1788, 'Living Hotels', 'null', 1788, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1789, 'Lizaplan/Kushitsu.com', 'null', 1789, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1790, 'LJ Hooker Real Estate', 'null', 1790, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1791, 'LK Group', 'null', 1791, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1792, 'lloguercerdanya_C', 'null', 1792, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1793, 'LO.AN Hotels', 'null', 1793, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1794, 'Loan Group', 'null', 1794, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1795, 'Locationwise', 'null', 1795, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1796, 'Lock Forward', 'null', 1796, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1797, 'Lodgable_C', 'null', 1797, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1798, 'Lodtunduh Sari Villa', 'null', 1798, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1799, 'Loews Hotels', 'null', 1799, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1800, 'Lofty Phuket', 'null', 1800, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1801, 'Logi -Service_C', 'null', 1801, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1802, 'Logis de France', 'null', 1802, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1803, 'Londo Bungalow', 'null', 1803, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1804, 'London Base_C', 'null', 1804, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1805, 'Long Beach Hotel Group', 'null', 1805, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1806, 'Long Stay', 'null', 1806, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1807, 'Looting Café Property', 'null', 1807, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1808, 'Lope De Vega Tower Condominium', 'null', 1808, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1809, 'Lords Hotels & Resorts', 'null', 1809, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1810, 'Lost in the Magic Vacation Homes_C', 'null', 1810, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1811, 'Lotte Hotel', 'null', 1811, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1812, 'Lotus House Hanoi', 'null', 1812, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1813, 'Louvre Hôtels', 'null', 1813, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1814, 'Lovaito Villa', 'null', 1814, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1815, 'Love I Feng Chia Night Market', 'null', 1815, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1816, 'Love Taipei', 'null', 1816, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1817, 'Lovina Beach House Villa', 'null', 1817, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1818, 'Lovina Homes', 'null', 1818, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1819, 'Lovina Krui Surf', 'null', 1819, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1820, 'Lovina Residences', 'null', 1820, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1821, 'Low Yat', 'null', 1821, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1822, 'Loyal Unicorn', 'null', 1822, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1823, 'Loyalty USA_C', 'null', 1823, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1824, 'LSE', 'null', 1824, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1825, 'Lugaris', 'null', 1825, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1826, 'Luo Properties', 'null', 1826, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1827, 'Luvill', 'null', 1827, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1828, 'Lux Rooms Night Bazaar', 'null', 1828, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1829, 'LUX* Resorts', 'null', 1829, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1830, 'Luxe in Venice', 'null', 1830, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1831, 'Luxe Worldwide Hotels', 'null', 1831, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1832, 'Luxemon Hotels Group', 'null', 1832, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1833, 'Luxstay', 'null', 1833, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1834, 'Luxury Hua Hin Property Co., Ltd', 'null', 1834, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1835, 'Luxury Lakeside Accommodation Group', 'null', 1835, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1836, 'Luxury Perth Stays', 'null', 1836, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1837, 'Luxury Rental Group_C', 'null', 1837, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1838, 'Luxury Samui Villa', 'null', 1838, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1839, 'Luxury Staycation', 'null', 1839, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1840, 'Luxury Travels', 'null', 1840, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1841, 'Luxury Villas and Homes', 'null', 1841, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1842, 'LuYue', 'null', 1842, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1843, 'Lvyue Hotels & Resorts', 'null', 1843, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1844, 'Lys Property Management', 'null', 1844, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1845, 'Lz myroad JP', 'null', 1845, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1846, 'M Boutique', 'null', 1846, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1847, 'M Design', 'null', 1847, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1848, 'M Stay', 'null', 1848, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1849, 'M Support', 'null', 1849, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1850, 'M&R Hospitality', 'null', 1852, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1851, 'M&T', 'null', 1853, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1852, 'MAADS', 'null', 1854, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1853, 'Macau CTS Hotel', 'null', 1855, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1854, 'Maccarat', 'null', 1856, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1855, 'Macdonald Hotels Group', 'null', 1857, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1856, 'Mad Monkey', 'null', 1858, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1857, 'Made Comfy', 'null', 1859, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1858, 'Madmee Property', 'null', 1860, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1859, 'Mae Rampung Beach House', 'null', 1861, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1860, 'Magic of Bali Villas', 'null', 1862, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1861, 'Magnetic Island_C', 'null', 1863, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1862, 'Magnuson Hotels', 'null', 1864, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1863, 'Mahkota Property', 'null', 1865, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1864, 'Mai Villa Group', 'null', 1866, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1865, 'Maison Prive', 'null', 1867, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1866, 'MaisonNets', 'null', 1868, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1867, 'Majestic Hotel Group', 'null', 1869, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1868, 'Majestic Hotels', 'null', 1870, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1869, 'Majordomo', 'null', 1871, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1870, 'Makarem Group', 'null', 1872, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1871, 'Makassar Kost Exclusive Group', 'null', 1873, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1872, 'Makati - Tomo\'s Condotel', 'null', 1874, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1873, 'Makluxuryliving', 'null', 1875, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1874, 'Maleo Moyo Diving', 'null', 1876, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1875, 'Mallorca Holiday Villas', 'null', 1877, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1876, 'Mallorcanorth Villa Services', 'null', 1878, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1877, 'Malmaison Hotels', 'null', 1879, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1878, 'Maman Sumarna', 'null', 1880, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1879, 'Man', 'null', 1881, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1880, 'Manara Management', 'null', 1882, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1881, 'Mandarin Oriental Hotel Group', 'null', 1883, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1882, 'Manggala Bali', 'null', 1884, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1883, 'Manhattan', 'null', 1885, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1884, 'Manila Condotel', 'null', 1886, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1885, 'Manlilu', 'null', 1887, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1886, 'Manly Stay', 'null', 1888, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1887, 'Manotel Hotel Group', 'null', 1889, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1888, 'Manseikaku Hotels', 'null', 1890, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1889, 'Mansley', 'null', 1891, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1890, 'Mantra Group', 'null', 1892, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1891, 'Marc Hotels & Resorts', 'null', 1893, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1892, 'Marcus Hotels and Resorts', 'null', 1894, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1893, 'Marenostrum and Curious', 'null', 1895, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1894, 'Margareta Nola', 'null', 1896, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1895, 'Margonda Residence', 'null', 1897, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1896, 'Marhba Holiday Homes', 'null', 1898, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1897, 'Marigold Aonang Villa', 'null', 1899, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1898, 'Maritim Hotels', 'null', 1900, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1899, 'Mark and Christine', 'null', 1901, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1900, 'Mark it', 'null', 1902, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1901, 'Marquee Residence', 'null', 1903, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1902, 'Marriott', 'null', 1904, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1903, 'Marroad Hotels', 'null', 1905, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1904, 'Martin\'s Hotels', 'null', 1906, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1905, 'Maruhide', 'null', 1907, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1906, 'Maruyama', 'null', 1908, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1907, 'Maryan Moyo Bungalows', 'null', 1909, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1908, 'Massimo Two', 'null', 1910, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1909, 'Massive Sapporo', 'null', 1911, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1910, 'Master Inn', 'null', 1912, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1911, 'Master Japan Minpaku', 'null', 1913, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1912, 'Matsue Kousan Inc', 'null', 1914, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1913, 'Matsuri Technology_JP', 'null', 1915, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1914, 'Matt Birkey Vacation Rentals_C', 'null', 1916, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1915, 'Maurice Hurand Hotels', 'null', 1917, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1916, 'Maviba Villas and Resorts', 'null', 1918, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1917, 'Max House', 'null', 1919, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1918, 'MaXhit_C', 'null', 1920, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1919, 'Maxhome', 'null', 1921, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1920, 'Maxima julianne Lindy M. Arce', 'null', 1922, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1921, 'MaxOneHotels', 'null', 1923, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1922, 'Maxstays', 'null', 1924, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1923, 'Maybourne Hotel Group', 'null', 1925, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1924, 'Mayfair Hotels & Resort', 'null', 1926, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1925, 'Mayfair Hotels&Resorts', 'null', 1927, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1926, 'Maykenbel Group', 'null', 1928, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1927, 'Mayland', 'null', 1929, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1928, 'McGrath Byron Bay', 'null', 1930, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1929, 'MCM Hotels', 'null', 1931, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1930, 'MDI', 'null', 1932, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1931, 'Mebuki', 'null', 1933, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1932, 'Mediapura', 'null', 1934, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1933, 'Mediatorat', 'null', 1935, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1934, 'Meininger Hotels', 'null', 1936, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1935, 'Meitetsu Inn', 'null', 1937, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1936, 'Melbourne Short Stay Apartments', 'null', 1938, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1937, 'Melco Resorts & Entertainment', 'null', 1939, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1938, 'Melhome_C', 'null', 1940, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1939, 'Melia Hotels International', 'null', 1941, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1940, 'Melinda group', 'null', 1942, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1941, 'Mella House Uluwatu', 'null', 1943, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1942, 'Meme Properties', 'null', 1944, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1943, 'Meninas Opera', 'null', 1945, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1944, 'Menteng Group', 'null', 1946, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1945, 'Menzies Hotels', 'null', 1947, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1946, 'Meow Homestay', 'null', 1948, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1947, 'Mercer Hotels', 'null', 1949, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1948, 'Merin City Suites Apartment', 'null', 1950, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1949, 'Merin Suites Tower', 'null', 1951, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1950, 'Meriton Suites', 'null', 1952, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1951, 'Meritus Group', 'null', 1953, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1952, 'Meritus Hotels & Resorts', 'null', 1954, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1953, 'Merivale Apartments', 'null', 1955, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1954, 'Merry Property', 'null', 1956, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1955, 'Metro Hotels', 'null', 1957, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1956, 'Metro Rent', 'null', 1958, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1957, 'Metropolitan Golden Management (MGM)', 'null', 1959, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1958, 'Mets', 'null', 1960, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1959, 'Meyer Real Estate_C', 'null', 1961, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1960, 'MGM Resorts International', 'null', 1962, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1961, 'MH Apartments', 'null', 1963, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1962, 'MH Group', 'null', 1964, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1963, 'Mi Casa Su Casa', 'null', 1965, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1964, 'Mia Macarena', 'null', 1966, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1965, 'Mickey TH', 'null', 1967, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1966, 'Middlewood Hotel & Resort', 'null', 1968, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1967, 'Midtown Rentals', 'null', 1969, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1968, 'Mielparque', 'null', 1970, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1969, 'Mikoto Far East Phil. Inc', 'null', 1971, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1970, 'Millennium & Copthorne Hotels', 'null', 1972, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1971, 'Minggu Villas', 'null', 1973, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1972, 'Mingren', 'null', 1974, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1973, 'Minh\'s Apt in Danang', 'null', 1975, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1974, 'Minor International', 'null', 1976, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1975, 'Minpaku Honpo', 'null', 1977, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1976, 'Mint Din House', 'null', 1978, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1977, 'Mint Hotels', 'null', 1979, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1978, 'Mint Resorts & Apartments', 'null', 1980, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1979, 'Miosba Homestay', 'null', 1981, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1980, 'Mirai Factory', 'null', 1982, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1981, 'Miramar Hotel and Investment', 'null', 1983, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1982, 'Miri Sato', 'null', 1984, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1983, 'Mirvac Hotel & Resort', 'null', 1985, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1984, 'Mitchell Corp', 'null', 1986, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1985, 'Mitsis Hotels', 'null', 1987, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1986, 'Mitsui Garden Hotel_DO NOT USE', 'null', 1988, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1987, 'Mitsui Garden Hotels', 'null', 1989, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1988, 'Miyako Hotels & Resorts', 'null', 1990, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1989, 'MJ', 'null', 1991, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1990, 'MK Hotel & Resort', 'null', 1992, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1991, 'Mode Duo', 'null', 1994, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1992, 'Modern Sun Estate', 'null', 1995, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1993, 'Moderno Urquinaona', 'null', 1996, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1994, 'Modo Apartment', 'null', 1997, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1995, 'Momo_JP', 'null', 1998, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1996, 'Mona Lisa Hotels and Residences', 'null', 1999, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1997, 'Mondo Hospitality', 'null', 2000, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1998, 'Mondo Living', 'null', 2001, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (1999, 'Mono Apartments', 'null', 2002, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2000, 'Montenegro-Booking_C', 'null', 2003, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2001, 'MonthStayz', 'null', 2004, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2002, 'Moon House', 'null', 2005, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2003, 'Moopun', 'null', 2006, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2004, 'Morae Travel & Tour Inc.', 'null', 2007, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2005, 'More Than a Room', 'null', 2008, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2006, 'Mo-Rentals', 'null', 1993, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2007, 'Morgans Hotel Group', 'null', 2009, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2008, 'Mornington Hotel Group', 'null', 2010, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2009, 'Moroccan Suite-Mr Liang', 'null', 2011, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2010, 'Morwing Hotels Group', 'null', 2012, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2011, 'Moshamanla Hotel Group', 'null', 2013, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2012, 'Motel 6', 'null', 2014, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2013, 'Mountain Plus_C', 'null', 2015, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2014, 'Movenpick Hotels & Resorts', 'null', 2016, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2015, 'Movenpick Residence Ekkamai', 'null', 2017, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2016, 'Mowu', 'null', 2018, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2017, 'Mr.KINJO_JP', 'null', 2019, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2018, 'M\'s Four', 'null', 1850, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2019, 'M\'s Hotel Group', 'null', 1851, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2020, 'Ms. Hsu Properties', 'null', 2020, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2021, 'Ms. Liao Properties', 'null', 2021, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2022, 'Mt.Baker Lodging_C', 'null', 2022, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2023, 'MTT Korea', 'null', 2023, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2024, 'Muar Warni Villa', 'null', 2024, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2025, 'Muh Zaenuddin Khair', 'null', 2025, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2026, 'Muhammad Gazali', 'null', 2026, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2027, 'Mulia Homestay', 'null', 2027, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2028, 'Musliadi Nasution', 'null', 2028, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2029, 'MXC Condotel', 'null', 2029, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2030, 'My Apatel', 'null', 2030, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2031, 'My Cannes_C', 'null', 2031, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2032, 'My Choice of Stay', 'null', 2032, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2033, 'My City Apartments', 'null', 2033, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2034, 'My Get Away Asia', 'null', 2034, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2035, 'My Gia', 'null', 2035, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2036, 'My Holiday WA', 'null', 2036, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2037, 'MY Home', 'null', 2037, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2038, 'My Hotel', 'null', 2038, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2039, 'My Place', 'null', 2039, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2040, 'My Rooms', 'null', 2040, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2041, 'My Story Hotels', 'null', 2041, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2042, 'My Villa Group', 'null', 2042, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2043, 'Myconian Collection', 'null', 2043, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2044, 'myNext', 'null', 2044, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2045, 'Mystays Hotel Management', 'null', 2045, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2046, 'Nadezhda', 'null', 2046, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2047, 'Nadler Hotels', 'null', 2047, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2048, 'Nadya Properties', 'null', 2048, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2049, 'Nagisa Bali', 'null', 2049, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2050, 'Naiade', 'null', 2050, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2051, 'Nakula Management', 'null', 2051, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2052, 'Nam Phuong Home', 'null', 2052, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2053, 'Namba Jisho', 'null', 2053, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2054, 'Namto House', 'null', 2054, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2055, 'Nancy Properties', 'null', 2055, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2056, 'Nandu Hospitality', 'null', 2056, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2057, 'Nanturuj Tower', 'null', 2057, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2058, 'Narai Hotel Group', 'null', 2058, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2059, 'Nasma Luxury Stays', 'null', 2059, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2060, 'NASYKEV', 'null', 2060, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2061, 'Natan Villa', 'null', 2061, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2062, 'National Company for Tourism Group', 'null', 2062, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2063, 'Native Apartments', 'null', 2063, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2064, 'Natsusan(Summer)_JP', 'null', 2064, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2065, 'Naumi', 'null', 2065, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2066, 'Nazeki Villa', 'null', 2066, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2067, 'Nazwa Room', 'null', 2067, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2068, 'NB Holidays AU', 'null', 2068, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2069, 'NB Villas', 'null', 2069, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2070, 'Ndalem Maharani Guesthouse', 'null', 2070, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2071, 'Ndalem Mantrigawen', 'null', 2071, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2072, 'Ndalem Natan Royal Heritage', 'null', 2072, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2073, 'Ndalem Sarengat', 'null', 2073, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2074, 'Nejico', 'null', 2074, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2075, 'Nest', 'null', 2075, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2076, 'Nest Hotel', 'null', 2076, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2077, 'Nesting Nomad', 'null', 2077, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2078, 'Nevsky Hotel Group', 'null', 2078, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2079, 'New Arabian Homes', 'null', 2079, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2080, 'New Century Tourism Group', 'null', 2080, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2081, 'New Gaea Hotels', 'null', 2081, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2082, 'New Gloria', 'null', 2082, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2083, 'New Life Villas', 'null', 2083, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2084, 'New Otani Hotels', 'null', 2084, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2085, 'New Town', 'null', 2085, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2086, 'New Wave Hotel', 'null', 2086, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2087, 'New World Hotels', 'null', 2087, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2088, 'NewLink', 'null', 2088, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2089, 'Newmark Hotels', 'null', 2089, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2090, 'Newton Residence', 'null', 2090, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2091, 'Next Story Group', 'null', 2091, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2092, 'Nextage', 'null', 2092, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2093, 'Nexus Regency', 'null', 2093, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2094, 'NH Hotels', 'null', 2094, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2095, 'NHA Master - Do Not Use', 'null', 2095, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2096, 'Nha Trang Harbor', 'null', 2096, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2097, 'Nha Trang Wonderland', 'null', 2097, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2098, 'NHM', 'null', 2098, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2099, 'Nicely Decorated', 'null', 2099, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2100, 'NicoJapan', 'null', 2100, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2101, 'Nimit Manatpon', 'null', 2101, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2102, 'Nimman Expat', 'null', 2102, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2103, 'Nine Hours', 'null', 2103, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2104, 'Ninety Six Hotel', 'null', 2104, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2105, 'Nirvana Holiday Homes', 'null', 2105, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2106, 'Nirwana Guest House', 'null', 2106, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2107, 'Nisade', 'null', 2107, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2108, 'Nishitetsu Hotel Group', 'null', 2108, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2109, 'Nishitetsu Inn', 'null', 2109, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2110, 'Nittakarn Property', 'null', 2110, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2111, 'No chain', 'null', 2111, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2112, 'NOAHCorporation', 'null', 2112, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2113, 'Nobel House Hotels & Resorts', 'null', 2113, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2114, 'Noguchi Kanko Group Hotels', 'null', 2114, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2115, 'Nomads World Hotel', 'null', 2115, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2116, 'Nomura Hiroyuki JP', 'null', 2116, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2117, 'Nordic Hotels', 'null', 2117, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2118, 'NOSTOI', 'null', 2118, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2119, 'Novascotia Boutique Homes', 'null', 2119, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2120, 'Novia', 'null', 2120, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2121, 'Novum Hospitality', 'null', 2121, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2122, 'Nox Rentals_C', 'null', 2122, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2123, 'NRMA', 'null', 2123, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2124, 'NTA Tower', 'null', 2124, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2125, 'Nueh GH', 'null', 2125, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2126, 'Nugroho Puji', 'null', 2126, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2127, 'Nuibay Sunset', 'null', 2127, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2128, 'Nunez i Navarro', 'null', 2128, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2129, 'Nurma Warianti', 'null', 2129, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2130, 'Nusa Dua Beach', 'null', 2130, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2131, 'NvUps Villa', 'null', 2131, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2132, 'NXimending Guesthouse', 'null', 2132, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2133, 'Nyanse Homestay', 'null', 2133, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2134, 'Nyoman Conto', 'null', 2134, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2135, 'Nyoman Dana', 'null', 2135, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2136, 'Oak Grove Inn', 'null', 2136, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2137, 'Oaks Hotels & Resorts', 'null', 2137, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2138, 'Oakwood Worldwide', 'null', 2138, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2139, 'OASIS HOTELS AND RESORTS', 'null', 2141, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2140, 'Oasis Apartments', 'null', 2139, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2141, 'Oasis Collections_C', 'null', 2140, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2142, 'Oberoi Hotels & Resorts', 'null', 2142, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2143, 'Occidental Hotels & Resorts', 'null', 2143, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2144, 'Ocean Link', 'null', 2144, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2145, 'Oceania Hotels', 'null', 2145, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2146, 'Octo Property Management', 'null', 2146, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2147, 'OD Group', 'null', 2147, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2148, 'Odalys Vacances', 'null', 2148, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2149, 'Oeoe', 'null', 2149, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2150, 'OeP', 'null', 2150, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2151, 'Office Fortune', 'null', 2151, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2152, 'Ohla', 'null', 2152, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2153, 'Ohruri Group', 'null', 2153, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2154, 'Oikos', 'null', 2154, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2155, 'OK Hotels', 'null', 2155, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2156, 'Okura Hotels and Resorts', 'null', 2156, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2157, 'Okura Nikko Hotels', 'null', 2157, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2158, 'Old Town Hotels', 'null', 2158, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2159, 'Olimpic Golden Tulip', 'null', 2159, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2160, 'Olivia Hotels', 'null', 2160, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2161, 'Olleh Private House', 'null', 2161, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2162, 'Omenahotelli', 'null', 2162, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2163, 'Omni Hotels & Resorts', 'null', 2163, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2164, 'Omran Hospitality', 'null', 2164, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2165, 'Onasol Hotels', 'null', 2165, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2166, 'ONDA (Zari)', 'null', 2166, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2167, 'ONDA (Zari) Homes', 'null', 2167, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2168, 'Onda YCS', 'null', 2168, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2169, 'One Avenue', 'null', 2169, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2170, 'One Day Guesthouse', 'null', 2170, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2171, 'One Host', 'null', 2171, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2172, 'One Perfect Stay', 'null', 2172, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2173, 'One Property', 'null', 2173, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2174, 'One&Only', 'null', 2174, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2175, 'Oneday More', 'null', 2175, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2176, 'Onk propeties', 'null', 2176, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2177, 'ONOMO Hotel Group', 'null', 2177, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2178, 'Onyx Hospitality', 'null', 2178, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2179, 'Ooedo Onsen Monogatari Hotels & Resorts', 'null', 2179, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2180, 'Oops', 'null', 2180, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2181, 'Operaramblas Sans', 'null', 2181, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2182, 'Orange Hotels', 'null', 2182, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2183, 'Orange Premier', 'null', 2183, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2184, 'Orchardz Hotels', 'null', 2184, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2185, 'Orenz Pro', 'null', 2185, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2186, 'Orient-Express Hotels', 'null', 2186, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2187, 'Origami Start_C', 'null', 2187, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2188, 'Orion Hotel Group', 'null', 2188, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2189, 'Orion Hotels and Resorts', 'null', 2189, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2190, 'Orix Real Estate', 'null', 2190, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2191, 'Orkid Inn', 'null', 2191, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2192, 'Orlando Holiday Rental Homes_C', 'null', 2192, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2193, 'Ormond Group', 'null', 2193, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2194, 'Oro Beach Houses', 'null', 2194, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2195, 'Oro oro ombo group', 'null', 2195, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2196, 'ORR Real Estate', 'null', 2196, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2197, 'OrtigasRealEstate.COM,CO', 'null', 2197, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2198, 'Osaka GLI(DNU)', 'null', 2198, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2199, 'Osaki BNB JP', 'null', 2199, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2200, 'Otomari', 'null', 2200, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2201, 'Oukaen_JP', 'null', 2201, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2202, 'Outrigger Hotels & Resorts', 'null', 2202, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2203, 'Ovolo Group', 'null', 2203, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2204, 'OX Consulting', 'null', 2204, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2205, 'Oxford Hotel Group', 'null', 2205, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2206, 'Oxford Hotels & Inns', 'null', 2206, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2207, 'OYO Rooms', 'null', 2207, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2208, 'P. Trower Co., Ltd', 'null', 2208, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2209, 'Pacific Investment', 'null', 2209, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2210, 'Pacific Palms Signature Properties', 'null', 2210, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2211, 'Pacific Regency', 'null', 2211, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2212, 'Paires', 'null', 2212, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2213, 'Pajaree Property', 'null', 2213, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2214, 'PALACE RESORTS', 'null', 2214, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2215, 'Palladium Hotels Group', 'null', 2215, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2216, 'Palm Beach Holiday Rentals', 'null', 2216, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2217, 'Palm Living', 'null', 2217, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2218, 'Pam K Property', 'null', 2218, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2219, 'Pammy Property', 'null', 2219, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2220, 'Pan Pacific Hotels and Resorts', 'null', 2220, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2221, 'Panda Villa', 'null', 2221, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2222, 'Panita', 'null', 2222, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2223, 'Pansion Glory Aleluja_C', 'null', 2223, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2224, 'PaQwell Property', 'null', 2224, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2225, 'Paradise Properties Hua Hin', 'null', 2225, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2226, 'Parador Hotels & Resorts', 'null', 2226, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2227, 'Paradores Hotels', 'null', 2227, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2228, 'Paragon Village Indonesia', 'null', 2228, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2229, 'Paragon Villas', 'null', 2229, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2230, 'Parinayokp Properties', 'null', 2230, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2231, 'Paris Inn', 'null', 2231, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2232, 'Parisian Home_C', 'null', 2233, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2233, 'Paris-Nice Vacations', 'null', 2232, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2234, 'Park & Suites Apart - Hotels', 'null', 2234, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2235, 'Park Avenue Hotels & Suites', 'null', 2235, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2236, 'Park Hotels', 'null', 2236, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2237, 'Parkcity Everly Group', 'null', 2237, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2238, 'Parkes Estate', 'null', 2238, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2239, 'Parkland Group', 'null', 2239, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2240, 'Pass The Keys_C', 'null', 2240, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2241, 'Patria Hospitalities Pvt Ltd', 'null', 2241, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2242, 'Pattamaporn Property', 'null', 2242, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2243, 'Patton Hospitality', 'null', 2243, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2244, 'Paul', 'null', 2244, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2245, 'Peace Land Group', 'null', 2245, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2246, 'Pearl Hotel Management', 'null', 2246, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2247, 'Pearl-Continental Hotels & Resorts', 'null', 2247, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2248, 'Peeradech TH', 'null', 2248, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2249, 'Peermont Hotels, Casinos & Resorts', 'null', 2249, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2250, 'Pegasus', 'null', 2250, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2251, 'Pelangi Rooms', 'null', 2251, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2252, 'Pelican Stay', 'null', 2252, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2253, 'Pelita Property', 'null', 2253, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2254, 'Pendowo Huis Guesthouse', 'null', 2254, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2255, 'Penguin Homes', 'null', 2255, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2256, 'Peninsula Hotels', 'null', 2256, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2257, 'Penta Hotels', 'null', 2257, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2258, 'Per AQUUM Retreats, Resorts and Residence', 'null', 2258, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2259, 'Perfect Host Malaysia', 'null', 2259, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2260, 'Perfectly Paris', 'null', 2260, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2261, 'Perkasa Hotel', 'null', 2261, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2262, 'Pesonna Hotels', 'null', 2262, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2263, 'Pestana Hotels and Resorts', 'null', 2263, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2264, 'Pet Friendly Holiday Homes', 'null', 2264, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2265, 'Petit Grande Homes', 'null', 2265, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2266, 'Petogogan Residence', 'null', 2266, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2267, 'Petr Tomanek', 'null', 2267, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2268, 'Petra Zidar', 'null', 2268, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2269, 'Peymans', 'null', 2269, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2270, 'Ph Condohub', 'null', 2270, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2271, 'Phasute', 'null', 2271, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2272, 'Phatthraporn Property', 'null', 2272, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2273, 'PHC', 'null', 2273, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2274, 'Phi Yen', 'null', 2274, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2275, 'Phil Property Expert, Inc', 'null', 2275, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2276, 'Phillip Island Holiday Homes', 'null', 2276, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2277, 'PHM Hospitality', 'null', 2277, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2278, 'Phoenix Apartment Hanoi', 'null', 2278, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2279, 'Phoenix Inn Suites', 'null', 2279, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2280, 'Phuket Direct', 'null', 2280, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2281, 'Phuket Island Property Services Co., Ltd', 'null', 2281, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2282, 'Phuket Marbella', 'null', 2282, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2283, 'Phuket Property Expert Co., Ltd', 'null', 2283, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2284, 'Phuket Real Estate Focus', 'null', 2284, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2285, 'Phuket Real Living', 'null', 2285, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2286, 'Phuket Rent Mix', 'null', 2286, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2287, 'Phuket Vacation Homes', 'null', 2287, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2288, 'PhuketAREA-com', 'null', 2288, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2289, 'Phuketas', 'null', 2289, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2290, 'Piao Home Inn', 'null', 2290, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2291, 'Pick A Flat', 'null', 2291, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2292, 'Pico De Loro Cove Condotel', 'null', 2292, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2293, 'Piks Keys', 'null', 2293, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2294, 'Pimprajan Property', 'null', 2294, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2295, 'Pinnacle Tourism Marketing', 'null', 2295, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2296, 'Pinoy Properties', 'null', 2296, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2297, 'Pintula V', 'null', 2297, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2298, 'Pipi', 'null', 2298, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2299, 'Pitaria', 'null', 2299, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2300, 'Pittinun Property', 'null', 2300, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2301, 'PK Holiday Homes', 'null', 2301, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2302, 'PK Park', 'null', 2302, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2303, 'Place2Stay Hotels', 'null', 2303, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2304, 'Placeholder', 'null', 2304, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2305, 'Plateno', 'null', 2305, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2306, 'Platinum Management', 'null', 2306, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2307, 'Platinum Stay', 'null', 2307, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2308, 'Play Residence', 'null', 2308, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2309, 'Playasol', 'null', 2309, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2310, 'Plays_JP', 'null', 2310, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2311, 'Plaza Premium Lounge', 'null', 2311, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2312, 'Plus One', 'null', 2312, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2313, 'Plus Property', 'null', 2313, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2314, 'Plush NHA', 'null', 2314, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2315, 'PM Malang', 'null', 2315, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2316, 'PML Apartments', 'null', 2316, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2317, 'Point A Hotels', 'null', 2317, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2318, 'Pondok Adi Mangement', 'null', 2318, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2319, 'PONDOK BRILLIANT', 'null', 2319, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2320, 'Pondok Nyaman 15', 'null', 2320, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2321, 'PONDOK PUJI', 'null', 2321, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2322, 'PONDOK SERUNI', 'null', 2322, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2323, 'Pondok Simpang Tiga / Amri', 'null', 2323, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2324, 'Port Fairy Holiday Rentals', 'null', 2324, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2325, 'Port Stephens Accommodation_C (Accom Nelson Bay)', 'null', 2325, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2326, 'Portfolio Niseko', 'null', 2326, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2327, 'Posadas', 'null', 2327, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2328, 'Position Perfect Accommodation Group', 'null', 2328, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2329, 'Prague for you_C', 'null', 2329, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2330, 'Prague Residences', 'null', 2330, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2331, 'Prague-hotel UK', 'null', 2331, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2332, 'Prajas Homestay', 'null', 2332, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2333, 'Pramana Hotels & Resorts', 'null', 2333, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2334, 'Pramana Villa Management', 'null', 2334, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2335, 'Prapassorn', 'null', 2335, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2336, 'Prasi Hospitality', 'null', 2336, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2337, 'Pratiwi Villas', 'null', 2337, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2338, 'Precious Suites', 'null', 2338, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2339, 'Preferred Hotel Group', 'null', 2339, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2340, 'Premier Hotels', 'null', 2340, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2341, 'Premier Inn International', 'null', 2341, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2342, 'Premiere Group', 'null', 2342, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2343, 'Premium Beach VT', 'null', 2343, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2344, 'Premium Holidays', 'null', 2344, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2345, 'Prestige Phuket', 'null', 2345, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2346, 'Prevera Homes', 'null', 2346, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2347, 'PRINCESS HOTELS AND RESORTS', 'null', 2351, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2348, 'Pricise Hotels', 'null', 2347, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2349, 'Prima', 'null', 2348, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2350, 'PrimeUCD_JP', 'null', 2349, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2351, 'Prince Hotels', 'null', 2350, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2352, 'Principal Hayley Hotels and Conference Venues', 'null', 2352, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2353, 'Prisa', 'null', 2353, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2354, 'Prisma Utama Group', 'null', 2354, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2355, 'Private Hotels', 'null', 2355, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2356, 'Private Ubud Villas', 'null', 2356, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2357, 'Pro Phuket', 'null', 2357, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2358, 'Promenade Hotels & Resort', 'null', 2358, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2359, 'Property Management Duncan', 'null', 2359, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2360, 'Property Management Queenstown', 'null', 2360, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2361, 'Property Providers', 'null', 2361, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2362, 'Protea Hotels', 'null', 2362, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2363, 'Provenance Hotels', 'null', 2363, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2364, 'Proviewland', 'null', 2364, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2365, 'PRS @ One Palm Tree Villas', 'null', 2365, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2366, 'PT Sixteen Event', 'null', 2366, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2367, 'PTOP Rent_C', 'null', 2367, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2368, 'PTT Group', 'null', 2368, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2369, 'Pub Rooms', 'null', 2369, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2370, 'PubLove', 'null', 2370, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2371, 'Pueblo Bonito Hotels and Resorts', 'null', 2371, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2372, 'Puerta del Sol', 'null', 2372, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2373, 'Pulitzer and Regina', 'null', 2373, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2374, 'Puncak Holiday', 'null', 2374, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2375, 'Punthill Apartment Hotels', 'null', 2375, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2376, 'Puri Landu', 'null', 2376, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2377, 'Puri Phunix Babarsari', 'null', 2377, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2378, 'Puri Sabina', 'null', 2378, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2379, 'Puri Senayan', 'null', 2379, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2380, 'Puri Vilas Indonesia', 'null', 2380, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2381, 'Purimas Group', 'null', 2381, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2382, 'PUTH', 'null', 2382, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2383, 'Puti Sari banilai Homestay', 'null', 2383, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2384, 'Q Hotels Group', 'null', 2384, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2385, 'Q properties', 'null', 2385, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2386, 'Q Square Garden Apartment', 'null', 2386, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2387, 'QB House', 'null', 2388, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2388, 'Qhome', 'null', 2389, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2389, 'Q-Home Apartments Group', 'null', 2387, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2390, 'Qiansu', 'null', 2390, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2391, 'Qifa Group', 'null', 2391, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2392, 'QT Hotels & Resorts', 'null', 2392, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2393, 'Quality Rentals Pattaya', 'null', 2393, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2394, 'Quay Holidays_C', 'null', 2394, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2395, 'Quentin Hotels', 'null', 2395, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2396, 'Quest Serviced Apartments', 'null', 2396, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2397, 'QUOC TRAVEL', 'null', 2397, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2398, 'R Guesthouse', 'null', 2398, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2399, 'R.Resort', 'null', 2399, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2400, 'R2 Residence', 'null', 2400, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2401, 'RAC Parks and Resorts', 'null', 2401, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2402, 'Raconteur Matsumoto', 'null', 2402, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2403, 'RACV Resorts', 'null', 2403, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2404, 'Radiance Hospitality Group', 'null', 2404, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2405, 'Radisson Hotel Group', 'null', 2405, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2406, 'Raditya Villa', 'null', 2406, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2407, 'Rafael Zulueta', 'null', 2407, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2408, 'Rafaelhoteles', 'null', 2408, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2409, 'Rafat Abuaqel Properties', 'null', 2409, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2410, 'Rafleys Costa del Sol_C', 'null', 2410, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2411, 'RAISYA KOST', 'null', 2415, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2412, 'Raimon Warmasen', 'null', 2411, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2413, 'Raine & Horne (Central Coast Holidays)', 'null', 2412, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2414, 'Raine & Horne Snowy Mountains', 'null', 2413, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2415, 'Rainstay', 'null', 2414, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2416, 'Raja Apartemen', 'null', 2416, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2417, 'Rajawali Property', 'null', 2417, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2418, 'Raka Group', 'null', 2418, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2419, 'Rakuna', 'null', 2419, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2420, 'Ramayana Hotels Group', 'null', 2420, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2421, 'Ramee Group of Hotels, Resorts and Apartments', 'null', 2421, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2422, 'Raoum Inn', 'null', 2422, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2423, 'RAPPOCINI', 'null', 2423, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2424, 'Rastasya Irawan', 'null', 2424, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2425, 'Rava Home', 'null', 2425, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2426, 'Raw Management', 'null', 2426, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2427, 'Ray White Cairns Beaches Holiday Collection', 'null', 2427, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2428, 'RDG Properties', 'null', 2428, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2429, 'Real Hospitality Group', 'null', 2429, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2430, 'Realizer', 'null', 2430, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2431, 'Recommended', 'null', 2431, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2432, 'Red Carnation Hotel Collection', 'null', 2432, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2433, 'Red Lion Hotels', 'null', 2433, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2434, 'Red Planet', 'null', 2434, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2435, 'Red Roof Inn', 'null', 2435, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2436, 'RedDoorz', 'null', 2436, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2437, 'Reez', 'null', 2437, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2438, 'Reffan Property', 'null', 2438, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2439, 'Reflect Property', 'null', 2439, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2440, 'Refre Forum', 'null', 2440, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2441, 'Regal Hotels International', 'null', 2441, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2442, 'Regalia', 'null', 2442, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2443, 'Regent International Hotels', 'null', 2443, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2444, 'Rejuvenate Stays', 'null', 2444, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2445, 'Relais & Chateaux', 'null', 2445, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2446, 'Relax Street', 'null', 2446, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2447, 'RelaxAway', 'null', 2447, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2448, 'Release Wanaka', 'null', 2448, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2449, 'Remar Hotels', 'null', 2449, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2450, 'Remember Trip', 'null', 2450, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2451, 'Rena', 'null', 2451, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2452, 'Rendi', 'null', 2452, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2453, 'Rent A Villa Phuket', 'null', 2453, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2454, 'Rent House Pattaya', 'null', 2454, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2455, 'Rent In Rome', 'null', 2455, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2456, 'Rent Suites', 'null', 2456, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2457, 'Rental Management', 'null', 2458, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2458, 'Rental Villa Bali', 'null', 2459, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2459, 'Rentaloka', 'null', 2460, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2460, 'Rentals Short Term', 'null', 2461, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2461, 'rent-apartment.com', 'null', 2457, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2462, 'RentClass', 'null', 2462, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2463, 'Rentmystay', 'null', 2463, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2464, 'Rentopolis', 'null', 2464, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2465, 'Rents In Phuket', 'null', 2465, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2466, 'Reqrea', 'null', 2466, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2467, 'Residence Tokyo', 'null', 2467, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2468, 'Residhome', 'null', 2468, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2469, 'Resol Hotel and Resort', 'null', 2469, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2470, 'Resol_JP', 'null', 2470, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2471, 'Resort Homes of Florida_C', 'null', 2471, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2472, 'Resort Izumigo', 'null', 2472, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2473, 'Resort Trust', 'null', 2473, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2474, 'Resorts Suites', 'null', 2474, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2475, 'Resorts World', 'null', 2475, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2476, 'Resorts World Genting', 'null', 2476, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2477, 'Rest Night Group', 'null', 2477, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2478, 'Retaj Hotels', 'null', 2478, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2479, 'Retreats Victoria', 'null', 2479, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2480, 'Revan Ray', 'null', 2480, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2481, 'Revanaya Bacpacker Room', 'null', 2481, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2482, 'RevFox', 'null', 2482, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2483, 'Revina Purnama Apartment', 'null', 2483, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2484, 'Rex Properties', 'null', 2484, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2485, 'Rey-Alexandra Suites', 'null', 2485, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2486, 'RHR', 'null', 2486, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2487, 'RIMBA DESA Jepara', 'null', 2494, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2488, 'Richard Cordero', 'null', 2488, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2489, 'Richmond Hotel', 'null', 2489, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2490, 'Richmonde Hotels & Resorts', 'null', 2490, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2491, 'Ridwan Property', 'null', 2491, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2492, 'Rihga Royal Hotels', 'null', 2492, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2493, 'Riki Metro Suite Apartment', 'null', 2493, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2494, 'Rimonim', 'null', 2495, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2495, 'Ring Bali Property', 'null', 2496, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2496, 'Ringhotels', 'null', 2497, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2497, 'Rinn Group', 'null', 2498, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2498, 'Risa Phuket Property', 'null', 2499, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2499, 'Risshisha_JP', 'null', 2500, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2500, 'Ritz Garden', 'null', 2501, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2501, 'Riu Hotels and Resorts', 'null', 2502, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2502, 'River Hotels', 'null', 2503, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2503, 'Riverview Hotel Group', 'null', 2504, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2504, 'Riviera House', 'null', 2505, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2505, 'Riviera VIP Real Estate_C', 'null', 2506, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2506, 'Rixos World Wide', 'null', 2507, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2507, 'Ri-Yaz Hotels & Resorts', 'null', 2487, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2508, 'RnR Serviced Apartments', 'null', 2508, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2509, 'Robert Property', 'null', 2509, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2510, 'Robinsons Land Corporation', 'null', 2510, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2511, 'Rocco Forte Collection', 'null', 2511, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2512, 'Rock Stay', 'null', 2512, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2513, 'Roda Group', 'null', 2513, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2514, 'Rodd Hotels & Resorts', 'null', 2514, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2515, 'Rodeway Inn', 'null', 2515, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2516, 'Roemah Abdoe Lembah Harau', 'null', 2516, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2517, 'Roemah Djogja Guest House', 'null', 2517, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2518, 'Rofusesignature Management', 'null', 2518, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2519, 'Roheda Group', 'null', 2519, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2520, 'Roi Putra', 'null', 2520, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2521, 'Roisa Apartments', 'null', 2521, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2522, 'Rojen Holiday Homes', 'null', 2522, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2523, 'Rokon Group', 'null', 2523, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2524, 'Roland Konrad Properties', 'null', 2524, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2525, 'Rome-unique_C', 'null', 2525, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2526, 'Ronda BCN House', 'null', 2526, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2527, 'Rongshu Gongguan', 'null', 2527, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2528, 'RoniaVilla', 'null', 2528, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2529, 'Room & Revenue', 'null', 2529, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2530, 'Room Mate Hotels', 'null', 2530, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2531, 'Room Panda', 'null', 2531, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2532, 'Room Rentals Cebu', 'null', 2532, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2533, 'Room Service Spain_C', 'null', 2533, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2534, 'room.rental.japan', 'null', 2534, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2535, 'Room007', 'null', 2535, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2536, 'Room22Phuket', 'null', 2536, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2537, 'RoomInger_C', 'null', 2537, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2538, 'Roomku @ Bassura City', 'null', 2538, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2539, 'Roomme', 'null', 2539, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2540, 'Roomromo', 'null', 2540, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2541, 'Ros Harries Marketing', 'null', 2541, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2542, 'Rosadi Rachman', 'null', 2542, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2543, 'Rose Hotels & Apartments', 'null', 2543, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2544, 'Rosedale', 'null', 2544, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2545, 'Roseland Hotels & Spa Group', 'null', 2545, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2546, 'Rosetta Homestay', 'null', 2546, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2547, 'Rosewood Hotels & Resorts', 'null', 2547, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2548, 'Rotana', 'null', 2548, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2549, 'Route Inn Hotels', 'null', 2549, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2550, 'Rove Hotels', 'null', 2550, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2551, 'Royal Orchid Hotels', 'null', 2551, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2552, 'Royal Park Hotels', 'null', 2552, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2553, 'ROYAL RESORTS', 'null', 2553, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2554, 'Royal Total Internasional', 'null', 2554, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2555, 'Royal Twin Group', 'null', 2555, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2556, 'Royale Chulan Hotels & Resorts', 'null', 2556, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2557, 'Rozd Holiday Homes', 'null', 2557, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2558, 'RPGC', 'null', 2558, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2559, 'RTSJ Real Estate Brokerage', 'null', 2559, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2560, 'RU Master_C', 'null', 2560, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2561, 'Ruby Group', 'null', 2561, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2562, 'Rudy Gunawan', 'null', 2562, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2563, 'Rujia Condotel Corp', 'null', 2563, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2564, 'Rully Property', 'null', 2564, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2565, 'RUMAH BUNDA', 'null', 2565, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2566, 'Rumah Desa', 'null', 2566, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2567, 'Rumah Kost Kualanamu', 'null', 2567, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2568, 'Rumah Kost Mahmud', 'null', 2568, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2569, 'Rumah Nenek', 'null', 2569, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2570, 'Rumah Rahman', 'null', 2570, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2571, 'Ruoyu Asset', 'null', 2571, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2572, 'Ryan Eka Putra', 'null', 2572, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2573, 'Rydges Hotels & Resorts', 'null', 2573, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2574, 'S&N', 'null', 2574, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2575, 'S&W Investment', 'null', 2575, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2576, 'SA Agency Services_C', 'null', 2576, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2577, 'Saba Suites', 'null', 2577, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2578, 'Sabai Group', 'null', 2578, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2579, 'SACO Apartments', 'null', 2579, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2580, 'Safeer Hotels', 'null', 2580, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2581, 'Safeer Hotels and Tourism Company', 'null', 2581, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2582, 'Safestay group', 'null', 2582, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2583, 'Safestay Hostels', 'null', 2583, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2584, 'Safir Hotels & Resorts', 'null', 2584, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2585, 'Safitour', 'null', 2585, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2586, 'Sahara', 'null', 2586, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2587, 'Sahid Hotels', 'null', 2587, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2588, 'Saigon Corner', 'null', 2588, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2589, 'Saigon Home Party', 'null', 2589, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2590, 'Saigon Lotus', 'null', 2590, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2591, 'Saigon Rooms', 'null', 2591, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2592, 'Saigon Sweethome', 'null', 2592, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2593, 'Saigonhost', 'null', 2593, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2594, 'Sain Property', 'null', 2594, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2595, 'Saitarn Samui', 'null', 2595, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2596, 'Sala Hospitality Group', 'null', 2596, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2597, 'Salam Group', 'null', 2597, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2598, 'Salles Hotels', 'null', 2598, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2599, 'Sam Xu', 'null', 2599, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2600, 'Sama Sama Holiday Homes', 'null', 2600, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2601, 'Samastha Indonesia', 'null', 2601, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2602, 'Samed Resorts Group', 'null', 2602, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2603, 'Sami Firdavs', 'null', 2603, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2604, 'Samui Dream Villas', 'null', 2604, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2605, 'Samui Holiday Villa Rental', 'null', 2605, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2606, 'Sana Hotels', 'null', 2606, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2607, 'Sanco Inn Hotels', 'null', 2607, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2608, 'Sandals & Beaches Resorts', 'null', 2608, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2609, 'Sandman Group', 'null', 2609, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2610, 'Sandra', 'null', 2610, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2611, 'Sandra Property', 'null', 2611, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2612, 'Sands China', 'null', 2612, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2613, 'Sanremo Oasis Condominium', 'null', 2613, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2614, 'Sant Jordi', 'null', 2614, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2615, 'Santa Grand Hotels', 'null', 2615, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2616, 'Santika Indonesia Hotels & Resorts', 'null', 2616, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2617, 'Santoni\'s Place', 'null', 2617, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2618, 'SARASA Hotels', 'null', 2618, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2619, 'Sari Pacifica Hotel & Resorts', 'null', 2619, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2620, 'Sarovar Hotels & Resorts', 'null', 2620, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2621, 'Sasmito Group', 'null', 2621, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2622, 'Sateraito Office', 'null', 2622, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2623, 'Savi Rooms', 'null', 2623, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2624, 'Sawa Muli Resort', 'null', 2624, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2625, 'Sawasdee Hotels Group', 'null', 2625, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2626, 'SB Hotels', 'null', 2626, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2627, 'Scandic Hotels', 'null', 2627, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2628, 'Scenic Hotel Group', 'null', 2628, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2629, 'Schick Hotels', 'null', 2629, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2630, 'Scuba Tribe Bali', 'null', 2630, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2631, 'Sea Lion House', 'null', 2631, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2632, 'Sea N\' Rent', 'null', 2632, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2633, 'Seaclub', 'null', 2633, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2634, 'Seashells Hospitality Group', 'null', 2634, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2635, 'Seaside Holiday Home Accommodation', 'null', 2635, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2636, 'Seasons Apartment Hotel Group', 'null', 2636, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2637, 'SeaStay Vung Tau', 'null', 2637, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2638, 'Secluded Bali Villas', 'null', 2638, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2639, 'Second Homes', 'null', 2639, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2640, 'Seda Hotels', 'null', 2640, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2641, 'Sederhana Homestay', 'null', 2641, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2642, 'Sejahtera Apartments', 'null', 2642, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2643, 'Sekai Hotel', 'null', 2643, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2644, 'Select Hotels Group', 'null', 2644, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2645, 'Selog Villa', 'null', 2645, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2646, 'Selvy', 'null', 2646, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2647, 'Sensation Apartments', 'null', 2647, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2648, 'Sentana Management', 'null', 2648, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2649, 'Sentoria Group', 'null', 2649, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2650, 'Sentra Timur Residence by Angelica', 'null', 2650, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2651, 'Sentra Timur Residence by Tulus', 'null', 2651, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2652, 'Seoul Apartment', 'null', 2652, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2653, 'Sercotel Hotels', 'null', 2653, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2654, 'Serena Hotels', 'null', 2654, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2655, 'Serena\'s House', 'null', 2655, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2656, 'Serenata Hotels & Resorts', 'null', 2656, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2657, 'Serenity Pattaya', 'null', 2657, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2658, 'Serenity Twin Villa', 'null', 2658, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2659, 'Serviced Houses', 'null', 2659, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2660, 'Servigroup Hotels', 'null', 2660, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2661, 'Seven Peaks Property', 'null', 2661, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2662, 'Seven Trumpets', 'null', 2662, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2663, 'Sewa in Villa', 'null', 2663, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2664, 'SGP', 'null', 2664, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2665, 'Shaftesbury Hotels', 'null', 2665, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2666, 'Shamrocks Rooms', 'null', 2666, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2667, 'Shangri-La Hotels and Resorts', 'null', 2667, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2668, 'Shangrila Mountain View Baguio', 'null', 2668, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2669, 'Shanika Properties @ F1 BGC', 'null', 2669, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2670, 'Shanshui Trends Hotel', 'null', 2670, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2671, 'Shanti Collection', 'null', 2671, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2672, 'Share Japan', 'null', 2672, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2673, 'SHCR', 'null', 2673, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2674, 'Shen Yu Lin', 'null', 2674, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2675, 'SHI Inc.', 'null', 2675, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2676, 'Shiadu', 'null', 2676, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2677, 'Shiki Group', 'null', 2677, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2678, 'Shilla Hotel', 'null', 2678, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2679, 'Shilo Inns', 'null', 2679, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2680, 'Shindom Inn', 'null', 2680, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2681, 'Shinju Apartment', 'null', 2681, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2682, 'SHKP Hotels', 'null', 2682, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2683, 'Shoney\'s Inn', 'null', 2683, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2684, 'Short Booking Holiday Homes', 'null', 2684, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2685, 'Short Letting_C', 'null', 2685, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2686, 'Short Rental Perth', 'null', 2686, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2687, 'Short Stay Group apartments', 'null', 2687, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2688, 'Short Stay Plus', 'null', 2688, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2689, 'Short Stay Plus AU', 'null', 2689, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2690, 'ShortOZ Stay', 'null', 2690, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2691, 'ShortstayPh Inc.', 'null', 2691, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2692, 'ShortStayPoland', 'null', 2692, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2693, 'ShuGuang Hotel Group', 'null', 2693, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2694, 'SILK Experience Excellence', 'null', 2700, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2695, 'Siam Hotel Group', 'null', 2694, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2696, 'Sichapak Property', 'null', 2695, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2697, 'Signature Group', 'null', 2696, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2698, 'Signature Holiday Homes', 'null', 2697, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2699, 'Siho Villa', 'null', 2698, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2700, 'Sila Dharma Hospitality Management', 'null', 2699, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2701, 'Silver Cloud Inns & Hotels', 'null', 2701, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2702, 'Silver Oaks Group', 'null', 2702, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2703, 'Silver Star (All Japan Ryokan Hotel Association)', 'null', 2703, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2704, 'Silverland Hotels & Spa Group', 'null', 2704, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2705, 'Simms Group', 'null', 2705, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2706, 'Simply Heaven', 'null', 2706, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2707, 'Simply Homy', 'null', 2707, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2708, 'Sina Hotels', 'null', 2708, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2709, 'Sinar Batu', 'null', 2709, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2710, 'Singapore Attractions', 'null', 2710, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2711, 'Singer Group', 'null', 2711, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2712, 'Sino Group', 'null', 2712, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2713, 'Sinta Putri', 'null', 2713, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2714, 'Sipadan Inn', 'null', 2714, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2715, 'Sirenis Hotels & Resorts', 'null', 2715, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2716, 'Sirinda Property', 'null', 2716, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2717, 'Siriwan Property', 'null', 2717, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2718, 'Sirkeci Group Hotels', 'null', 2718, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2719, 'Sitipong Property', 'null', 2719, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2720, 'Six Senses', 'null', 2720, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2721, 'SJM', 'null', 2721, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2722, 'Skalon Limited', 'null', 2722, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2723, 'Skandinavia by TC Property', 'null', 2723, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2724, 'Ski Japan', 'null', 2724, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2725, 'SkiJapan', 'null', 2725, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2726, 'Sky Dragon Hai Phong', 'null', 2726, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2727, 'Sky Hotel', 'null', 2727, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2728, 'Skycity', 'null', 2728, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2729, 'Skytary', 'null', 2729, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2730, 'Slaviero Hotels', 'null', 2730, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2731, 'Slavyanka Hotel Chain', 'null', 2731, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2732, 'Sleepover in NMMBA', 'null', 2732, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2733, 'SleepRest', 'null', 2733, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2734, 'SlifeAus_C', 'null', 2734, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2735, 'Slow Enjoy Living', 'null', 2735, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2736, 'Small Luxury Hotel of the World', 'null', 2736, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2737, 'Smart Hotel', 'null', 2737, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2738, 'Smiley Group', 'null', 2738, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2739, 'SMU Trading', 'null', 2739, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2740, 'SNC Management', 'null', 2740, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2741, 'SoftInn', 'null', 2741, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2742, 'Soho Suites KLCC by Aloha', 'null', 2742, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2743, 'SoHome Australia', 'null', 2743, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2744, 'Sokha Hotels & Resorts', 'null', 2744, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2745, 'Solare Hotels', 'null', 2745, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2746, 'SOLMAR HOTELS & RESORTS', 'null', 2746, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2747, 'Soluxe Hotel Group', 'null', 2747, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2748, 'Som', 'null', 2748, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2749, 'Somnuk', 'null', 2749, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2750, 'Sonders Property Management Corp', 'null', 2750, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2751, 'Sonesta', 'null', 2751, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2752, 'Sonya Stoyanova', 'null', 2752, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2753, 'SooBali', 'null', 2753, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2754, 'Sorell Hotels', 'null', 2754, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2755, 'Sotetsu Fresa Inn', 'null', 2755, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2756, 'Southouse Residence', 'null', 2756, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2757, 'Sozonext', 'null', 2757, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2758, 'Space Management_JP', 'null', 2758, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2759, 'Spain Rentals', 'null', 2759, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2760, 'SPB Citystar', 'null', 2760, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2761, 'Spicers Retreats, Hotels and Lodges', 'null', 2761, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2762, 'Spring Garden Villa', 'null', 2762, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2763, 'Squeeze', 'null', 2763, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2764, 'SR Vacation Rental', 'null', 2764, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2765, 'Sriya Homestay', 'null', 2765, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2766, 'SSAW Boutique Group', 'null', 2766, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2767, 'ST Vung Tau Apartment', 'null', 2767, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2768, 'Sta. Maria Homes', 'null', 2768, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2769, 'Stamford Hotels & Resorts', 'null', 2769, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2770, 'Stanford', 'null', 2770, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2771, 'Stanley', 'null', 2771, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2772, 'Starfish Properties', 'null', 2772, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2773, 'Starhotels', 'null', 2773, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2774, 'Starlight Suites Hotels', 'null', 2774, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2775, 'Starwood Hotels & Resorts Worldwide', 'null', 2775, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2776, 'Station Casino', 'null', 2776, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2777, 'Stay Barcelona Apartments_C', 'null', 2777, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2778, 'Stay Gold', 'null', 2778, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2779, 'Stay Humble', 'null', 2779, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2780, 'Stay in Cape Town', 'null', 2780, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2781, 'Stay in Paternoster', 'null', 2781, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2782, 'Stay in Villa Bandung', 'null', 2782, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2783, 'Stay Japan', 'null', 2783, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2784, 'Stay Kyoto', 'null', 2784, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2785, 'Stay Waiheke Holiday Homes', 'null', 2785, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2786, 'StayBeyond', 'null', 2786, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2787, 'Staybeyond_C', 'null', 2787, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2788, 'Staycation', 'null', 2788, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2789, 'Staycation Vacation Rentals_C', 'null', 2789, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2790, 'StayHome Asia', 'null', 2790, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2791, 'Staykeeper', 'null', 2791, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2792, 'Staynest_C', 'null', 2792, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2793, 'Staypineapple', 'null', 2793, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2794, 'StayWell Hospitality Group', 'null', 2794, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2795, 'Staywest Apartments', 'null', 2795, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2796, 'Stefanie Halim', 'null', 2796, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2797, 'Stella', 'null', 2797, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2798, 'Stellawood_JP', 'null', 2798, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2799, 'Sterling Hotels and Resorts', 'null', 2799, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2800, 'Stevanus Group', 'null', 2800, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2801, 'Stoneman Vacation Villa_C', 'null', 2801, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2802, 'StoneTree', 'null', 2802, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2803, 'Studio Apartment Korea', 'null', 2803, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2804, 'Stylers', 'null', 2804, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2805, 'Suba Group of Hotels', 'null', 2805, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2806, 'Subhome', 'null', 2806, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2807, 'Sublet4U HQ Ltd_C', 'null', 2807, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2808, 'Sudima', 'null', 2808, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2809, 'SuerteFortun', 'null', 2809, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2810, 'Suflindo Group', 'null', 2810, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2811, 'Suhelmiadi Adi', 'null', 2811, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2812, 'SuiwaGroup', 'null', 2812, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2813, 'Sukuragawa(Phoenix.lyx89)_JP', 'null', 2813, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2814, 'Sumberkima Hill Private Villa Retreat', 'null', 2814, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2815, 'Sumifuku_JP', 'null', 2815, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2816, 'SummerHouse Bali', 'null', 2816, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2817, 'Summit Group', 'null', 2817, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2818, 'Sumuka', 'null', 2818, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2819, 'Sun Inn Group', 'null', 2819, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2820, 'Sun International', 'null', 2820, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2821, 'Sun Resorts', 'null', 2821, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2822, 'Sun Siyam Resorts', 'null', 2822, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2823, 'Sunburst Holidays', 'null', 2823, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2824, 'Sunex Luxury Apartment', 'null', 2824, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2825, 'SunMei', 'null', 2825, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2826, 'Sunny Group', 'null', 2826, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2827, 'Sunny House', 'null', 2827, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2828, 'Sunny Thailand', 'null', 2828, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2829, 'Sunotel Grupo', 'null', 2829, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2830, 'SUNRISE HOUSE', 'null', 2832, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2831, 'SunRich_JP', 'null', 2830, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2832, 'Sunrise (xiangxiang.zhang)_JP', 'null', 2831, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2833, 'Sunroute Hotel', 'null', 2833, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2834, 'Sunshine Hotels & Resort', 'null', 2834, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2835, 'Sunshine Hotels & Resorts, Pattaya', 'null', 2835, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2836, 'Sunshine Samui Villas', 'null', 2836, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2837, 'Sunway Group', 'null', 2837, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2838, 'Sunway Hotels & Resorts', 'null', 2838, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2839, 'Sunworld Dynasty Hotel', 'null', 2839, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2840, 'Supaporn Property', 'null', 2840, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2841, 'Super 8', 'null', 2841, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2842, 'Super 8 Hotels', 'null', 2842, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2843, 'Super Hotel', 'null', 2843, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2844, 'Superlab', 'null', 2844, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2845, 'Supranational Hotels', 'null', 2845, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2846, 'Surapong Property', 'null', 2846, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2847, 'Sure Property', 'null', 2847, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2848, 'Suria', 'null', 2848, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2849, 'Surmeli Hotels & Resorts', 'null', 2849, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2850, 'Surya Hotels', 'null', 2850, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2851, 'Susan Group', 'null', 2851, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2852, 'Susan\'s Bali Villas', 'null', 2852, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2853, 'Sutera Harbour Resort', 'null', 2853, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2854, 'Sutton Place Hotel', 'null', 2854, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2855, 'Suzaku', 'null', 2855, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2856, 'Suzuka', 'null', 2856, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2857, 'Sweet Inn Apartments', 'null', 2857, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2858, 'Sweet Inn_C', 'null', 2858, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2859, 'Swell Stays', 'null', 2859, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2860, 'Swisbell Hotel International', 'null', 2860, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2861, 'Swiss Garden International', 'null', 2861, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2862, 'Swiss International Hotels & Resorts', 'null', 2862, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2863, 'Swiss Quality Hotels International', 'null', 2863, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2864, 'Swiss Youth Hostels', 'null', 2864, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2865, 'Swiss-Belhotel International', 'null', 2865, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2866, 'Switch entertaiment', 'null', 2866, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2867, 'Syaeful', 'null', 2867, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2868, 'Syafic Homestay', 'null', 2868, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2869, 'Sydney Lodges', 'null', 2869, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2870, 'Sylvie Asa', 'null', 2870, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2871, 'T Stay', 'null', 2871, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2872, 'T+Hotel', 'null', 2872, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2873, 'Taga Home Vietnam', 'null', 2873, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2874, 'Tagaytay A-Staycation by Naya and Darla', 'null', 2874, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2875, 'Tagaytay Staycation', 'null', 2875, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2876, 'Tagaytay Vacation House', 'null', 2876, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2877, 'TAI PHAT Hold Family Business', 'null', 2877, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2878, 'Tai Tai House', 'null', 2878, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2879, 'Tainan Gulf Castle', 'null', 2879, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2880, 'Taipei Meets', 'null', 2880, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2881, 'Taj Hotels Resorts and Palaces', 'null', 2881, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2882, 'TAKE Consulting_JP', 'null', 2882, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2883, 'Takumi', 'null', 2883, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2884, 'Tam House Villa', 'null', 2884, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2885, 'Tamalate Residence Group', 'null', 2885, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2886, 'Taman Paradise', 'null', 2886, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2887, 'Taman Sari Bali Villas Kerobokan', 'null', 2887, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2888, 'Taman Tirta Lovina', 'null', 2888, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2889, 'Tamora', 'null', 2889, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2890, 'Tamu Seseh', 'null', 2890, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2891, 'Tan Long', 'null', 2891, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2892, 'Tang Dynasty Group of Hotels', 'null', 2893, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2893, 'Tang’s Living Group', 'null', 2894, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2894, 'Tanglin Pakuwon', 'null', 2895, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2895, 'Tan\'s Apartment', 'null', 2892, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2896, 'Taruruhto JP', 'null', 2896, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2897, 'Taurus Bali', 'null', 2897, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2898, 'Tauzia Hotel Management', 'null', 2898, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2899, 'Tavers Pension House', 'null', 2899, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2900, 'Taylor Korea', 'null', 2900, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2901, 'TD Hotels Group', 'null', 2901, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2902, 'Teddy Yudi', 'null', 2902, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2903, 'Tedi', 'null', 2903, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2904, 'Tembi Rumah Budaya', 'null', 2904, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2905, 'Terrace Hill Resort', 'null', 2905, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2906, 'test', 'null', 2906, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2907, 'Test Chain', 'null', 2907, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2908, 'Test Hotel', 'null', 2908, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2909, 'TFE Hotels', 'null', 2909, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2910, 'Thai Homeaway', 'null', 2910, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2911, 'Thai Nice @ 15 Sukhumvit Residence', 'null', 2911, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2912, 'Thai Property Group', 'null', 2912, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2913, 'Thai Smile Property', 'null', 2913, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2914, 'Thailand Holiday Homes', 'null', 2914, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2915, 'Thaittaya', 'null', 2915, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2916, 'Tham Yeo', 'null', 2916, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2917, 'Thanh Nga', 'null', 2917, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2918, 'Thansasit', 'null', 2918, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2919, 'Thao Lieu', 'null', 2919, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2920, 'THC Hostels', 'null', 2920, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2921, 'The Ark Retreat', 'null', 2921, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2922, 'The Backpacker Group', 'null', 2922, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2923, 'The Baile Apartment', 'null', 2923, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2924, 'The Bali Agent', 'null', 2924, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2925, 'The Batu Villa', 'null', 2925, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2926, 'The Berry Luxury Apartments', 'null', 2926, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2927, 'THE BnB Bandung Metro Indah', 'null', 2927, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2928, 'The Boutique Collection', 'null', 2928, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2929, 'The Cabin', 'null', 2929, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2930, 'The Cabin Property', 'null', 2930, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2931, 'The Cairn Collection', 'null', 2931, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2932, 'The Capital Hotels & Apartments', 'null', 2932, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2933, 'The Como Serviced Apartment', 'null', 2933, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2934, 'The Concierge Guru_C', 'null', 2934, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2935, 'The Damai Master Pool Villa', 'null', 2935, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2936, 'The Doors', 'null', 2936, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2937, 'The Fern Hotels and Resorts', 'null', 2937, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2938, 'THE FIVE', 'null', 2938, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2939, 'The Green Park', 'null', 2939, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2940, 'The Heritage Hotels Group', 'null', 2940, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2941, 'THE HOMEE', 'null', 2941, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2942, 'The Jerai Hotels & Resorts', 'null', 2942, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2943, 'The Jones', 'null', 2943, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2944, 'The Klagan', 'null', 2944, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2945, 'The Kraton Homestay', 'null', 2945, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2946, 'The Kunci', 'null', 2946, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2947, 'The Lane Bay Villa', 'null', 2947, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2948, 'The Leela Palaces, Hotels and Resorts', 'null', 2948, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2949, 'The Leverage Hotel', 'null', 2949, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2950, 'The Local Stay_C', 'null', 2950, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2951, 'The London Agent', 'null', 2951, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2952, 'The Lowy Group', 'null', 2952, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2953, 'The Luxe Nomad', 'null', 2953, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2954, 'The Luxe Residences', 'null', 2954, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2955, 'The Luxury Signature', 'null', 2955, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2956, 'The Luxury Topview Phuket', 'null', 2956, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2957, 'The Maharajo Guest House Jakarta', 'null', 2957, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2958, 'The Mantis Collection', 'null', 2958, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2959, 'The Marmara Collection', 'null', 2959, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2960, 'The Maruca Group_C', 'null', 2960, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2961, 'The Memories Cottage', 'null', 2961, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2962, 'The Nox Group', 'null', 2962, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2963, 'The Palacio de Laoag', 'null', 2963, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2964, 'The Patra', 'null', 2964, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2965, 'The Pride Hotels', 'null', 2965, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2966, 'The Red House Company', 'null', 2966, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2967, 'The Regency Group', 'null', 2967, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2968, 'The Scout Group', 'null', 2968, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2969, 'The Star Entertainment Group', 'null', 2969, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2970, 'The Streets Apartments', 'null', 2970, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2971, 'The Student Hotel', 'null', 2971, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2972, 'The Sunset Hotel Group', 'null', 2972, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2973, 'The Three Corners', 'null', 2973, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2974, 'The Trump Hotel Collection', 'null', 2974, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2975, 'The Unique Collection of Hotels and Resorts', 'null', 2975, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2976, 'The Venues Collection', 'null', 2976, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2977, 'The Zonvil Condominium', 'null', 2977, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2978, 'THHR', 'null', 2978, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2979, 'Thi Hoang Homes', 'null', 2979, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2980, 'Thien Kim Real', 'null', 2980, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2981, 'Think Hotels', 'null', 2981, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2982, 'Thippawan Property', 'null', 2982, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2983, 'Thistle & Guoman', 'null', 2983, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2984, 'Thon Hotels', 'null', 2984, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2985, 'Three A Corporation', 'null', 2985, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2986, 'Three Oaks Group', 'null', 2986, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2987, 'Thunder Storm Property', 'null', 2987, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2988, 'TIHG', 'null', 2990, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2989, 'TIME Hotels', 'null', 2991, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2990, 'Tianlun International Hotels', 'null', 2988, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2991, 'Tiara Court', 'null', 2989, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2992, 'Times Japan', 'null', 2992, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2993, 'Times Square NHA', 'null', 2993, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2994, 'Timhotels', 'null', 2994, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2995, 'Tirto 27', 'null', 2995, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2996, 'Titanic', 'null', 2996, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2997, 'Tivoli Hotels & Resorts', 'null', 2997, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2998, 'TOBU Group', 'null', 2998, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (2999, 'TOC Hostel & Suites', 'null', 2999, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3000, 'Toho Resort Hotels', 'null', 3000, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3001, 'Tokyo Furnished', 'null', 3001, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3002, 'Tokyu Hotels', 'null', 3002, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3003, 'Tokyu Stay', 'null', 3003, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3004, 'TOP 10 Holiday Parks', 'null', 3004, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3005, 'Topotels International', 'null', 3005, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3006, 'Toronto Motel Group', 'null', 3006, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3007, 'Total Bali', 'null', 3007, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3008, 'Touch of Spice', 'null', 3008, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3009, 'Tourism Adventure Group', 'null', 3009, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3010, 'Tourism By G', 'null', 3010, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3011, 'Toushin', 'null', 3011, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3012, 'Towers Real Estate', 'null', 3012, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3013, 'Towny', 'null', 3013, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3014, 'Toyoko Inn', 'null', 3014, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3015, 'Toyotomi', 'null', 3015, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3016, 'Traditional Hanok', 'null', 3016, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3017, 'Tran Duy Villa', 'null', 3017, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3018, 'Tran Suites Hanoi', 'null', 3018, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3019, 'Trans Vita d.o.o_C', 'null', 3019, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3020, 'Travel Estate', 'null', 3020, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3021, 'Travel Gate Croatia_C', 'null', 3021, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3022, 'Travel Guidal', 'null', 3022, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3023, 'Travel Ouensha Kyoto', 'null', 3023, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3024, 'Travelbee Management Corporation', 'null', 3024, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3025, 'Traveler Genius', 'null', 3025, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3026, 'Travelessence K.K', 'null', 3026, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3027, 'Traveletc Apartments', 'null', 3027, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3028, 'Traveling Family Villa', 'null', 3028, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3029, 'Travelio', 'null', 3029, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3030, 'Traveller\'s Inn', 'null', 3030, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3031, 'Travelodge', 'null', 3031, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3032, 'Travelodge Europe', 'null', 3032, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3033, 'Treebo Hotels', 'null', 3033, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3034, 'Tribal Xperience Port Barton Town', 'null', 3034, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3035, 'Trim Off JP', 'null', 3035, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3036, 'Trip Inn', 'null', 3036, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3037, 'Trip Mall', 'null', 3037, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3038, 'Trip Pod', 'null', 3038, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3039, 'Trip11', 'null', 3039, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3040, 'Triple Apartment', 'null', 3040, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3041, 'Tripster Hub Travel Solutions', 'null', 3041, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3042, 'TripVillas', 'null', 3042, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3043, 'Tripvillas_C', 'null', 3043, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3044, 'Tritic Management', 'null', 3044, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3045, 'Triumph Hotels', 'null', 3045, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3046, 'Trivelles group', 'null', 3046, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3047, 'TRM Hospitality', 'null', 3047, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3048, 'TropicLook_C', 'null', 3048, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3049, 'Truc Phan', 'null', 3049, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3050, 'Trust In_C', 'null', 3050, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3051, 'TrustLight_JP', 'null', 3051, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3052, 'Trustrive', 'null', 3052, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3053, 'Tsokkos', 'null', 3053, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3054, 'Tsukigime Club', 'null', 3054, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3055, 'Tsuruga Group Hotels', 'null', 3055, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3056, 'Tujia Group', 'null', 3056, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3057, 'Tuli Hotels& Resorts', 'null', 3057, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3058, 'Tulip Hotel Group', 'null', 3058, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3059, 'TUMON VACATION RENTAL', 'null', 3059, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3060, 'Tune Hotels', 'null', 3060, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3061, 'Turim Hotels', 'null', 3061, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3062, 'Tuti Ha Long', 'null', 3062, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3063, 'Twin sea', 'null', 3063, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3064, 'Twist', 'null', 3064, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3065, 'Twizel Holiday Homes', 'null', 3065, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3066, 'Two Roads Hospitality', 'null', 3066, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3067, 'Two Villas Holiday', 'null', 3067, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3068, 'Ty House Danang', 'null', 3068, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3069, 'Tyson Properties', 'null', 3069, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3070, 'U Hotel Group', 'null', 3070, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3071, 'UBIQS', 'null', 3072, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3072, 'Ubud Batan Nyuh Bed Breakfast & Spa', 'null', 3073, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3073, 'Ubud Prime Villas', 'null', 3074, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3074, 'Ubud Property', 'null', 3075, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3075, 'Ubud Stay', 'null', 3076, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3076, 'UHG', 'null', 3077, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3077, 'Uhi Property', 'null', 3078, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3078, 'Uhome', 'null', 3079, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3079, 'Ulysses Getaways', 'null', 3080, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3080, 'Umah Tantra Tantri', 'null', 3081, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3081, 'Umdloti Letting & Sales', 'null', 3082, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3082, 'Umi hotels', 'null', 3083, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3083, 'UNA Hotels', 'null', 3084, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3084, 'Unico Hotels', 'null', 3085, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3085, 'UniQue Koh Samui', 'null', 3086, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3086, 'UniRez', 'null', 3087, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3087, 'U-Nit Rental Group', 'null', 3071, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3088, 'Unitune', 'null', 3088, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3089, 'Universal Resorts', 'null', 3089, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3090, 'Universal Studios', 'null', 3090, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3091, 'Unizo Hotels', 'null', 3091, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3092, 'Unlisted Collection', 'null', 3092, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3093, 'Unwind Holidays', 'null', 3093, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3094, 'Uptown', 'null', 3094, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3095, 'Urban Hotel Group', 'null', 3095, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3096, 'Urban House', 'null', 3096, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3097, 'Urban Spaces', 'null', 3097, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3098, 'Urban Suites', 'null', 3098, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3099, 'Urbanauts', 'null', 3099, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3100, 'UrbanMinder', 'null', 3100, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3101, 'Urbany Hostels', 'null', 3101, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3102, 'USP Property Management', 'null', 3102, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3103, 'Utama Mas Propertindo', 'null', 3103, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3104, 'V Guest House', 'null', 3104, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3105, 'Vacance', 'null', 3105, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3106, 'Vacances Bleues', 'null', 3106, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3107, 'Vacanze Italiane MED', 'null', 3107, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3108, 'Vacanzes', 'null', 3108, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3109, 'Vacasa SA', 'null', 3109, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3110, 'Vacation Bay Holiday Homes_C', 'null', 3110, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3111, 'Vacation Marbella', 'null', 3111, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3112, 'Vacation Rental Pros_C', 'null', 3112, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3113, 'Vacay Villa', 'null', 3113, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3114, 'Vagabond Inn', 'null', 3114, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3115, 'Vail Resorts', 'null', 3115, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3116, 'Valamar Hotels & Resorts', 'null', 3116, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3117, 'Valcambre_C', 'null', 3117, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3118, 'Vals Next_JP', 'null', 3118, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3119, 'Vantage Hospitality', 'null', 3119, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3120, 'Variety Hotels', 'null', 3120, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3121, 'Vaway_C', 'null', 3121, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3122, 'VBA Hospitality Group', 'null', 3122, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3123, 'Vebriani', 'null', 3123, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3124, 'Veeve_C', 'null', 3124, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3125, 'Vegan Organizer', 'null', 3125, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3126, 'Veloche home', 'null', 3126, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3127, 'Venice Rent Apartments', 'null', 3127, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3128, 'Vera Villas Management', 'null', 3128, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3129, 'Veranda Resorts', 'null', 3129, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3130, 'Veriu group', 'null', 3130, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3131, 'Verve Travel Management', 'null', 3131, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3132, 'Very Jaya', 'null', 3132, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3133, 'Vessel Hotel', 'null', 3133, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3134, 'VHM Hotels', 'null', 3134, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3135, 'VIP Hotels', 'null', 3186, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3136, 'VIP Stays Concierge PH', 'null', 3187, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3137, 'Via Inn', 'null', 3135, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3138, 'Viceroy Hotel Group', 'null', 3136, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3139, 'Victor Plotnikov', 'null', 3137, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3140, 'Victor Properties', 'null', 3138, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3141, 'Victoria Court', 'null', 3139, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3142, 'Victoria Home NHA', 'null', 3140, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3143, 'Victoria Properties', 'null', 3141, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3144, 'Victoria Room', 'null', 3142, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3145, 'Vienna Hotel Group', 'null', 3143, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3146, 'Vienna Hotels', 'null', 3144, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3147, 'Vienna House', 'null', 3145, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3148, 'Vietnam 1989', 'null', 3146, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3149, 'Vietnam House', 'null', 3147, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3150, 'Vietstork TA', 'null', 3148, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3151, 'Vika Meredian', 'null', 3149, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3152, 'Vila Galé', 'null', 3150, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3153, 'Villa Anastacia Inn', 'null', 3151, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3154, 'Villa Bali Management', 'null', 3152, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3155, 'Villa Bening', 'null', 3153, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3156, 'Villa BoBos', 'null', 3154, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3157, 'Villa Bugis Management', 'null', 3155, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3158, 'Villa Finder', 'null', 3156, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3159, 'Villa Fontaine Hotels', 'null', 3157, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3160, 'Villa Griya Wira Karya', 'null', 3158, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3161, 'Villa Hotels', 'null', 3159, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3162, 'Villa Istana Bunga', 'null', 3160, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3163, 'Villa Kampung Artis Bali', 'null', 3161, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3164, 'Villa Kampung Ayem', 'null', 3162, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3165, 'Villa Kubu Management', 'null', 3163, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3166, 'Villa Lombok Pahawang', 'null', 3164, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3167, 'Villa Lucio Selviana', 'null', 3165, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3168, 'Villa Marizella_C', 'null', 3166, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3169, 'Villa Mulyono', 'null', 3167, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3170, 'Villa Nadira', 'null', 3168, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3171, 'Villa Paerdoe', 'null', 3169, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3172, 'Villa Paolija', 'null', 3170, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3173, 'Villa Pulau Cinta', 'null', 3171, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3174, 'Villa Santibis', 'null', 3172, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3175, 'Villa Short Stay', 'null', 3173, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3176, 'Villa Tentram', 'null', 3174, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3177, 'Villa Trixie', 'null', 3175, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3178, 'Villa True Colors', 'null', 3176, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3179, 'VillaIstanaBunga', 'null', 3177, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3180, 'Villas Bali', 'null', 3178, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3181, 'VillaVania', 'null', 3179, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3182, 'Villaz_C', 'null', 3180, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3183, 'Vilorium', 'null', 3181, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3184, 'Vincci Hoteles', 'null', 3182, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3185, 'Vince Chan', 'null', 3183, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3186, 'Vinomofo', 'null', 3184, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3187, 'Vinpearl Hotels and Resorts', 'null', 3185, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3188, 'Visit Japan', 'null', 3188, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3189, 'Visit the Entrance', 'null', 3189, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3190, 'Visith Property', 'null', 3190, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3191, 'Vista Hotel Management', 'null', 3191, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3192, 'Vista Residences Inc 1', 'null', 3192, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3193, 'Vista Rooms', 'null', 3193, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3194, 'Vistarooms', 'null', 3194, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3195, 'Vistay', 'null', 3195, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3196, 'Vita Emerald', 'null', 3196, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3197, 'Viva Hotels and Resorts', 'null', 3197, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3198, 'VMC Vacation Home Rental', 'null', 3198, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3199, 'VMG Resorts_C', 'null', 3199, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3200, 'Vodaless', 'null', 3200, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3201, 'Voluntad', 'null', 3201, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3202, 'Von Essen Hotels', 'null', 3202, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3203, 'Vondel Hotels', 'null', 3203, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3204, 'Vortex Managers_C', 'null', 3204, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3205, 'Voyages Ayers Rock Resort', 'null', 3205, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3206, 'Voyages Hotels and Resorts', 'null', 3206, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3207, 'VP Hotels', 'null', 3207, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3208, 'VR Hotels', 'null', 3208, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3209, 'VRI (Vacation Resorts International)', 'null', 3209, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3210, 'Vsbias', 'null', 3210, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3211, 'Vuaban Pattaya', 'null', 3211, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3212, 'Vuaban Phuket', 'null', 3212, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3213, 'WA Stay Group', 'null', 3213, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3214, 'Wabisaby', 'null', 3214, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3215, 'Wafaby Guesthouse', 'null', 3215, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3216, 'Waiheke Unlimited', 'null', 3216, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3217, 'Wako', 'null', 3217, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3218, 'Waldorf Apartment Group', 'null', 3218, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3219, 'Waldorf New Zealand', 'null', 3219, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3220, 'Walt Disney World Resort', 'null', 3220, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3221, 'Wana Prasta', 'null', 3221, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3222, 'Wanda Hotels and Resorts', 'null', 3222, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3223, 'Wang', 'null', 3223, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3224, 'Wangtianxing_JP', 'null', 3224, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3225, 'Wardana Group', 'null', 3225, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3226, 'Warner Leisure Hotels', 'null', 3226, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3227, 'Warwick International Hotels', 'null', 3227, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3228, 'Washington Hotel Corporation', 'null', 3228, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3229, 'Waterfront Hotels & Casinos', 'null', 3229, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3230, 'Waterplace Group', 'null', 3230, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3231, 'Wayne Group', 'null', 3231, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3232, 'Weflating', 'null', 3232, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3233, 'WEI MING CHANG_C', 'null', 3233, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3234, 'Wei Tiao', 'null', 3234, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3235, 'Wejoin', 'null', 3235, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3236, 'Welcom Heritage', 'null', 3236, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3237, 'Welcome Break', 'null', 3237, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3238, 'Welcomer Group_C', 'null', 3238, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3239, 'Wellington Management Group', 'null', 3239, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3240, 'Wenxing Hotel Group', 'null', 3240, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3241, 'Westay', 'null', 3241, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3242, 'Westcord Hotels', 'null', 3242, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3243, 'Westgate', 'null', 3243, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3244, 'White Beard', 'null', 3244, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3245, 'Widi', 'null', 3245, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3246, 'Wiggo Yttergard', 'null', 3246, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3247, 'Wigi Hidayat', 'null', 3247, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3248, 'Wild Bush Luxury', 'null', 3248, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3249, 'Williams Group', 'null', 3249, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3250, 'Wilson Hispanos', 'null', 3250, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3251, 'Win Rooms', 'null', 3251, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3252, 'Win Win Property_C', 'null', 3252, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3253, 'Wintc Property', 'null', 3253, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3254, 'Winterfell Hotels', 'null', 3254, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3255, 'Wise Factory', 'null', 3255, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3256, 'Wisma Karmel', 'null', 3256, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3257, 'Wisma Kopari', 'null', 3257, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3258, 'Wisma Pondok Pelangi', 'null', 3258, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3259, 'Wisma Sederhana', 'null', 3259, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3260, 'Withus Condotel', 'null', 3260, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3261, 'Wizzela Media', 'null', 3261, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3262, 'WMC GROUP', 'null', 3262, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3263, 'Wofox Apartments', 'null', 3263, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3264, 'Wombat\'s City Hostels', 'null', 3264, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3265, 'Wonderland Japan', 'null', 3265, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3266, 'Woodside Bay Estate', 'null', 3266, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3267, 'Woodstock', 'null', 3267, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3268, 'World Apartments', 'null', 3268, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3269, 'World Hotels - Do not use', 'null', 3269, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3270, 'World Resort Operation', 'null', 3270, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3271, 'World Wide Japan', 'null', 3271, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3272, 'Worldwide Apartments_C', 'null', 3272, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3273, 'Wotopia', 'null', 3273, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3274, 'WoZhai', 'null', 3274, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3275, 'Wuxiaoning', 'null', 3275, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3276, 'Wyndham Hotels & Resorts', 'null', 3276, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3277, 'Wyndham Vacation Rentals', 'null', 3277, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3278, 'Wynn', 'null', 3278, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3279, 'Xflat Services FZ LLC', 'null', 3279, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3280, 'Xiamen Yangxue Flower House', 'null', 3280, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3281, 'Xotel', 'null', 3281, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3282, 'XS', 'null', 3282, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3283, 'Yamashita', 'null', 3283, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3284, 'Yanapat Property', 'null', 3284, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3285, 'Yanjoon Holiday Homes Rental L.L.C', 'null', 3285, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3286, 'Yanthi Property', 'null', 3286, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3287, 'Yanti Yanti', 'null', 3287, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3288, 'YayanKurniawan', 'null', 3288, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3289, 'Yedawon', 'null', 3289, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3290, 'Yellow Hotels', 'null', 3290, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3291, 'Yendebabo Homestay', 'null', 3291, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3292, 'YHA Australia Hostels', 'null', 3292, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3293, 'YHA England & Wales', 'null', 3293, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3294, 'YHA Israel', 'null', 3294, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3295, 'YHA New Zealand', 'null', 3295, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3296, 'Yield Management', 'null', 3296, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3297, 'Ying Stay', 'null', 3297, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3298, 'YMCA', 'null', 3298, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3299, 'Yobel', 'null', 3299, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3300, 'Yoga', 'null', 3300, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3301, 'Yoho', 'null', 3301, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3302, 'Yoho_AC', 'null', 3302, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3303, 'Yorkeys Knob accommodation', 'null', 3303, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3304, 'Yoshihiro Kimura', 'null', 3304, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3305, 'You Hometel', 'null', 3305, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3306, 'You Stylish Apartments', 'null', 3306, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3307, 'Your Host Helper', 'null', 3307, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3308, 'Your Prague Hotels', 'null', 3308, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3309, 'Your Vica Stay_C', 'null', 3309, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3310, 'Your.Rentals A/S_C', 'null', 3310, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3311, 'YTL', 'null', 3311, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3312, 'YTL Hotels', 'null', 3312, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3313, 'Yudi Eko', 'null', 3313, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3314, 'Yudis Nur Riyadi', 'null', 3314, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3315, 'Yueyou Hotels', 'null', 3315, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3316, 'Yukai Resort', 'null', 3316, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3317, 'Yuly Anjiani', 'null', 3317, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3318, 'Yummy Plus', 'null', 3318, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3319, 'Yuni Kajane', 'null', 3319, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3320, 'Yupihome_C', 'null', 3320, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3321, 'YUSTAY Saigon', 'null', 3321, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3322, 'Yuyuan', 'null', 3322, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3323, 'YY38', 'null', 3323, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3324, 'Z Hotels', 'null', 3324, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3325, 'Zahra Al Jazeerah', 'null', 3325, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3326, 'Zara Property', 'null', 3326, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3327, 'Zarems', 'null', 3327, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3328, 'Zedekiah\'s Apartment Transient House', 'null', 3328, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3329, 'ZEN Rooms', 'null', 3329, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3330, 'Zencious Dormitory', 'null', 3330, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3331, 'Zenit Hotels', 'null', 3331, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3332, 'Zenith Holiday Homes', 'null', 3332, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3333, 'Zens', 'null', 3333, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3334, 'Zensan Bed And Bath', 'null', 3334, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3335, 'Zhensu Information Technology', 'null', 3335, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3336, 'Zhiqin', 'null', 3336, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3337, 'Zleepy', 'null', 3337, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3338, 'ZO Rooms', 'null', 3338, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3339, 'ZOSTEL', 'null', 3339, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3340, 'Zuri Hotel Management (ZHM)', 'null', 3340, 1, 1, 1, 1, 1)"); + DB::connection()->statement("INSERT INTO `property_chain` VALUES (3341, 'Zuzu Hospitality', 'null', 3341, 1, 1, 1, 1, 1)"); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property_chain', function (Blueprint $table) { + $table->dropColumn('priority'); + }); + } +} diff --git a/database/migrations/2020_07_08_141717_change_column_language_base_and_language_translate_clear_foreingkeys.php b/database/migrations/2020_07_08_141717_change_column_language_base_and_language_translate_clear_foreingkeys.php new file mode 100644 index 0000000..a3079a0 --- /dev/null +++ b/database/migrations/2020_07_08_141717_change_column_language_base_and_language_translate_clear_foreingkeys.php @@ -0,0 +1,37 @@ +dropForeign(['created_by']); + $table->dropForeign(['updated_by']); + }); + + Schema::table('language_translate', function (Blueprint $table) { + $table->dropForeign(['created_by']); + $table->dropForeign(['updated_by']); + }); + + } + + public function down() + { + Schema::table('language_base', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + + Schema::table('language_translate', function (Blueprint $table) { + $table->foreign('created_by')->references('id')->on('user'); + $table->foreign('updated_by')->references('id')->on('user'); + }); + } + +} diff --git a/database/migrations/2020_07_13_104422_add_column_and_create_data_property_room_view_type_table.php b/database/migrations/2020_07_13_104422_add_column_and_create_data_property_room_view_type_table.php new file mode 100644 index 0000000..1eb86a3 --- /dev/null +++ b/database/migrations/2020_07_13_104422_add_column_and_create_data_property_room_view_type_table.php @@ -0,0 +1,104 @@ +string('language_key',100)->after('name')->nullable(); + }); + + DB::statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::table('property_room_view_type')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + DB::table('property_room_view_type')->insert( + [ + [ + 'name' => 'Park', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Pool ', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'River', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Sea ', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Street', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Valley', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ], + [ + 'name' => 'Monument', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ] + ] + + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property_room_view_type', function (Blueprint $table) { + $table->dropColumn('language_key'); + }); + + DB::statement('SET FOREIGN_KEY_CHECKS=0;'); + DB::table('property_room_view_type')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + } +} diff --git a/database/migrations/2020_07_14_181858_add_title_language_key_to_property_fact.php b/database/migrations/2020_07_14_181858_add_title_language_key_to_property_fact.php new file mode 100644 index 0000000..4e7f69d --- /dev/null +++ b/database/migrations/2020_07_14_181858_add_title_language_key_to_property_fact.php @@ -0,0 +1,33 @@ +string('title_language_key',250)->after('language_key')->nullable(); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property_fact', function (Blueprint $table) { + $table->dropColumn('title_language_key'); + }); + } +} diff --git a/database/migrations/2020_07_22_095749_truncate_related_property_tables.php b/database/migrations/2020_07_22_095749_truncate_related_property_tables.php new file mode 100644 index 0000000..1ce924c --- /dev/null +++ b/database/migrations/2020_07_22_095749_truncate_related_property_tables.php @@ -0,0 +1,68 @@ +truncate(); + DB::table('jobs')->truncate(); + DB::table('failed_jobs')->truncate(); + + DB::table('offer')->truncate(); + DB::table('offer_accommodation_mapping')->truncate(); + DB::table('offer_cancellation_policy')->truncate(); + DB::table('offer_contact_mapping')->truncate(); + DB::table('offer_fact_mapping')->truncate(); + DB::table('offer_important_notes')->truncate(); + DB::table('offer_photo_mapping')->truncate(); + DB::table('offer_price')->truncate(); + DB::table('offer_room_mapping')->truncate(); + + DB::table('property')->truncate(); + DB::table('property_additional_info')->truncate(); + DB::table('property_brand')->truncate(); + DB::table('property_channel_mapping')->truncate(); + DB::table('property_config')->truncate(); + DB::table('property_contact')->truncate(); + DB::table('property_content')->truncate(); + DB::table('property_executive')->truncate(); + DB::table('property_fact_attribute_mapping')->truncate(); + DB::table('property_fact_mapping')->truncate(); + DB::table('property_module_mapping')->truncate(); + DB::table('property_photo')->truncate(); + DB::table('property_room')->truncate(); + DB::table('property_room_availability')->truncate(); + DB::table('property_room_bed')->truncate(); + DB::table('property_room_fact_mapping')->truncate(); + DB::table('property_room_photo_mapping')->truncate(); + DB::table('property_room_rate')->truncate(); + DB::table('property_room_rate_channel_mapping')->truncate(); + DB::table('property_room_rate_inclusion_mapping')->truncate(); + DB::table('property_room_rate_mapping')->truncate(); + DB::table('property_room_rate_mapping_setup')->truncate(); + DB::table('property_room_rate_price')->truncate(); + DB::table('property_room_view_mapping')->truncate(); + DB::table('property_web')->truncate(); + + DB::table('user')->truncate(); + DB::table('user_property_mapping')->truncate(); + + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + */ + } + + + public function down() + { + // + } +} diff --git a/database/migrations/2020_07_22_135248_add_icon_to_property_executive_type.php b/database/migrations/2020_07_22_135248_add_icon_to_property_executive_type.php new file mode 100644 index 0000000..1109f18 --- /dev/null +++ b/database/migrations/2020_07_22_135248_add_icon_to_property_executive_type.php @@ -0,0 +1,34 @@ +string('icon',32)->after('language_key')->nullable(); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('property_executive_type', function (Blueprint $table) { + $table->dropColumn('icon'); + + }); + } +} diff --git a/database/migrations/2020_07_24_143309_add_language_key_to_tables.php b/database/migrations/2020_07_24_143309_add_language_key_to_tables.php new file mode 100644 index 0000000..cc68d7e --- /dev/null +++ b/database/migrations/2020_07_24_143309_add_language_key_to_tables.php @@ -0,0 +1,30 @@ +string('language_key',128)->after('name')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('country', function (Blueprint $table) { $table->dropColumn('language_key'); }); + } +} diff --git a/database/migrations/2020_08_10_112926_add_accommodation_type_to_property_room_rate.php b/database/migrations/2020_08_10_112926_add_accommodation_type_to_property_room_rate.php new file mode 100644 index 0000000..a324ee3 --- /dev/null +++ b/database/migrations/2020_08_10_112926_add_accommodation_type_to_property_room_rate.php @@ -0,0 +1,35 @@ +unsignedInteger('accommodation_type')->after('description'); + }); + \DB::table('property_room_rate')->update(['accommodation_type' => 13]); + Schema::table('property_room_rate', function (Blueprint $table) { + $table->foreign('accommodation_type', 'accommodation_type_foreign')->references('id')->on('property_fact'); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + } +} diff --git a/database/migrations/2020_08_12_111815_add_room_size_to_property_room.php b/database/migrations/2020_08_12_111815_add_room_size_to_property_room.php new file mode 100644 index 0000000..86b02aa --- /dev/null +++ b/database/migrations/2020_08_12_111815_add_room_size_to_property_room.php @@ -0,0 +1,25 @@ +statement("UPDATE property_chain SET name = 'property_chain-independent' WHERE id = 1"); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_08_26_174248_update_siteconfig_20200826.php b/database/migrations/2020_08_26_174248_update_siteconfig_20200826.php new file mode 100644 index 0000000..4b85a90 --- /dev/null +++ b/database/migrations/2020_08_26_174248_update_siteconfig_20200826.php @@ -0,0 +1,32 @@ +where('config_key' , 'profile_rate_mapping')->update(['config_value_json' => $profileRateMapping]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_08_27_173249_update_site_config_20200827.php b/database/migrations/2020_08_27_173249_update_site_config_20200827.php new file mode 100644 index 0000000..a4a373c --- /dev/null +++ b/database/migrations/2020_08_27_173249_update_site_config_20200827.php @@ -0,0 +1,32 @@ +where('config_key' , 'profile_rate_mapping')->update(['config_value_json' => $profileRateMapping]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_08_28_144129_edit_property_channel_mapping_setup_20200828.php b/database/migrations/2020_08_28_144129_edit_property_channel_mapping_setup_20200828.php new file mode 100644 index 0000000..b34cf46 --- /dev/null +++ b/database/migrations/2020_08_28_144129_edit_property_channel_mapping_setup_20200828.php @@ -0,0 +1,38 @@ + Error; 1 =>Success; 2=> Start, 3=> Pending, 4 => Cancel/Refund', + `created_at` int(11) DEFAULT NULL, + `updated_at` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +); + "); + + DB::statement(" + CREATE TABLE `payment_type` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `pos_code` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `bank_code` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `threed_model` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `params` mediumtext COLLATE utf8mb4_unicode_ci, + `installment` tinyint(1) DEFAULT NULL COMMENT '0:Active, 1:Passive', + `status` tinyint(1) DEFAULT NULL COMMENT '0: Passive, 1: ActiveProd, 2: Test', + `created_by` int(11) DEFAULT NULL, + `updated_by` int(11) DEFAULT NULL, + `created_at` int(11) DEFAULT NULL, + `updated_at` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +); + "); + + DB::statement(" + CREATE TABLE `property_payment_installment` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `property_id` int(11) NOT NULL, + `property_payment_mapping_id` int(11) NOT NULL, + `installment` tinyint(3) DEFAULT NULL, + `commission` double(10,2) NOT NULL, + `status` tinyint(1) DEFAULT NULL, + `created_by` int(11) DEFAULT NULL, + `updated_by` int(11) DEFAULT NULL, + `created_at` int(11) DEFAULT NULL, + `updated_at` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +); + "); + + DB::statement(" + CREATE TABLE `property_payment_mapping` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `property_id` int(11) NOT NULL, + `payment_type_id` int(11) NOT NULL, + `params` mediumtext COLLATE utf8mb4_unicode_ci, + `installment` tinyint(1) DEFAULT NULL COMMENT '0:Active, 1:Passive', + `currency_code` varchar(3) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `is_default` tinyint(1) DEFAULT NULL, + `status` tinyint(1) DEFAULT NULL COMMENT '0: Passive, 1: ActiveProd, 2: Test', + `created_by` int(11) DEFAULT NULL, + `updated_by` int(11) DEFAULT NULL, + `created_at` int(11) DEFAULT NULL, + `updated_at` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +); + "); + + DB::statement(" + CREATE TABLE `currency_rates` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `date` date DEFAULT NULL, + `currency_code` varchar(3) CHARACTER SET utf8 DEFAULT NULL, + `exc_currency_code` varchar(3) CHARACTER SET utf8 DEFAULT NULL, + `rate` double(10,2) DEFAULT NULL, + `custom_rate` double(10,2) DEFAULT NULL, + `created_at` int(11) DEFAULT NULL, + `updated_at` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +); + "); + + + } + + + public function down() + { + // + } +} diff --git a/database/migrations/2020_11_02_133050_update_property_fact_table.php b/database/migrations/2020_11_02_133050_update_property_fact_table.php new file mode 100644 index 0000000..22769e6 --- /dev/null +++ b/database/migrations/2020_11_02_133050_update_property_fact_table.php @@ -0,0 +1,27 @@ +statement("INSERT INTO `property_web_menu` VALUES (1, null, 'Home', 'myweb-menu-home', '/', 1, 1, 1, 1, 1, 1, 1);"); + DB::connection()->statement("INSERT INTO `property_web_menu` VALUES (2, null, 'About', 'myweb-menu-about', '/about', 1, 2, 1, 1, 1, 1, 1);"); + DB::connection()->statement("INSERT INTO `property_web_menu` VALUES (3, null, 'Gallery', 'myweb-menu-gallery', '/gallery', 1, 3, 1, 1, 1, 1, 1);"); + DB::connection()->statement("INSERT INTO `property_web_menu` VALUES (4, null, 'Rooms', 'myweb-menu-rooms', '/rooms', 1, 4, 1, 1, 1, 1, 1);"); + DB::connection()->statement("INSERT INTO `property_web_menu` VALUES (5, null, 'Reservation', 'myweb-menu-reservation', '/booking', 2, 5, 1, 1, 1, 1, 1);"); + DB::connection()->statement("INSERT INTO `property_web_menu` VALUES (6, null, 'Contact Us', 'myweb-menu-contact_us', '/contact', 1, 6, 1, 1, 1, 1, 1);"); + } + + + public function down() + { + // + } +} diff --git a/database/migrations/2020_11_12_131542_create_table_property_web_setup.php b/database/migrations/2020_11_12_131542_create_table_property_web_setup.php new file mode 100644 index 0000000..7816de8 --- /dev/null +++ b/database/migrations/2020_11_12_131542_create_table_property_web_setup.php @@ -0,0 +1,41 @@ +where('config_key' , 'profile_rate_mapping')->update(['config_value_json' => $profileRateMapping]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2020_12_22_175505_payment_type_change_name_data.php b/database/migrations/2020_12_22_175505_payment_type_change_name_data.php new file mode 100644 index 0000000..efb997a --- /dev/null +++ b/database/migrations/2020_12_22_175505_payment_type_change_name_data.php @@ -0,0 +1,58 @@ +truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + DB::statement(' + INSERT INTO `property_place_fact_title_fact_mapping` VALUES (1,36,1,1,1,1,1,1,1),(2,36,1,2,1,1,1,1,1),(3,36,1,3,1,1,1,1,1),(4,36,1,4,1,1,1,1,1),(5,36,1,5,1,1,1,1,1),(6,37,1,1,1,1,1,1,1),(7,37,1,2,1,1,1,1,1),(8,37,1,3,1,1,1,1,1),(9,37,1,4,1,1,1,1,1),(10,37,1,5,1,1,1,1,1),(11,38,1,1,1,1,1,1,1),(12,38,1,2,1,1,1,1,1),(13,38,1,3,1,1,1,1,1),(14,38,1,4,1,1,1,1,1),(15,38,1,5,1,1,1,1,1),(16,39,1,1,1,1,1,1,1),(17,39,1,2,1,1,1,1,1),(18,39,1,3,1,1,1,1,1),(19,39,1,4,1,1,1,1,1),(20,39,1,5,1,1,1,1,1),(21,36,2,7,1,1,1,1,1),(22,36,2,8,1,1,1,1,1),(23,36,2,9,1,1,1,1,1),(24,36,2,10,1,1,1,1,1),(25,36,2,11,1,1,1,1,1),(26,36,2,12,1,1,1,1,1),(27,36,2,13,1,1,1,1,1),(28,36,2,14,1,1,1,1,1),(29,36,2,15,1,1,1,1,1),(30,36,2,16,1,1,1,1,1),(31,36,2,17,1,1,1,1,1),(32,36,2,18,1,1,1,1,1),(33,36,2,19,1,1,1,1,1),(34,36,2,20,1,1,1,1,1),(35,36,2,21,1,1,1,1,1),(36,36,2,22,1,1,1,1,1),(37,36,2,23,1,1,1,1,1),(38,36,2,24,1,1,1,1,1),(39,36,2,25,1,1,1,1,1),(40,37,2,7,1,1,1,1,1),(41,37,2,8,1,1,1,1,1),(42,37,2,9,1,1,1,1,1),(43,37,2,10,1,1,1,1,1),(44,37,2,11,1,1,1,1,1),(45,37,2,12,1,1,1,1,1),(46,37,2,13,1,1,1,1,1),(47,37,2,14,1,1,1,1,1),(48,37,2,15,1,1,1,1,1),(49,37,2,16,1,1,1,1,1),(50,37,2,17,1,1,1,1,1),(51,37,2,18,1,1,1,1,1),(52,37,2,19,1,1,1,1,1),(53,37,2,20,1,1,1,1,1),(54,37,2,21,1,1,1,1,1),(55,37,2,22,1,1,1,1,1),(56,37,2,23,1,1,1,1,1),(57,37,2,24,1,1,1,1,1),(58,37,2,25,1,1,1,1,1),(59,38,2,7,1,1,1,1,1),(60,38,2,8,1,1,1,1,1),(61,38,2,9,1,1,1,1,1),(62,38,2,10,1,1,1,1,1),(63,38,2,11,1,1,1,1,1),(64,38,2,12,1,1,1,1,1),(65,38,2,13,1,1,1,1,1),(66,38,2,14,1,1,1,1,1),(67,38,2,15,1,1,1,1,1),(68,38,2,16,1,1,1,1,1),(69,38,2,17,1,1,1,1,1),(70,38,2,18,1,1,1,1,1),(71,38,2,19,1,1,1,1,1),(72,38,2,20,1,1,1,1,1),(73,38,2,21,1,1,1,1,1),(74,38,2,22,1,1,1,1,1),(75,38,2,23,1,1,1,1,1),(76,38,2,24,1,1,1,1,1),(77,38,2,25,1,1,1,1,1),(78,39,2,7,1,1,1,1,1),(79,39,2,8,1,1,1,1,1),(80,39,2,9,1,1,1,1,1),(81,39,2,10,1,1,1,1,1),(82,39,2,11,1,1,1,1,1),(83,39,2,12,1,1,1,1,1),(84,39,2,13,1,1,1,1,1),(85,39,2,14,1,1,1,1,1),(86,39,2,15,1,1,1,1,1),(87,39,2,16,1,1,1,1,1),(88,39,2,17,1,1,1,1,1),(89,39,2,18,1,1,1,1,1),(90,39,2,19,1,1,1,1,1),(91,39,2,20,1,1,1,1,1),(92,39,2,21,1,1,1,1,1),(93,39,2,22,1,1,1,1,1),(94,39,2,23,1,1,1,1,1),(95,39,2,24,1,1,1,1,1),(96,39,2,25,1,1,1,1,1),(97,36,3,26,1,1,1,1,1),(98,36,3,27,1,1,1,1,1),(99,36,3,28,1,1,1,1,1),(100,36,3,29,1,1,1,1,1),(101,36,3,30,1,1,1,1,1),(102,36,3,31,1,1,1,1,1),(103,36,3,32,1,1,1,1,1),(104,36,3,33,1,1,1,1,1),(105,36,3,34,1,1,1,1,1),(106,36,3,35,1,1,1,1,1),(107,37,3,26,1,1,1,1,1),(108,37,3,27,1,1,1,1,1),(109,37,3,28,1,1,1,1,1),(110,37,3,29,1,1,1,1,1),(111,37,3,30,1,1,1,1,1),(112,37,3,31,1,1,1,1,1),(113,37,3,32,1,1,1,1,1),(114,37,3,33,1,1,1,1,1),(115,37,3,34,1,1,1,1,1),(116,37,3,35,1,1,1,1,1),(117,38,3,26,1,1,1,1,1),(118,38,3,27,1,1,1,1,1),(119,38,3,28,1,1,1,1,1),(120,38,3,29,1,1,1,1,1),(121,38,3,30,1,1,1,1,1),(122,38,3,31,1,1,1,1,1),(123,38,3,32,1,1,1,1,1),(124,38,3,33,1,1,1,1,1),(125,38,3,34,1,1,1,1,1),(126,38,3,35,1,1,1,1,1),(127,39,3,26,1,1,1,1,1),(128,39,3,27,1,1,1,1,1),(129,39,3,28,1,1,1,1,1),(130,39,3,29,1,1,1,1,1),(131,39,3,30,1,1,1,1,1),(132,39,3,31,1,1,1,1,1),(133,39,3,32,1,1,1,1,1),(134,39,3,33,1,1,1,1,1),(135,39,3,34,1,1,1,1,1),(136,39,3,35,1,1,1,1,1),(137,36,4,42,1,1,1,1,1),(138,36,4,43,1,1,1,1,1),(139,36,4,44,1,1,1,1,1),(140,36,4,45,1,1,1,1,1),(141,36,4,46,1,1,1,1,1),(142,36,4,47,1,1,1,1,1),(143,36,4,48,1,1,1,1,1),(144,36,4,49,1,1,1,1,1),(145,36,4,50,1,1,1,1,1),(146,36,4,51,1,1,1,1,1),(147,36,4,52,1,1,1,1,1),(148,36,4,53,1,1,1,1,1),(149,36,4,54,1,1,1,1,1),(150,36,4,55,1,1,1,1,1),(151,36,4,56,1,1,1,1,1),(152,37,4,42,1,1,1,1,1),(153,37,4,43,1,1,1,1,1),(154,37,4,44,1,1,1,1,1),(155,37,4,45,1,1,1,1,1),(156,37,4,46,1,1,1,1,1),(157,37,4,47,1,1,1,1,1),(158,37,4,48,1,1,1,1,1),(159,37,4,49,1,1,1,1,1),(160,37,4,50,1,1,1,1,1),(161,37,4,51,1,1,1,1,1),(162,37,4,52,1,1,1,1,1),(163,37,4,53,1,1,1,1,1),(164,37,4,54,1,1,1,1,1),(165,37,4,55,1,1,1,1,1),(166,37,4,56,1,1,1,1,1),(167,38,4,42,1,1,1,1,1),(168,38,4,43,1,1,1,1,1),(169,38,4,44,1,1,1,1,1),(170,38,4,45,1,1,1,1,1),(171,38,4,46,1,1,1,1,1),(172,38,4,47,1,1,1,1,1),(173,38,4,48,1,1,1,1,1),(174,38,4,49,1,1,1,1,1),(175,38,4,50,1,1,1,1,1),(176,38,4,51,1,1,1,1,1),(177,38,4,52,1,1,1,1,1),(178,38,4,53,1,1,1,1,1),(179,38,4,54,1,1,1,1,1),(180,38,4,55,1,1,1,1,1),(181,38,4,56,1,1,1,1,1),(182,39,4,42,1,1,1,1,1),(183,39,4,43,1,1,1,1,1),(184,39,4,44,1,1,1,1,1),(185,39,4,45,1,1,1,1,1),(186,39,4,46,1,1,1,1,1),(187,39,4,47,1,1,1,1,1),(188,39,4,48,1,1,1,1,1),(189,39,4,49,1,1,1,1,1),(190,39,4,50,1,1,1,1,1),(191,39,4,51,1,1,1,1,1),(192,39,4,52,1,1,1,1,1),(193,39,4,53,1,1,1,1,1),(194,39,4,54,1,1,1,1,1),(195,39,4,55,1,1,1,1,1),(196,39,4,56,1,1,1,1,1),(197,36,5,66,1,1,1,1,1),(198,36,5,67,1,1,1,1,1),(199,36,5,68,1,1,1,1,1),(200,36,5,69,1,1,1,1,1),(201,36,5,70,1,1,1,1,1),(202,36,5,71,1,1,1,1,1),(203,36,5,72,1,1,1,1,1),(204,36,5,73,1,1,1,1,1),(205,36,5,74,1,1,1,1,1),(206,36,5,75,1,1,1,1,1),(207,36,5,76,1,1,1,1,1),(208,36,5,77,1,1,1,1,1),(209,36,5,78,1,1,1,1,1),(210,36,5,79,1,1,1,1,1),(211,36,5,80,1,1,1,1,1),(212,36,5,81,1,1,1,1,1),(213,36,5,82,1,1,1,1,1),(214,36,5,83,1,1,1,1,1),(215,36,5,84,1,1,1,1,1),(216,36,5,85,1,1,1,1,1),(217,36,5,86,1,1,1,1,1),(218,36,5,87,1,1,1,1,1),(219,36,5,88,1,1,1,1,1),(220,36,5,89,1,1,1,1,1),(221,36,5,90,1,1,1,1,1),(222,36,5,91,1,1,1,1,1),(223,36,5,92,1,1,1,1,1),(224,36,5,93,1,1,1,1,1),(225,36,5,94,1,1,1,1,1),(226,36,5,95,1,1,1,1,1),(227,36,5,96,1,1,1,1,1),(228,36,5,97,1,1,1,1,1),(229,36,5,98,1,1,1,1,1),(230,37,5,66,1,1,1,1,1),(231,37,5,67,1,1,1,1,1),(232,37,5,68,1,1,1,1,1),(233,37,5,69,1,1,1,1,1),(234,37,5,70,1,1,1,1,1),(235,37,5,71,1,1,1,1,1),(236,37,5,72,1,1,1,1,1),(237,37,5,73,1,1,1,1,1),(238,37,5,74,1,1,1,1,1),(239,37,5,75,1,1,1,1,1),(240,37,5,76,1,1,1,1,1),(241,37,5,77,1,1,1,1,1),(242,37,5,78,1,1,1,1,1),(243,37,5,79,1,1,1,1,1),(244,37,5,80,1,1,1,1,1),(245,37,5,81,1,1,1,1,1),(246,37,5,82,1,1,1,1,1),(247,37,5,83,1,1,1,1,1),(248,37,5,84,1,1,1,1,1),(249,37,5,85,1,1,1,1,1),(250,37,5,86,1,1,1,1,1),(251,37,5,87,1,1,1,1,1),(252,37,5,88,1,1,1,1,1),(253,37,5,89,1,1,1,1,1),(254,37,5,90,1,1,1,1,1),(255,37,5,91,1,1,1,1,1),(256,37,5,92,1,1,1,1,1),(257,37,5,93,1,1,1,1,1),(258,37,5,94,1,1,1,1,1),(259,37,5,95,1,1,1,1,1),(260,37,5,96,1,1,1,1,1),(261,37,5,97,1,1,1,1,1),(262,37,5,98,1,1,1,1,1),(263,38,5,66,1,1,1,1,1),(264,38,5,67,1,1,1,1,1),(265,38,5,68,1,1,1,1,1),(266,38,5,69,1,1,1,1,1),(267,38,5,70,1,1,1,1,1),(268,38,5,71,1,1,1,1,1),(269,38,5,72,1,1,1,1,1),(270,38,5,73,1,1,1,1,1),(271,38,5,74,1,1,1,1,1),(272,38,5,75,1,1,1,1,1),(273,38,5,76,1,1,1,1,1),(274,38,5,77,1,1,1,1,1),(275,38,5,78,1,1,1,1,1),(276,38,5,79,1,1,1,1,1),(277,38,5,80,1,1,1,1,1),(278,38,5,81,1,1,1,1,1),(279,38,5,82,1,1,1,1,1),(280,38,5,83,1,1,1,1,1),(281,38,5,84,1,1,1,1,1),(282,38,5,85,1,1,1,1,1),(283,38,5,86,1,1,1,1,1),(284,38,5,87,1,1,1,1,1),(285,38,5,88,1,1,1,1,1),(286,38,5,89,1,1,1,1,1),(287,38,5,90,1,1,1,1,1),(288,38,5,91,1,1,1,1,1),(289,38,5,92,1,1,1,1,1),(290,38,5,93,1,1,1,1,1),(291,38,5,94,1,1,1,1,1),(292,38,5,95,1,1,1,1,1),(293,38,5,96,1,1,1,1,1),(294,38,5,97,1,1,1,1,1),(295,38,5,98,1,1,1,1,1),(296,39,5,66,1,1,1,1,1),(297,39,5,67,1,1,1,1,1),(298,39,5,68,1,1,1,1,1),(299,39,5,69,1,1,1,1,1),(300,39,5,70,1,1,1,1,1),(301,39,5,71,1,1,1,1,1),(302,39,5,72,1,1,1,1,1),(303,39,5,73,1,1,1,1,1),(304,39,5,74,1,1,1,1,1),(305,39,5,75,1,1,1,1,1),(306,39,5,76,1,1,1,1,1),(307,39,5,77,1,1,1,1,1),(308,39,5,78,1,1,1,1,1),(309,39,5,79,1,1,1,1,1),(310,39,5,80,1,1,1,1,1),(311,39,5,81,1,1,1,1,1),(312,39,5,82,1,1,1,1,1),(313,39,5,83,1,1,1,1,1),(314,39,5,84,1,1,1,1,1),(315,39,5,85,1,1,1,1,1),(316,39,5,86,1,1,1,1,1),(317,39,5,87,1,1,1,1,1),(318,39,5,88,1,1,1,1,1),(319,39,5,89,1,1,1,1,1),(320,39,5,90,1,1,1,1,1),(321,39,5,91,1,1,1,1,1),(322,39,5,92,1,1,1,1,1),(323,39,5,93,1,1,1,1,1),(324,39,5,94,1,1,1,1,1),(325,39,5,95,1,1,1,1,1),(326,39,5,96,1,1,1,1,1),(327,39,5,97,1,1,1,1,1),(328,39,5,98,1,1,1,1,1),(329,40,1,5,1,1,1,1,1),(330,40,1,6,1,1,1,1,1),(331,41,1,5,1,1,1,1,1),(332,41,1,6,1,1,1,1,1),(333,42,1,5,1,1,1,1,1),(334,42,1,6,1,1,1,1,1),(335,43,1,5,1,1,1,1,1),(336,43,1,6,1,1,1,1,1),(337,44,1,5,1,1,1,1,1),(338,44,1,6,1,1,1,1,1),(339,45,1,5,1,1,1,1,1),(340,45,1,6,1,1,1,1,1),(341,46,1,5,1,1,1,1,1),(342,46,1,6,1,1,1,1,1),(343,47,1,5,1,1,1,1,1),(344,47,1,6,1,1,1,1,1),(345,48,1,5,1,1,1,1,1),(346,48,1,6,1,1,1,1,1),(347,49,1,5,1,1,1,1,1),(348,49,1,6,1,1,1,1,1),(349,50,1,5,1,1,1,1,1),(350,50,1,6,1,1,1,1,1),(351,51,1,5,1,1,1,1,1),(352,51,1,6,1,1,1,1,1),(353,52,1,5,1,1,1,1,1),(354,52,1,6,1,1,1,1,1),(355,53,1,5,1,1,1,1,1),(356,53,1,6,1,1,1,1,1),(357,54,1,5,1,1,1,1,1),(358,54,1,6,1,1,1,1,1),(359,55,1,5,1,1,1,1,1),(360,55,1,6,1,1,1,1,1),(361,56,1,5,1,1,1,1,1),(362,56,1,6,1,1,1,1,1),(363,41,3,36,1,1,1,1,1),(364,41,3,37,1,1,1,1,1),(365,41,3,38,1,1,1,1,1),(366,41,3,39,1,1,1,1,1),(367,41,3,40,1,1,1,1,1),(368,41,3,41,1,1,1,1,1),(369,42,3,36,1,1,1,1,1),(370,42,3,37,1,1,1,1,1),(371,42,3,38,1,1,1,1,1),(372,42,3,39,1,1,1,1,1),(373,42,3,40,1,1,1,1,1),(374,42,3,41,1,1,1,1,1),(375,43,3,37,1,1,1,1,1),(376,43,3,38,1,1,1,1,1),(377,43,3,39,1,1,1,1,1),(378,43,3,40,1,1,1,1,1),(379,43,3,41,1,1,1,1,1),(380,44,3,37,1,1,1,1,1),(381,44,3,38,1,1,1,1,1),(382,44,3,39,1,1,1,1,1),(383,44,3,40,1,1,1,1,1),(384,44,3,41,1,1,1,1,1),(385,45,3,37,1,1,1,1,1),(386,45,3,38,1,1,1,1,1),(387,45,3,39,1,1,1,1,1),(388,45,3,40,1,1,1,1,1),(389,45,3,41,1,1,1,1,1),(390,46,3,37,1,1,1,1,1),(391,46,3,38,1,1,1,1,1),(392,46,3,39,1,1,1,1,1),(393,46,3,40,1,1,1,1,1),(394,46,3,41,1,1,1,1,1),(395,47,3,37,1,1,1,1,1),(396,47,3,38,1,1,1,1,1),(397,47,3,39,1,1,1,1,1),(398,47,3,40,1,1,1,1,1),(399,47,3,41,1,1,1,1,1),(400,48,3,37,1,1,1,1,1),(401,48,3,38,1,1,1,1,1),(402,48,3,39,1,1,1,1,1),(403,48,3,40,1,1,1,1,1),(404,48,3,41,1,1,1,1,1),(405,49,3,37,1,1,1,1,1),(406,49,3,38,1,1,1,1,1),(407,49,3,39,1,1,1,1,1),(408,49,3,40,1,1,1,1,1),(409,49,3,41,1,1,1,1,1),(410,50,3,37,1,1,1,1,1),(411,50,3,38,1,1,1,1,1),(412,50,3,39,1,1,1,1,1),(413,50,3,40,1,1,1,1,1),(414,50,3,41,1,1,1,1,1),(415,51,3,37,1,1,1,1,1),(416,51,3,38,1,1,1,1,1),(417,51,3,39,1,1,1,1,1),(418,51,3,40,1,1,1,1,1),(419,51,3,41,1,1,1,1,1),(420,52,3,37,1,1,1,1,1),(421,52,3,38,1,1,1,1,1),(422,52,3,39,1,1,1,1,1),(423,52,3,40,1,1,1,1,1),(424,52,3,41,1,1,1,1,1),(425,53,3,37,1,1,1,1,1),(426,53,3,38,1,1,1,1,1),(427,53,3,39,1,1,1,1,1),(428,53,3,40,1,1,1,1,1),(429,53,3,41,1,1,1,1,1),(430,54,3,37,1,1,1,1,1),(431,54,3,38,1,1,1,1,1),(432,54,3,39,1,1,1,1,1),(433,54,3,40,1,1,1,1,1),(434,54,3,41,1,1,1,1,1),(435,55,3,37,1,1,1,1,1),(436,55,3,38,1,1,1,1,1),(437,55,3,39,1,1,1,1,1),(438,55,3,40,1,1,1,1,1),(439,55,3,41,1,1,1,1,1),(440,56,3,37,1,1,1,1,1),(441,56,3,38,1,1,1,1,1),(442,56,3,39,1,1,1,1,1),(443,56,3,40,1,1,1,1,1),(444,56,3,41,1,1,1,1,1),(445,41,4,42,1,1,1,1,1),(446,41,4,43,1,1,1,1,1),(447,41,4,44,1,1,1,1,1),(448,41,4,45,1,1,1,1,1),(449,41,4,46,1,1,1,1,1),(450,41,4,47,1,1,1,1,1),(451,41,4,48,1,1,1,1,1),(452,41,4,49,1,1,1,1,1),(453,41,4,50,1,1,1,1,1),(454,41,4,51,1,1,1,1,1),(455,41,4,52,1,1,1,1,1),(456,41,4,53,1,1,1,1,1),(457,41,4,54,1,1,1,1,1),(458,41,4,55,1,1,1,1,1),(459,41,4,56,1,1,1,1,1),(460,42,4,42,1,1,1,1,1),(461,42,4,43,1,1,1,1,1),(462,42,4,44,1,1,1,1,1),(463,42,4,45,1,1,1,1,1),(464,42,4,46,1,1,1,1,1),(465,42,4,47,1,1,1,1,1),(466,42,4,48,1,1,1,1,1),(467,42,4,49,1,1,1,1,1),(468,42,4,50,1,1,1,1,1),(469,42,4,51,1,1,1,1,1),(470,42,4,52,1,1,1,1,1),(471,42,4,53,1,1,1,1,1),(472,42,4,54,1,1,1,1,1),(473,42,4,55,1,1,1,1,1),(474,42,4,56,1,1,1,1,1),(475,43,4,42,1,1,1,1,1),(476,43,4,43,1,1,1,1,1),(477,43,4,44,1,1,1,1,1),(478,43,4,45,1,1,1,1,1),(479,43,4,46,1,1,1,1,1),(480,43,4,47,1,1,1,1,1),(481,43,4,48,1,1,1,1,1),(482,43,4,49,1,1,1,1,1),(483,43,4,50,1,1,1,1,1),(484,43,4,51,1,1,1,1,1),(485,43,4,52,1,1,1,1,1),(486,43,4,53,1,1,1,1,1),(487,43,4,54,1,1,1,1,1),(488,43,4,55,1,1,1,1,1),(489,43,4,56,1,1,1,1,1),(490,44,4,42,1,1,1,1,1),(491,44,4,43,1,1,1,1,1),(492,44,4,44,1,1,1,1,1),(493,44,4,45,1,1,1,1,1),(494,44,4,46,1,1,1,1,1),(495,44,4,47,1,1,1,1,1),(496,44,4,48,1,1,1,1,1),(497,44,4,49,1,1,1,1,1),(498,44,4,50,1,1,1,1,1),(499,44,4,51,1,1,1,1,1),(500,44,4,52,1,1,1,1,1),(501,44,4,53,1,1,1,1,1),(502,44,4,54,1,1,1,1,1),(503,44,4,55,1,1,1,1,1),(504,44,4,56,1,1,1,1,1),(505,45,4,42,1,1,1,1,1),(506,45,4,43,1,1,1,1,1),(507,45,4,44,1,1,1,1,1),(508,45,4,45,1,1,1,1,1),(509,45,4,46,1,1,1,1,1),(510,45,4,47,1,1,1,1,1),(511,45,4,48,1,1,1,1,1),(512,45,4,49,1,1,1,1,1),(513,45,4,50,1,1,1,1,1),(514,45,4,51,1,1,1,1,1),(515,45,4,52,1,1,1,1,1),(516,45,4,53,1,1,1,1,1),(517,45,4,54,1,1,1,1,1),(518,45,4,55,1,1,1,1,1),(519,45,4,56,1,1,1,1,1),(520,46,4,42,1,1,1,1,1),(521,46,4,43,1,1,1,1,1),(522,46,4,44,1,1,1,1,1),(523,46,4,45,1,1,1,1,1),(524,46,4,46,1,1,1,1,1),(525,46,4,47,1,1,1,1,1),(526,46,4,48,1,1,1,1,1),(527,46,4,49,1,1,1,1,1),(528,46,4,50,1,1,1,1,1),(529,46,4,51,1,1,1,1,1),(530,46,4,52,1,1,1,1,1),(531,46,4,53,1,1,1,1,1),(532,46,4,54,1,1,1,1,1),(533,46,4,55,1,1,1,1,1),(534,46,4,56,1,1,1,1,1),(535,47,4,42,1,1,1,1,1),(536,47,4,43,1,1,1,1,1),(537,47,4,44,1,1,1,1,1),(538,47,4,45,1,1,1,1,1),(539,47,4,46,1,1,1,1,1),(540,47,4,47,1,1,1,1,1),(541,47,4,48,1,1,1,1,1),(542,47,4,49,1,1,1,1,1),(543,47,4,50,1,1,1,1,1),(544,47,4,51,1,1,1,1,1),(545,47,4,52,1,1,1,1,1),(546,47,4,53,1,1,1,1,1),(547,47,4,54,1,1,1,1,1),(548,47,4,55,1,1,1,1,1),(549,47,4,56,1,1,1,1,1),(550,48,4,42,1,1,1,1,1),(551,48,4,43,1,1,1,1,1),(552,48,4,44,1,1,1,1,1),(553,48,4,45,1,1,1,1,1),(554,48,4,46,1,1,1,1,1),(555,48,4,47,1,1,1,1,1),(556,48,4,48,1,1,1,1,1),(557,48,4,49,1,1,1,1,1),(558,48,4,50,1,1,1,1,1),(559,48,4,51,1,1,1,1,1),(560,48,4,52,1,1,1,1,1),(561,48,4,53,1,1,1,1,1),(562,48,4,54,1,1,1,1,1),(563,48,4,55,1,1,1,1,1),(564,48,4,56,1,1,1,1,1),(565,49,4,42,1,1,1,1,1),(566,49,4,43,1,1,1,1,1),(567,49,4,44,1,1,1,1,1),(568,49,4,45,1,1,1,1,1),(569,49,4,46,1,1,1,1,1),(570,49,4,47,1,1,1,1,1),(571,49,4,48,1,1,1,1,1),(572,49,4,49,1,1,1,1,1),(573,49,4,50,1,1,1,1,1),(574,49,4,51,1,1,1,1,1),(575,49,4,52,1,1,1,1,1),(576,49,4,53,1,1,1,1,1),(577,49,4,54,1,1,1,1,1),(578,49,4,55,1,1,1,1,1),(579,49,4,56,1,1,1,1,1),(580,50,4,42,1,1,1,1,1),(581,50,4,43,1,1,1,1,1),(582,50,4,44,1,1,1,1,1),(583,50,4,45,1,1,1,1,1),(584,50,4,46,1,1,1,1,1),(585,50,4,47,1,1,1,1,1),(586,50,4,48,1,1,1,1,1),(587,50,4,49,1,1,1,1,1),(588,50,4,50,1,1,1,1,1),(589,50,4,51,1,1,1,1,1),(590,50,4,52,1,1,1,1,1),(591,50,4,53,1,1,1,1,1),(592,50,4,54,1,1,1,1,1),(593,50,4,55,1,1,1,1,1),(594,50,4,56,1,1,1,1,1),(595,51,4,42,1,1,1,1,1),(596,51,4,43,1,1,1,1,1),(597,51,4,44,1,1,1,1,1),(598,51,4,45,1,1,1,1,1),(599,51,4,46,1,1,1,1,1),(600,51,4,47,1,1,1,1,1),(601,51,4,48,1,1,1,1,1),(602,51,4,49,1,1,1,1,1),(603,51,4,50,1,1,1,1,1),(604,51,4,51,1,1,1,1,1),(605,51,4,52,1,1,1,1,1),(606,51,4,53,1,1,1,1,1),(607,51,4,54,1,1,1,1,1),(608,51,4,55,1,1,1,1,1),(609,51,4,56,1,1,1,1,1),(610,52,4,42,1,1,1,1,1),(611,52,4,43,1,1,1,1,1),(612,52,4,44,1,1,1,1,1),(613,52,4,45,1,1,1,1,1),(614,52,4,46,1,1,1,1,1),(615,52,4,47,1,1,1,1,1),(616,52,4,48,1,1,1,1,1),(617,52,4,49,1,1,1,1,1),(618,52,4,50,1,1,1,1,1),(619,52,4,51,1,1,1,1,1),(620,52,4,52,1,1,1,1,1),(621,52,4,53,1,1,1,1,1),(622,52,4,54,1,1,1,1,1),(623,52,4,55,1,1,1,1,1),(624,52,4,56,1,1,1,1,1),(625,53,4,42,1,1,1,1,1),(626,53,4,43,1,1,1,1,1),(627,53,4,44,1,1,1,1,1),(628,53,4,45,1,1,1,1,1),(629,53,4,46,1,1,1,1,1),(630,53,4,47,1,1,1,1,1),(631,53,4,48,1,1,1,1,1),(632,53,4,49,1,1,1,1,1),(633,53,4,50,1,1,1,1,1),(634,53,4,51,1,1,1,1,1),(635,53,4,52,1,1,1,1,1),(636,53,4,53,1,1,1,1,1),(637,53,4,54,1,1,1,1,1),(638,53,4,55,1,1,1,1,1),(639,53,4,56,1,1,1,1,1),(640,54,4,42,1,1,1,1,1),(641,54,4,43,1,1,1,1,1),(642,54,4,44,1,1,1,1,1),(643,54,4,45,1,1,1,1,1),(644,54,4,46,1,1,1,1,1),(645,54,4,47,1,1,1,1,1),(646,54,4,48,1,1,1,1,1),(647,54,4,49,1,1,1,1,1),(648,54,4,50,1,1,1,1,1),(649,54,4,51,1,1,1,1,1),(650,54,4,52,1,1,1,1,1),(651,54,4,53,1,1,1,1,1),(652,54,4,54,1,1,1,1,1),(653,54,4,55,1,1,1,1,1),(654,54,4,56,1,1,1,1,1),(655,55,4,42,1,1,1,1,1),(656,55,4,43,1,1,1,1,1),(657,55,4,44,1,1,1,1,1),(658,55,4,45,1,1,1,1,1),(659,55,4,46,1,1,1,1,1),(660,55,4,47,1,1,1,1,1),(661,55,4,48,1,1,1,1,1),(662,55,4,49,1,1,1,1,1),(663,55,4,50,1,1,1,1,1),(664,55,4,51,1,1,1,1,1),(665,55,4,52,1,1,1,1,1),(666,55,4,53,1,1,1,1,1),(667,55,4,54,1,1,1,1,1),(668,55,4,55,1,1,1,1,1),(669,55,4,56,1,1,1,1,1),(670,56,4,42,1,1,1,1,1),(671,56,4,43,1,1,1,1,1),(672,56,4,44,1,1,1,1,1),(673,56,4,45,1,1,1,1,1),(674,56,4,46,1,1,1,1,1),(675,56,4,47,1,1,1,1,1),(676,56,4,48,1,1,1,1,1),(677,56,4,49,1,1,1,1,1),(678,56,4,50,1,1,1,1,1),(679,56,4,51,1,1,1,1,1),(680,56,4,52,1,1,1,1,1),(681,56,4,53,1,1,1,1,1),(682,56,4,54,1,1,1,1,1),(683,56,4,55,1,1,1,1,1),(684,56,4,56,1,1,1,1,1),(685,41,5,66,1,1,1,1,1),(686,41,5,67,1,1,1,1,1),(687,41,5,68,1,1,1,1,1),(688,41,5,69,1,1,1,1,1),(689,41,5,70,1,1,1,1,1),(690,41,5,71,1,1,1,1,1),(691,41,5,72,1,1,1,1,1),(692,41,5,73,1,1,1,1,1),(693,41,5,74,1,1,1,1,1),(694,41,5,75,1,1,1,1,1),(695,41,5,76,1,1,1,1,1),(696,41,5,77,1,1,1,1,1),(697,41,5,78,1,1,1,1,1),(698,41,5,79,1,1,1,1,1),(699,41,5,80,1,1,1,1,1),(700,41,5,81,1,1,1,1,1),(701,41,5,82,1,1,1,1,1),(702,41,5,83,1,1,1,1,1),(703,41,5,84,1,1,1,1,1),(704,41,5,85,1,1,1,1,1),(705,41,5,86,1,1,1,1,1),(706,41,5,87,1,1,1,1,1),(707,41,5,88,1,1,1,1,1),(708,41,5,89,1,1,1,1,1),(709,41,5,90,1,1,1,1,1),(710,41,5,91,1,1,1,1,1),(711,41,5,92,1,1,1,1,1),(712,41,5,93,1,1,1,1,1),(713,41,5,94,1,1,1,1,1),(714,41,5,95,1,1,1,1,1),(715,41,5,96,1,1,1,1,1),(716,41,5,97,1,1,1,1,1),(717,41,5,98,1,1,1,1,1),(718,42,5,66,1,1,1,1,1),(719,42,5,67,1,1,1,1,1),(720,42,5,68,1,1,1,1,1),(721,42,5,69,1,1,1,1,1),(722,42,5,70,1,1,1,1,1),(723,42,5,71,1,1,1,1,1),(724,42,5,72,1,1,1,1,1),(725,42,5,73,1,1,1,1,1),(726,42,5,74,1,1,1,1,1),(727,42,5,75,1,1,1,1,1),(728,42,5,76,1,1,1,1,1),(729,42,5,77,1,1,1,1,1),(730,42,5,78,1,1,1,1,1),(731,42,5,79,1,1,1,1,1),(732,42,5,80,1,1,1,1,1),(733,42,5,81,1,1,1,1,1),(734,42,5,82,1,1,1,1,1),(735,42,5,83,1,1,1,1,1),(736,42,5,84,1,1,1,1,1),(737,42,5,85,1,1,1,1,1),(738,42,5,86,1,1,1,1,1),(739,42,5,87,1,1,1,1,1),(740,42,5,88,1,1,1,1,1),(741,42,5,89,1,1,1,1,1),(742,42,5,90,1,1,1,1,1),(743,42,5,91,1,1,1,1,1),(744,42,5,92,1,1,1,1,1),(745,42,5,93,1,1,1,1,1),(746,42,5,94,1,1,1,1,1),(747,42,5,95,1,1,1,1,1),(748,42,5,96,1,1,1,1,1),(749,42,5,97,1,1,1,1,1),(750,42,5,98,1,1,1,1,1),(751,43,5,66,1,1,1,1,1),(752,43,5,67,1,1,1,1,1),(753,43,5,68,1,1,1,1,1),(754,43,5,69,1,1,1,1,1),(755,43,5,70,1,1,1,1,1),(756,43,5,71,1,1,1,1,1),(757,43,5,72,1,1,1,1,1),(758,43,5,73,1,1,1,1,1),(759,43,5,74,1,1,1,1,1),(760,43,5,75,1,1,1,1,1),(761,43,5,76,1,1,1,1,1),(762,43,5,77,1,1,1,1,1),(763,43,5,78,1,1,1,1,1),(764,43,5,79,1,1,1,1,1),(765,43,5,80,1,1,1,1,1),(766,43,5,81,1,1,1,1,1),(767,43,5,82,1,1,1,1,1),(768,43,5,83,1,1,1,1,1),(769,43,5,84,1,1,1,1,1),(770,43,5,85,1,1,1,1,1),(771,43,5,86,1,1,1,1,1),(772,43,5,87,1,1,1,1,1),(773,43,5,88,1,1,1,1,1),(774,43,5,89,1,1,1,1,1),(775,43,5,90,1,1,1,1,1),(776,43,5,91,1,1,1,1,1),(777,43,5,92,1,1,1,1,1),(778,43,5,93,1,1,1,1,1),(779,43,5,94,1,1,1,1,1),(780,43,5,95,1,1,1,1,1),(781,43,5,96,1,1,1,1,1),(782,43,5,97,1,1,1,1,1),(783,43,5,98,1,1,1,1,1),(784,44,5,66,1,1,1,1,1),(785,44,5,67,1,1,1,1,1),(786,44,5,68,1,1,1,1,1),(787,44,5,69,1,1,1,1,1),(788,44,5,70,1,1,1,1,1),(789,44,5,71,1,1,1,1,1),(790,44,5,72,1,1,1,1,1),(791,44,5,73,1,1,1,1,1),(792,44,5,74,1,1,1,1,1),(793,44,5,75,1,1,1,1,1),(794,44,5,76,1,1,1,1,1),(795,44,5,77,1,1,1,1,1),(796,44,5,78,1,1,1,1,1),(797,44,5,79,1,1,1,1,1),(798,44,5,80,1,1,1,1,1),(799,44,5,81,1,1,1,1,1),(800,44,5,82,1,1,1,1,1),(801,44,5,83,1,1,1,1,1),(802,44,5,84,1,1,1,1,1),(803,44,5,85,1,1,1,1,1),(804,44,5,86,1,1,1,1,1),(805,44,5,87,1,1,1,1,1),(806,44,5,88,1,1,1,1,1),(807,44,5,89,1,1,1,1,1),(808,44,5,90,1,1,1,1,1),(809,44,5,91,1,1,1,1,1),(810,44,5,92,1,1,1,1,1),(811,44,5,93,1,1,1,1,1),(812,44,5,94,1,1,1,1,1),(813,44,5,95,1,1,1,1,1),(814,44,5,96,1,1,1,1,1),(815,44,5,97,1,1,1,1,1),(816,44,5,98,1,1,1,1,1),(817,45,5,66,1,1,1,1,1),(818,45,5,67,1,1,1,1,1),(819,45,5,68,1,1,1,1,1),(820,45,5,69,1,1,1,1,1),(821,45,5,70,1,1,1,1,1),(822,45,5,71,1,1,1,1,1),(823,45,5,72,1,1,1,1,1),(824,45,5,73,1,1,1,1,1),(825,45,5,74,1,1,1,1,1),(826,45,5,75,1,1,1,1,1),(827,45,5,76,1,1,1,1,1),(828,45,5,77,1,1,1,1,1),(829,45,5,78,1,1,1,1,1),(830,45,5,79,1,1,1,1,1),(831,45,5,80,1,1,1,1,1),(832,45,5,81,1,1,1,1,1),(833,45,5,82,1,1,1,1,1),(834,45,5,83,1,1,1,1,1),(835,45,5,84,1,1,1,1,1),(836,45,5,85,1,1,1,1,1),(837,45,5,86,1,1,1,1,1),(838,45,5,87,1,1,1,1,1),(839,45,5,88,1,1,1,1,1),(840,45,5,89,1,1,1,1,1),(841,45,5,90,1,1,1,1,1),(842,45,5,91,1,1,1,1,1),(843,45,5,92,1,1,1,1,1),(844,45,5,93,1,1,1,1,1),(845,45,5,94,1,1,1,1,1),(846,45,5,95,1,1,1,1,1),(847,45,5,96,1,1,1,1,1),(848,45,5,97,1,1,1,1,1),(849,45,5,98,1,1,1,1,1),(850,46,5,66,1,1,1,1,1),(851,46,5,67,1,1,1,1,1),(852,46,5,68,1,1,1,1,1),(853,46,5,69,1,1,1,1,1),(854,46,5,70,1,1,1,1,1),(855,46,5,71,1,1,1,1,1),(856,46,5,72,1,1,1,1,1),(857,46,5,73,1,1,1,1,1),(858,46,5,74,1,1,1,1,1),(859,46,5,75,1,1,1,1,1),(860,46,5,76,1,1,1,1,1),(861,46,5,77,1,1,1,1,1),(862,46,5,78,1,1,1,1,1),(863,46,5,79,1,1,1,1,1),(864,46,5,80,1,1,1,1,1),(865,46,5,81,1,1,1,1,1),(866,46,5,82,1,1,1,1,1),(867,46,5,83,1,1,1,1,1),(868,46,5,84,1,1,1,1,1),(869,46,5,85,1,1,1,1,1),(870,46,5,86,1,1,1,1,1),(871,46,5,87,1,1,1,1,1),(872,46,5,88,1,1,1,1,1),(873,46,5,89,1,1,1,1,1),(874,46,5,90,1,1,1,1,1),(875,46,5,91,1,1,1,1,1),(876,46,5,92,1,1,1,1,1),(877,46,5,93,1,1,1,1,1),(878,46,5,94,1,1,1,1,1),(879,46,5,95,1,1,1,1,1),(880,46,5,96,1,1,1,1,1),(881,46,5,97,1,1,1,1,1),(882,46,5,98,1,1,1,1,1),(883,47,5,66,1,1,1,1,1),(884,47,5,67,1,1,1,1,1),(885,47,5,68,1,1,1,1,1),(886,47,5,69,1,1,1,1,1),(887,47,5,70,1,1,1,1,1),(888,47,5,71,1,1,1,1,1),(889,47,5,72,1,1,1,1,1),(890,47,5,73,1,1,1,1,1),(891,47,5,74,1,1,1,1,1),(892,47,5,75,1,1,1,1,1),(893,47,5,76,1,1,1,1,1),(894,47,5,77,1,1,1,1,1),(895,47,5,78,1,1,1,1,1),(896,47,5,79,1,1,1,1,1),(897,47,5,80,1,1,1,1,1),(898,47,5,81,1,1,1,1,1),(899,47,5,82,1,1,1,1,1),(900,47,5,83,1,1,1,1,1),(901,47,5,84,1,1,1,1,1),(902,47,5,85,1,1,1,1,1),(903,47,5,86,1,1,1,1,1),(904,47,5,87,1,1,1,1,1),(905,47,5,88,1,1,1,1,1),(906,47,5,89,1,1,1,1,1),(907,47,5,90,1,1,1,1,1),(908,47,5,91,1,1,1,1,1),(909,47,5,92,1,1,1,1,1),(910,47,5,93,1,1,1,1,1),(911,47,5,94,1,1,1,1,1),(912,47,5,95,1,1,1,1,1),(913,47,5,96,1,1,1,1,1),(914,47,5,97,1,1,1,1,1),(915,47,5,98,1,1,1,1,1),(916,48,5,66,1,1,1,1,1),(917,48,5,67,1,1,1,1,1),(918,48,5,68,1,1,1,1,1),(919,48,5,69,1,1,1,1,1),(920,48,5,70,1,1,1,1,1),(921,48,5,71,1,1,1,1,1),(922,48,5,72,1,1,1,1,1),(923,48,5,73,1,1,1,1,1),(924,48,5,74,1,1,1,1,1),(925,48,5,75,1,1,1,1,1),(926,48,5,76,1,1,1,1,1),(927,48,5,77,1,1,1,1,1),(928,48,5,78,1,1,1,1,1),(929,48,5,79,1,1,1,1,1),(930,48,5,80,1,1,1,1,1),(931,48,5,81,1,1,1,1,1),(932,48,5,82,1,1,1,1,1),(933,48,5,83,1,1,1,1,1),(934,48,5,84,1,1,1,1,1),(935,48,5,85,1,1,1,1,1),(936,48,5,86,1,1,1,1,1),(937,48,5,87,1,1,1,1,1),(938,48,5,88,1,1,1,1,1),(939,48,5,89,1,1,1,1,1),(940,48,5,90,1,1,1,1,1),(941,48,5,91,1,1,1,1,1),(942,48,5,92,1,1,1,1,1),(943,48,5,93,1,1,1,1,1),(944,48,5,94,1,1,1,1,1),(945,48,5,95,1,1,1,1,1),(946,48,5,96,1,1,1,1,1),(947,48,5,97,1,1,1,1,1),(948,48,5,98,1,1,1,1,1),(949,49,5,66,1,1,1,1,1),(950,49,5,67,1,1,1,1,1),(951,49,5,68,1,1,1,1,1),(952,49,5,69,1,1,1,1,1),(953,49,5,70,1,1,1,1,1),(954,49,5,71,1,1,1,1,1),(955,49,5,72,1,1,1,1,1),(956,49,5,73,1,1,1,1,1),(957,49,5,74,1,1,1,1,1),(958,49,5,75,1,1,1,1,1),(959,49,5,76,1,1,1,1,1),(960,49,5,77,1,1,1,1,1),(961,49,5,78,1,1,1,1,1),(962,49,5,79,1,1,1,1,1),(963,49,5,80,1,1,1,1,1),(964,49,5,81,1,1,1,1,1),(965,49,5,82,1,1,1,1,1),(966,49,5,83,1,1,1,1,1),(967,49,5,84,1,1,1,1,1),(968,49,5,85,1,1,1,1,1),(969,49,5,86,1,1,1,1,1),(970,49,5,87,1,1,1,1,1),(971,49,5,88,1,1,1,1,1),(972,49,5,89,1,1,1,1,1),(973,49,5,90,1,1,1,1,1),(974,49,5,91,1,1,1,1,1),(975,49,5,92,1,1,1,1,1),(976,49,5,93,1,1,1,1,1),(977,49,5,94,1,1,1,1,1),(978,49,5,95,1,1,1,1,1),(979,49,5,96,1,1,1,1,1),(980,49,5,97,1,1,1,1,1),(981,49,5,98,1,1,1,1,1),(982,50,5,66,1,1,1,1,1),(983,50,5,67,1,1,1,1,1),(984,50,5,68,1,1,1,1,1),(985,50,5,69,1,1,1,1,1),(986,50,5,70,1,1,1,1,1),(987,50,5,71,1,1,1,1,1),(988,50,5,72,1,1,1,1,1),(989,50,5,73,1,1,1,1,1),(990,50,5,74,1,1,1,1,1),(991,50,5,75,1,1,1,1,1),(992,50,5,76,1,1,1,1,1),(993,50,5,77,1,1,1,1,1),(994,50,5,78,1,1,1,1,1),(995,50,5,79,1,1,1,1,1),(996,50,5,80,1,1,1,1,1),(997,50,5,81,1,1,1,1,1),(998,50,5,82,1,1,1,1,1),(999,50,5,83,1,1,1,1,1),(1000,50,5,84,1,1,1,1,1),(1001,50,5,85,1,1,1,1,1),(1002,50,5,86,1,1,1,1,1),(1003,50,5,87,1,1,1,1,1),(1004,50,5,88,1,1,1,1,1),(1005,50,5,89,1,1,1,1,1),(1006,50,5,90,1,1,1,1,1),(1007,50,5,91,1,1,1,1,1),(1008,50,5,92,1,1,1,1,1),(1009,50,5,93,1,1,1,1,1),(1010,50,5,94,1,1,1,1,1),(1011,50,5,95,1,1,1,1,1),(1012,50,5,96,1,1,1,1,1),(1013,50,5,97,1,1,1,1,1),(1014,50,5,98,1,1,1,1,1),(1015,51,5,66,1,1,1,1,1),(1016,51,5,67,1,1,1,1,1),(1017,51,5,68,1,1,1,1,1),(1018,51,5,69,1,1,1,1,1),(1019,51,5,70,1,1,1,1,1),(1020,51,5,71,1,1,1,1,1),(1021,51,5,72,1,1,1,1,1),(1022,51,5,73,1,1,1,1,1),(1023,51,5,74,1,1,1,1,1),(1024,51,5,75,1,1,1,1,1),(1025,51,5,76,1,1,1,1,1),(1026,51,5,77,1,1,1,1,1),(1027,51,5,78,1,1,1,1,1),(1028,51,5,79,1,1,1,1,1),(1029,51,5,80,1,1,1,1,1),(1030,51,5,81,1,1,1,1,1),(1031,51,5,82,1,1,1,1,1),(1032,51,5,83,1,1,1,1,1),(1033,51,5,84,1,1,1,1,1),(1034,51,5,85,1,1,1,1,1),(1035,51,5,86,1,1,1,1,1),(1036,51,5,87,1,1,1,1,1),(1037,51,5,88,1,1,1,1,1),(1038,51,5,89,1,1,1,1,1),(1039,51,5,90,1,1,1,1,1),(1040,51,5,91,1,1,1,1,1),(1041,51,5,92,1,1,1,1,1),(1042,51,5,93,1,1,1,1,1),(1043,51,5,94,1,1,1,1,1),(1044,51,5,95,1,1,1,1,1),(1045,51,5,96,1,1,1,1,1),(1046,51,5,97,1,1,1,1,1),(1047,51,5,98,1,1,1,1,1),(1048,52,5,66,1,1,1,1,1),(1049,52,5,67,1,1,1,1,1),(1050,52,5,68,1,1,1,1,1),(1051,52,5,69,1,1,1,1,1),(1052,52,5,70,1,1,1,1,1),(1053,52,5,71,1,1,1,1,1),(1054,52,5,72,1,1,1,1,1),(1055,52,5,73,1,1,1,1,1),(1056,52,5,74,1,1,1,1,1),(1057,52,5,75,1,1,1,1,1),(1058,52,5,76,1,1,1,1,1),(1059,52,5,77,1,1,1,1,1),(1060,52,5,78,1,1,1,1,1),(1061,52,5,79,1,1,1,1,1),(1062,52,5,80,1,1,1,1,1),(1063,52,5,81,1,1,1,1,1),(1064,52,5,82,1,1,1,1,1),(1065,52,5,83,1,1,1,1,1),(1066,52,5,84,1,1,1,1,1),(1067,52,5,85,1,1,1,1,1),(1068,52,5,86,1,1,1,1,1),(1069,52,5,87,1,1,1,1,1),(1070,52,5,88,1,1,1,1,1),(1071,52,5,89,1,1,1,1,1),(1072,52,5,90,1,1,1,1,1),(1073,52,5,91,1,1,1,1,1),(1074,52,5,92,1,1,1,1,1),(1075,52,5,93,1,1,1,1,1),(1076,52,5,94,1,1,1,1,1),(1077,52,5,95,1,1,1,1,1),(1078,52,5,96,1,1,1,1,1),(1079,52,5,97,1,1,1,1,1),(1080,52,5,98,1,1,1,1,1),(1081,53,5,66,1,1,1,1,1),(1082,53,5,67,1,1,1,1,1),(1083,53,5,68,1,1,1,1,1),(1084,53,5,69,1,1,1,1,1),(1085,53,5,70,1,1,1,1,1),(1086,53,5,71,1,1,1,1,1),(1087,53,5,72,1,1,1,1,1),(1088,53,5,73,1,1,1,1,1),(1089,53,5,74,1,1,1,1,1),(1090,53,5,75,1,1,1,1,1),(1091,53,5,76,1,1,1,1,1),(1092,53,5,77,1,1,1,1,1),(1093,53,5,78,1,1,1,1,1),(1094,53,5,79,1,1,1,1,1),(1095,53,5,80,1,1,1,1,1),(1096,53,5,81,1,1,1,1,1),(1097,53,5,82,1,1,1,1,1),(1098,53,5,83,1,1,1,1,1),(1099,53,5,84,1,1,1,1,1),(1100,53,5,85,1,1,1,1,1),(1101,53,5,86,1,1,1,1,1),(1102,53,5,87,1,1,1,1,1),(1103,53,5,88,1,1,1,1,1),(1104,53,5,89,1,1,1,1,1),(1105,53,5,90,1,1,1,1,1),(1106,53,5,91,1,1,1,1,1),(1107,53,5,92,1,1,1,1,1),(1108,53,5,93,1,1,1,1,1),(1109,53,5,94,1,1,1,1,1),(1110,53,5,95,1,1,1,1,1),(1111,53,5,96,1,1,1,1,1),(1112,53,5,97,1,1,1,1,1),(1113,53,5,98,1,1,1,1,1),(1114,54,5,66,1,1,1,1,1),(1115,54,5,67,1,1,1,1,1),(1116,54,5,68,1,1,1,1,1),(1117,54,5,69,1,1,1,1,1),(1118,54,5,70,1,1,1,1,1),(1119,54,5,71,1,1,1,1,1),(1120,54,5,72,1,1,1,1,1),(1121,54,5,73,1,1,1,1,1),(1122,54,5,74,1,1,1,1,1),(1123,54,5,75,1,1,1,1,1),(1124,54,5,76,1,1,1,1,1),(1125,54,5,77,1,1,1,1,1),(1126,54,5,78,1,1,1,1,1),(1127,54,5,79,1,1,1,1,1),(1128,54,5,80,1,1,1,1,1),(1129,54,5,81,1,1,1,1,1),(1130,54,5,82,1,1,1,1,1),(1131,54,5,83,1,1,1,1,1),(1132,54,5,84,1,1,1,1,1),(1133,54,5,85,1,1,1,1,1),(1134,54,5,86,1,1,1,1,1),(1135,54,5,87,1,1,1,1,1),(1136,54,5,88,1,1,1,1,1),(1137,54,5,89,1,1,1,1,1),(1138,54,5,90,1,1,1,1,1),(1139,54,5,91,1,1,1,1,1),(1140,54,5,92,1,1,1,1,1),(1141,54,5,93,1,1,1,1,1),(1142,54,5,94,1,1,1,1,1),(1143,54,5,95,1,1,1,1,1),(1144,54,5,96,1,1,1,1,1),(1145,54,5,97,1,1,1,1,1),(1146,54,5,98,1,1,1,1,1),(1147,55,5,66,1,1,1,1,1),(1148,55,5,67,1,1,1,1,1),(1149,55,5,68,1,1,1,1,1),(1150,55,5,69,1,1,1,1,1),(1151,55,5,70,1,1,1,1,1),(1152,55,5,71,1,1,1,1,1),(1153,55,5,72,1,1,1,1,1),(1154,55,5,73,1,1,1,1,1),(1155,55,5,74,1,1,1,1,1),(1156,55,5,75,1,1,1,1,1),(1157,55,5,76,1,1,1,1,1),(1158,55,5,77,1,1,1,1,1),(1159,55,5,78,1,1,1,1,1),(1160,55,5,79,1,1,1,1,1),(1161,55,5,80,1,1,1,1,1),(1162,55,5,81,1,1,1,1,1),(1163,55,5,82,1,1,1,1,1),(1164,55,5,83,1,1,1,1,1),(1165,55,5,84,1,1,1,1,1),(1166,55,5,85,1,1,1,1,1),(1167,55,5,86,1,1,1,1,1),(1168,55,5,87,1,1,1,1,1),(1169,55,5,88,1,1,1,1,1),(1170,55,5,89,1,1,1,1,1),(1171,55,5,90,1,1,1,1,1),(1172,55,5,91,1,1,1,1,1),(1173,55,5,92,1,1,1,1,1),(1174,55,5,93,1,1,1,1,1),(1175,55,5,94,1,1,1,1,1),(1176,55,5,95,1,1,1,1,1),(1177,55,5,96,1,1,1,1,1),(1178,55,5,97,1,1,1,1,1),(1179,55,5,98,1,1,1,1,1),(1180,56,5,66,1,1,1,1,1),(1181,56,5,67,1,1,1,1,1),(1182,56,5,68,1,1,1,1,1),(1183,56,5,69,1,1,1,1,1),(1184,56,5,70,1,1,1,1,1),(1185,56,5,71,1,1,1,1,1),(1186,56,5,72,1,1,1,1,1),(1187,56,5,73,1,1,1,1,1),(1188,56,5,74,1,1,1,1,1),(1189,56,5,75,1,1,1,1,1),(1190,56,5,76,1,1,1,1,1),(1191,56,5,77,1,1,1,1,1),(1192,56,5,78,1,1,1,1,1),(1193,56,5,79,1,1,1,1,1),(1194,56,5,80,1,1,1,1,1),(1195,56,5,81,1,1,1,1,1),(1196,56,5,82,1,1,1,1,1),(1197,56,5,83,1,1,1,1,1),(1198,56,5,84,1,1,1,1,1),(1199,56,5,85,1,1,1,1,1),(1200,56,5,86,1,1,1,1,1),(1201,56,5,87,1,1,1,1,1),(1202,56,5,88,1,1,1,1,1),(1203,56,5,89,1,1,1,1,1),(1204,56,5,90,1,1,1,1,1),(1205,56,5,91,1,1,1,1,1),(1206,56,5,92,1,1,1,1,1),(1207,56,5,93,1,1,1,1,1),(1208,56,5,94,1,1,1,1,1),(1209,56,5,95,1,1,1,1,1),(1210,56,5,96,1,1,1,1,1),(1211,56,5,97,1,1,1,1,1),(1212,56,5,98,1,1,1,1,1),(1213,57,1,5,1,1,1,1,1),(1214,57,1,6,1,1,1,1,1),(1215,58,1,5,1,1,1,1,1),(1216,58,1,6,1,1,1,1,1),(1217,59,1,5,1,1,1,1,1),(1218,59,1,6,1,1,1,1,1),(1219,57,3,36,1,1,1,1,1),(1220,57,3,37,1,1,1,1,1),(1221,57,3,38,1,1,1,1,1),(1222,57,3,39,1,1,1,1,1),(1223,57,3,40,1,1,1,1,1),(1224,57,3,41,1,1,1,1,1),(1225,58,3,36,1,1,1,1,1),(1226,58,3,37,1,1,1,1,1),(1227,58,3,38,1,1,1,1,1),(1228,58,3,39,1,1,1,1,1),(1229,58,3,40,1,1,1,1,1),(1230,58,3,41,1,1,1,1,1),(1231,59,3,36,1,1,1,1,1),(1232,59,3,37,1,1,1,1,1),(1233,59,3,38,1,1,1,1,1),(1234,59,3,39,1,1,1,1,1),(1235,59,3,40,1,1,1,1,1),(1236,59,3,41,1,1,1,1,1),(1237,57,4,42,1,1,1,1,1),(1238,57,4,43,1,1,1,1,1),(1239,57,4,44,1,1,1,1,1),(1240,57,4,45,1,1,1,1,1),(1241,57,4,46,1,1,1,1,1),(1242,57,4,47,1,1,1,1,1),(1243,57,4,48,1,1,1,1,1),(1244,57,4,49,1,1,1,1,1),(1245,57,4,50,1,1,1,1,1),(1246,57,4,51,1,1,1,1,1),(1247,57,4,52,1,1,1,1,1),(1248,57,4,53,1,1,1,1,1),(1249,57,4,54,1,1,1,1,1),(1250,57,4,55,1,1,1,1,1),(1251,57,4,56,1,1,1,1,1),(1252,58,4,42,1,1,1,1,1),(1253,58,4,43,1,1,1,1,1),(1254,58,4,44,1,1,1,1,1),(1255,58,4,45,1,1,1,1,1),(1256,58,4,46,1,1,1,1,1),(1257,58,4,47,1,1,1,1,1),(1258,58,4,48,1,1,1,1,1),(1259,58,4,49,1,1,1,1,1),(1260,58,4,50,1,1,1,1,1),(1261,58,4,51,1,1,1,1,1),(1262,58,4,52,1,1,1,1,1),(1263,58,4,53,1,1,1,1,1),(1264,58,4,54,1,1,1,1,1),(1265,58,4,55,1,1,1,1,1),(1266,58,4,56,1,1,1,1,1),(1267,59,4,42,1,1,1,1,1),(1268,59,4,43,1,1,1,1,1),(1269,59,4,44,1,1,1,1,1),(1270,59,4,45,1,1,1,1,1),(1271,59,4,46,1,1,1,1,1),(1272,59,4,47,1,1,1,1,1),(1273,59,4,48,1,1,1,1,1),(1274,59,4,49,1,1,1,1,1),(1275,59,4,50,1,1,1,1,1),(1276,59,4,51,1,1,1,1,1),(1277,59,4,52,1,1,1,1,1),(1278,59,4,53,1,1,1,1,1),(1279,59,4,54,1,1,1,1,1),(1280,59,4,55,1,1,1,1,1),(1281,59,4,56,1,1,1,1,1),(1282,57,5,66,1,1,1,1,1),(1283,57,5,67,1,1,1,1,1),(1284,57,5,68,1,1,1,1,1),(1285,57,5,69,1,1,1,1,1),(1286,57,5,70,1,1,1,1,1),(1287,57,5,71,1,1,1,1,1),(1288,57,5,72,1,1,1,1,1),(1289,57,5,73,1,1,1,1,1),(1290,57,5,74,1,1,1,1,1),(1291,57,5,75,1,1,1,1,1),(1292,57,5,76,1,1,1,1,1),(1293,57,5,77,1,1,1,1,1),(1294,57,5,78,1,1,1,1,1),(1295,57,5,79,1,1,1,1,1),(1296,57,5,80,1,1,1,1,1),(1297,57,5,81,1,1,1,1,1),(1298,57,5,82,1,1,1,1,1),(1299,57,5,83,1,1,1,1,1),(1300,57,5,84,1,1,1,1,1),(1301,57,5,85,1,1,1,1,1),(1302,57,5,86,1,1,1,1,1),(1303,57,5,87,1,1,1,1,1),(1304,57,5,88,1,1,1,1,1),(1305,57,5,89,1,1,1,1,1),(1306,57,5,90,1,1,1,1,1),(1307,57,5,91,1,1,1,1,1),(1308,57,5,92,1,1,1,1,1),(1309,57,5,93,1,1,1,1,1),(1310,57,5,94,1,1,1,1,1),(1311,57,5,95,1,1,1,1,1),(1312,57,5,96,1,1,1,1,1),(1313,57,5,97,1,1,1,1,1),(1314,57,5,98,1,1,1,1,1),(1315,58,5,66,1,1,1,1,1),(1316,58,5,67,1,1,1,1,1),(1317,58,5,68,1,1,1,1,1),(1318,58,5,69,1,1,1,1,1),(1319,58,5,70,1,1,1,1,1),(1320,58,5,71,1,1,1,1,1),(1321,58,5,72,1,1,1,1,1),(1322,58,5,73,1,1,1,1,1),(1323,58,5,74,1,1,1,1,1),(1324,58,5,75,1,1,1,1,1),(1325,58,5,76,1,1,1,1,1),(1326,58,5,77,1,1,1,1,1),(1327,58,5,78,1,1,1,1,1),(1328,58,5,79,1,1,1,1,1),(1329,58,5,80,1,1,1,1,1),(1330,58,5,81,1,1,1,1,1),(1331,58,5,82,1,1,1,1,1),(1332,58,5,83,1,1,1,1,1),(1333,58,5,84,1,1,1,1,1),(1334,58,5,85,1,1,1,1,1),(1335,58,5,86,1,1,1,1,1),(1336,58,5,87,1,1,1,1,1),(1337,58,5,88,1,1,1,1,1),(1338,58,5,89,1,1,1,1,1),(1339,58,5,90,1,1,1,1,1),(1340,58,5,91,1,1,1,1,1),(1341,58,5,92,1,1,1,1,1),(1342,58,5,93,1,1,1,1,1),(1343,58,5,94,1,1,1,1,1),(1344,58,5,95,1,1,1,1,1),(1345,58,5,96,1,1,1,1,1),(1346,58,5,97,1,1,1,1,1),(1347,58,5,98,1,1,1,1,1),(1348,59,5,66,1,1,1,1,1),(1349,59,5,67,1,1,1,1,1),(1350,59,5,68,1,1,1,1,1),(1351,59,5,69,1,1,1,1,1),(1352,59,5,70,1,1,1,1,1),(1353,59,5,71,1,1,1,1,1),(1354,59,5,72,1,1,1,1,1),(1355,59,5,73,1,1,1,1,1),(1356,59,5,74,1,1,1,1,1),(1357,59,5,75,1,1,1,1,1),(1358,59,5,76,1,1,1,1,1),(1359,59,5,77,1,1,1,1,1),(1360,59,5,78,1,1,1,1,1),(1361,59,5,79,1,1,1,1,1),(1362,59,5,80,1,1,1,1,1),(1363,59,5,81,1,1,1,1,1),(1364,59,5,82,1,1,1,1,1),(1365,59,5,83,1,1,1,1,1),(1366,59,5,84,1,1,1,1,1),(1367,59,5,85,1,1,1,1,1),(1368,59,5,86,1,1,1,1,1),(1369,59,5,87,1,1,1,1,1),(1370,59,5,88,1,1,1,1,1),(1371,59,5,89,1,1,1,1,1),(1372,59,5,90,1,1,1,1,1),(1373,59,5,91,1,1,1,1,1),(1374,59,5,92,1,1,1,1,1),(1375,59,5,93,1,1,1,1,1),(1376,59,5,94,1,1,1,1,1),(1377,59,5,95,1,1,1,1,1),(1378,59,5,96,1,1,1,1,1),(1379,59,5,97,1,1,1,1,1),(1380,59,5,98,1,1,1,1,1),(1381,10,1,5,1,1,1,1,1),(1382,10,1,6,1,1,1,1,1),(1383,10,2,7,1,1,1,1,1),(1384,10,2,8,1,1,1,1,1),(1385,10,2,9,1,1,1,1,1),(1386,10,2,10,1,1,1,1,1),(1387,10,2,11,1,1,1,1,1),(1388,10,2,12,1,1,1,1,1),(1389,10,2,13,1,1,1,1,1),(1390,10,2,14,1,1,1,1,1),(1391,10,2,15,1,1,1,1,1),(1392,10,2,16,1,1,1,1,1),(1393,10,2,17,1,1,1,1,1),(1394,10,2,18,1,1,1,1,1),(1395,10,2,19,1,1,1,1,1),(1396,10,2,20,1,1,1,1,1),(1397,10,2,21,1,1,1,1,1),(1398,10,2,22,1,1,1,1,1),(1399,10,2,23,1,1,1,1,1),(1400,10,2,24,1,1,1,1,1),(1401,10,2,25,1,1,1,1,1),(1402,10,3,26,1,1,1,1,1),(1403,10,3,27,1,1,1,1,1),(1404,10,3,28,1,1,1,1,1),(1405,10,3,29,1,1,1,1,1),(1406,10,3,30,1,1,1,1,1),(1407,10,3,31,1,1,1,1,1),(1408,10,3,32,1,1,1,1,1),(1409,10,3,33,1,1,1,1,1),(1410,10,3,34,1,1,1,1,1),(1411,10,3,35,1,1,1,1,1),(1412,10,4,42,1,1,1,1,1),(1413,10,4,43,1,1,1,1,1),(1414,10,4,44,1,1,1,1,1),(1415,10,4,45,1,1,1,1,1),(1416,10,4,46,1,1,1,1,1),(1417,10,4,47,1,1,1,1,1),(1418,10,4,48,1,1,1,1,1),(1419,10,4,49,1,1,1,1,1),(1420,10,4,57,1,1,1,1,1),(1421,10,4,58,1,1,1,1,1),(1422,10,4,59,1,1,1,1,1),(1423,10,4,60,1,1,1,1,1),(1424,10,4,61,1,1,1,1,1),(1425,10,4,62,1,1,1,1,1),(1426,10,4,63,1,1,1,1,1),(1427,10,4,64,1,1,1,1,1),(1428,10,4,65,1,1,1,1,1),(1429,10,5,66,1,1,1,1,1),(1430,10,5,67,1,1,1,1,1),(1431,10,5,68,1,1,1,1,1),(1432,10,5,69,1,1,1,1,1),(1433,10,5,70,1,1,1,1,1),(1434,10,5,71,1,1,1,1,1),(1435,10,5,72,1,1,1,1,1),(1436,10,5,73,1,1,1,1,1),(1437,10,5,74,1,1,1,1,1),(1438,10,5,75,1,1,1,1,1),(1439,10,5,76,1,1,1,1,1),(1440,10,5,77,1,1,1,1,1),(1441,10,5,78,1,1,1,1,1),(1442,10,5,79,1,1,1,1,1),(1443,10,5,80,1,1,1,1,1),(1444,10,5,81,1,1,1,1,1),(1445,10,5,82,1,1,1,1,1),(1446,10,5,83,1,1,1,1,1),(1447,10,5,84,1,1,1,1,1),(1448,10,5,85,1,1,1,1,1),(1449,10,5,86,1,1,1,1,1),(1450,10,5,87,1,1,1,1,1),(1451,10,5,88,1,1,1,1,1),(1452,10,5,89,1,1,1,1,1),(1453,10,5,90,1,1,1,1,1),(1454,10,5,91,1,1,1,1,1),(1455,10,5,92,1,1,1,1,1),(1456,10,5,93,1,1,1,1,1),(1457,10,5,94,1,1,1,1,1),(1458,10,5,95,1,1,1,1,1),(1459,10,5,96,1,1,1,1,1),(1460,10,5,97,1,1,1,1,1),(1461,10,5,98,1,1,1,1,1),(1462,60,6,99,1,1,1,1,1),(1463,60,6,100,1,1,1,1,1),(1464,60,6,101,1,1,1,1,1),(1465,60,6,102,1,1,1,1,1),(1466,60,6,103,1,1,1,1,1),(1467,60,6,104,1,1,1,1,1),(1468,61,6,99,1,1,1,1,1),(1469,61,6,100,1,1,1,1,1),(1470,61,6,101,1,1,1,1,1),(1471,61,6,102,1,1,1,1,1),(1472,61,6,103,1,1,1,1,1),(1473,61,6,104,1,1,1,1,1),(1474,62,6,99,1,1,1,1,1),(1475,62,6,100,1,1,1,1,1),(1476,62,6,101,1,1,1,1,1),(1477,62,6,102,1,1,1,1,1),(1478,62,6,103,1,1,1,1,1),(1479,62,6,104,1,1,1,1,1),(1480,63,6,99,1,1,1,1,1),(1481,63,6,100,1,1,1,1,1),(1482,63,6,101,1,1,1,1,1),(1483,63,6,102,1,1,1,1,1),(1484,63,6,103,1,1,1,1,1),(1485,63,6,104,1,1,1,1,1),(1486,64,6,99,1,1,1,1,1),(1487,64,6,100,1,1,1,1,1),(1488,64,6,101,1,1,1,1,1),(1489,64,6,102,1,1,1,1,1),(1490,64,6,103,1,1,1,1,1),(1491,64,6,104,1,1,1,1,1),(1492,65,6,99,1,1,1,1,1),(1493,65,6,100,1,1,1,1,1),(1494,65,6,101,1,1,1,1,1),(1495,65,6,102,1,1,1,1,1),(1496,65,6,103,1,1,1,1,1),(1497,65,6,104,1,1,1,1,1),(1498,66,6,99,1,1,1,1,1),(1499,66,6,100,1,1,1,1,1),(1500,66,6,101,1,1,1,1,1),(1501,66,6,102,1,1,1,1,1),(1502,66,6,103,1,1,1,1,1),(1503,66,6,104,1,1,1,1,1),(1504,67,6,99,1,1,1,1,1),(1505,67,6,100,1,1,1,1,1),(1506,67,6,101,1,1,1,1,1),(1507,67,6,102,1,1,1,1,1),(1508,67,6,103,1,1,1,1,1),(1509,67,6,104,1,1,1,1,1),(1510,68,6,99,1,1,1,1,1),(1511,68,6,100,1,1,1,1,1),(1512,68,6,101,1,1,1,1,1),(1513,68,6,102,1,1,1,1,1),(1514,68,6,103,1,1,1,1,1),(1515,68,6,104,1,1,1,1,1),(1516,69,6,99,1,1,1,1,1),(1517,69,6,100,1,1,1,1,1),(1518,69,6,101,1,1,1,1,1),(1519,69,6,102,1,1,1,1,1),(1520,69,6,103,1,1,1,1,1),(1521,69,6,104,1,1,1,1,1),(1522,70,6,99,1,1,1,1,1),(1523,70,6,100,1,1,1,1,1),(1524,70,6,101,1,1,1,1,1),(1525,70,6,102,1,1,1,1,1),(1526,70,6,103,1,1,1,1,1),(1527,70,6,104,1,1,1,1,1),(1528,71,6,99,1,1,1,1,1),(1529,71,6,100,1,1,1,1,1),(1530,71,6,101,1,1,1,1,1),(1531,71,6,102,1,1,1,1,1),(1532,71,6,103,1,1,1,1,1),(1533,71,6,104,1,1,1,1,1),(1534,72,6,99,1,1,1,1,1),(1535,72,6,100,1,1,1,1,1),(1536,72,6,101,1,1,1,1,1),(1537,72,6,102,1,1,1,1,1),(1538,72,6,103,1,1,1,1,1),(1539,72,6,104,1,1,1,1,1),(1540,60,7,108,1,1,1,1,1),(1541,60,7,109,1,1,1,1,1),(1542,60,7,110,1,1,1,1,1),(1543,60,7,111,1,1,1,1,1),(1544,60,7,112,1,1,1,1,1),(1545,60,7,113,1,1,1,1,1),(1546,60,7,114,1,1,1,1,1),(1547,60,7,115,1,1,1,1,1),(1548,60,7,116,1,1,1,1,1),(1549,60,7,117,1,1,1,1,1),(1550,60,7,118,1,1,1,1,1),(1551,60,7,119,1,1,1,1,1),(1552,60,7,120,1,1,1,1,1),(1553,60,7,121,1,1,1,1,1),(1554,61,7,108,1,1,1,1,1),(1555,61,7,109,1,1,1,1,1),(1556,61,7,110,1,1,1,1,1),(1557,61,7,111,1,1,1,1,1),(1558,61,7,112,1,1,1,1,1),(1559,61,7,113,1,1,1,1,1),(1560,61,7,114,1,1,1,1,1),(1561,61,7,115,1,1,1,1,1),(1562,61,7,116,1,1,1,1,1),(1563,61,7,117,1,1,1,1,1),(1564,61,7,118,1,1,1,1,1),(1565,61,7,119,1,1,1,1,1),(1566,61,7,120,1,1,1,1,1),(1567,61,7,121,1,1,1,1,1),(1568,62,7,108,1,1,1,1,1),(1569,62,7,109,1,1,1,1,1),(1570,62,7,110,1,1,1,1,1),(1571,62,7,111,1,1,1,1,1),(1572,62,7,112,1,1,1,1,1),(1573,62,7,113,1,1,1,1,1),(1574,62,7,114,1,1,1,1,1),(1575,62,7,115,1,1,1,1,1),(1576,62,7,116,1,1,1,1,1),(1577,62,7,117,1,1,1,1,1),(1578,62,7,118,1,1,1,1,1),(1579,62,7,119,1,1,1,1,1),(1580,62,7,120,1,1,1,1,1),(1581,62,7,121,1,1,1,1,1),(1582,63,7,108,1,1,1,1,1),(1583,63,7,109,1,1,1,1,1),(1584,63,7,110,1,1,1,1,1),(1585,63,7,111,1,1,1,1,1),(1586,63,7,112,1,1,1,1,1),(1587,63,7,113,1,1,1,1,1),(1588,63,7,114,1,1,1,1,1),(1589,63,7,115,1,1,1,1,1),(1590,63,7,116,1,1,1,1,1),(1591,63,7,117,1,1,1,1,1),(1592,63,7,118,1,1,1,1,1),(1593,63,7,119,1,1,1,1,1),(1594,63,7,120,1,1,1,1,1),(1595,63,7,121,1,1,1,1,1),(1596,64,7,108,1,1,1,1,1),(1597,64,7,109,1,1,1,1,1),(1598,64,7,110,1,1,1,1,1),(1599,64,7,111,1,1,1,1,1),(1600,64,7,112,1,1,1,1,1),(1601,64,7,113,1,1,1,1,1),(1602,64,7,114,1,1,1,1,1),(1603,64,7,115,1,1,1,1,1),(1604,64,7,116,1,1,1,1,1),(1605,64,7,117,1,1,1,1,1),(1606,64,7,118,1,1,1,1,1),(1607,64,7,119,1,1,1,1,1),(1608,64,7,120,1,1,1,1,1),(1609,64,7,121,1,1,1,1,1),(1610,65,7,108,1,1,1,1,1),(1611,65,7,109,1,1,1,1,1),(1612,65,7,110,1,1,1,1,1),(1613,65,7,111,1,1,1,1,1),(1614,65,7,112,1,1,1,1,1),(1615,65,7,113,1,1,1,1,1),(1616,65,7,114,1,1,1,1,1),(1617,65,7,115,1,1,1,1,1),(1618,65,7,116,1,1,1,1,1),(1619,65,7,117,1,1,1,1,1),(1620,65,7,118,1,1,1,1,1),(1621,65,7,119,1,1,1,1,1),(1622,65,7,120,1,1,1,1,1),(1623,65,7,121,1,1,1,1,1),(1624,66,7,108,1,1,1,1,1),(1625,66,7,109,1,1,1,1,1),(1626,66,7,110,1,1,1,1,1),(1627,66,7,111,1,1,1,1,1),(1628,66,7,112,1,1,1,1,1),(1629,66,7,113,1,1,1,1,1),(1630,66,7,114,1,1,1,1,1),(1631,66,7,115,1,1,1,1,1),(1632,66,7,116,1,1,1,1,1),(1633,66,7,117,1,1,1,1,1),(1634,66,7,118,1,1,1,1,1),(1635,66,7,119,1,1,1,1,1),(1636,66,7,120,1,1,1,1,1),(1637,66,7,121,1,1,1,1,1),(1638,67,7,108,1,1,1,1,1),(1639,67,7,109,1,1,1,1,1),(1640,67,7,110,1,1,1,1,1),(1641,67,7,111,1,1,1,1,1),(1642,67,7,112,1,1,1,1,1),(1643,67,7,113,1,1,1,1,1),(1644,67,7,114,1,1,1,1,1),(1645,67,7,115,1,1,1,1,1),(1646,67,7,116,1,1,1,1,1),(1647,67,7,117,1,1,1,1,1),(1648,67,7,118,1,1,1,1,1),(1649,67,7,119,1,1,1,1,1),(1650,67,7,120,1,1,1,1,1),(1651,67,7,121,1,1,1,1,1),(1652,68,7,108,1,1,1,1,1),(1653,68,7,109,1,1,1,1,1),(1654,68,7,110,1,1,1,1,1),(1655,68,7,111,1,1,1,1,1),(1656,68,7,112,1,1,1,1,1),(1657,68,7,113,1,1,1,1,1),(1658,68,7,114,1,1,1,1,1),(1659,68,7,115,1,1,1,1,1),(1660,68,7,116,1,1,1,1,1),(1661,68,7,117,1,1,1,1,1),(1662,68,7,118,1,1,1,1,1),(1663,68,7,119,1,1,1,1,1),(1664,68,7,120,1,1,1,1,1),(1665,68,7,121,1,1,1,1,1),(1666,69,7,108,1,1,1,1,1),(1667,69,7,109,1,1,1,1,1),(1668,69,7,110,1,1,1,1,1),(1669,69,7,111,1,1,1,1,1),(1670,69,7,112,1,1,1,1,1),(1671,69,7,113,1,1,1,1,1),(1672,69,7,114,1,1,1,1,1),(1673,69,7,115,1,1,1,1,1),(1674,69,7,116,1,1,1,1,1),(1675,69,7,117,1,1,1,1,1),(1676,69,7,118,1,1,1,1,1),(1677,69,7,119,1,1,1,1,1),(1678,69,7,120,1,1,1,1,1),(1679,69,7,121,1,1,1,1,1),(1680,70,7,108,1,1,1,1,1),(1681,70,7,109,1,1,1,1,1),(1682,70,7,110,1,1,1,1,1),(1683,70,7,111,1,1,1,1,1),(1684,70,7,112,1,1,1,1,1),(1685,70,7,113,1,1,1,1,1),(1686,70,7,114,1,1,1,1,1),(1687,70,7,115,1,1,1,1,1),(1688,70,7,116,1,1,1,1,1),(1689,70,7,117,1,1,1,1,1),(1690,70,7,118,1,1,1,1,1),(1691,70,7,119,1,1,1,1,1),(1692,70,7,120,1,1,1,1,1),(1693,70,7,121,1,1,1,1,1),(1694,71,7,108,1,1,1,1,1),(1695,71,7,109,1,1,1,1,1),(1696,71,7,110,1,1,1,1,1),(1697,71,7,111,1,1,1,1,1),(1698,71,7,112,1,1,1,1,1),(1699,71,7,113,1,1,1,1,1),(1700,71,7,114,1,1,1,1,1),(1701,71,7,115,1,1,1,1,1),(1702,71,7,116,1,1,1,1,1),(1703,71,7,117,1,1,1,1,1),(1704,71,7,118,1,1,1,1,1),(1705,71,7,119,1,1,1,1,1),(1706,71,7,120,1,1,1,1,1),(1707,71,7,121,1,1,1,1,1),(1708,72,7,108,1,1,1,1,1),(1709,72,7,109,1,1,1,1,1),(1710,72,7,110,1,1,1,1,1),(1711,72,7,111,1,1,1,1,1),(1712,72,7,112,1,1,1,1,1),(1713,72,7,113,1,1,1,1,1),(1714,72,7,114,1,1,1,1,1),(1715,72,7,115,1,1,1,1,1),(1716,72,7,116,1,1,1,1,1),(1717,72,7,117,1,1,1,1,1),(1718,72,7,118,1,1,1,1,1),(1719,72,7,119,1,1,1,1,1),(1720,72,7,120,1,1,1,1,1),(1721,72,7,121,1,1,1,1,1),(1722,70,8,124,1,1,1,1,1),(1723,70,8,125,1,1,1,1,1),(1724,70,8,126,1,1,1,1,1),(1725,70,8,127,1,1,1,1,1),(1726,70,8,128,1,1,1,1,1),(1727,70,8,129,1,1,1,1,1),(1728,70,8,130,1,1,1,1,1),(1729,70,9,131,1,1,1,1,1),(1730,70,9,132,1,1,1,1,1),(1731,70,9,133,1,1,1,1,1),(1732,70,9,134,1,1,1,1,1),(1733,70,9,135,1,1,1,1,1),(1734,70,9,136,1,1,1,1,1),(1735,70,9,137,1,1,1,1,1),(1736,70,9,138,1,1,1,1,1),(1737,70,9,139,1,1,1,1,1),(1738,70,9,140,1,1,1,1,1),(1739,70,9,141,1,1,1,1,1),(1740,73,6,105,1,1,1,1,1),(1741,73,6,106,1,1,1,1,1),(1742,73,6,107,1,1,1,1,1),(1743,74,6,105,1,1,1,1,1),(1744,74,6,106,1,1,1,1,1),(1745,74,6,107,1,1,1,1,1),(1746,75,6,105,1,1,1,1,1),(1747,75,6,106,1,1,1,1,1),(1748,75,6,107,1,1,1,1,1),(1749,76,6,105,1,1,1,1,1),(1750,76,6,106,1,1,1,1,1),(1751,76,6,107,1,1,1,1,1),(1752,77,6,106,1,1,1,1,1),(1753,77,6,107,1,1,1,1,1),(1754,77,6,105,1,1,1,1,1),(1755,73,7,108,1,1,1,1,1),(1756,73,7,109,1,1,1,1,1),(1757,73,7,122,1,1,1,1,1),(1758,73,7,123,1,1,1,1,1),(1759,73,7,117,1,1,1,1,1),(1760,73,7,118,1,1,1,1,1),(1761,73,7,119,1,1,1,1,1),(1762,73,7,120,1,1,1,1,1),(1763,73,7,121,1,1,1,1,1),(1764,74,7,108,1,1,1,1,1),(1765,74,7,109,1,1,1,1,1),(1766,74,7,122,1,1,1,1,1),(1767,74,7,123,1,1,1,1,1),(1768,74,7,117,1,1,1,1,1),(1769,74,7,118,1,1,1,1,1),(1770,74,7,119,1,1,1,1,1),(1771,74,7,120,1,1,1,1,1),(1772,74,7,121,1,1,1,1,1),(1773,75,7,108,1,1,1,1,1),(1774,75,7,109,1,1,1,1,1),(1775,75,7,122,1,1,1,1,1),(1776,75,7,123,1,1,1,1,1),(1777,75,7,117,1,1,1,1,1),(1778,75,7,118,1,1,1,1,1),(1779,75,7,119,1,1,1,1,1),(1780,75,7,120,1,1,1,1,1),(1781,75,7,121,1,1,1,1,1),(1782,76,7,108,1,1,1,1,1),(1783,76,7,109,1,1,1,1,1),(1784,76,7,122,1,1,1,1,1),(1785,76,7,123,1,1,1,1,1),(1786,76,7,117,1,1,1,1,1),(1787,76,7,118,1,1,1,1,1),(1788,76,7,119,1,1,1,1,1),(1789,76,7,120,1,1,1,1,1),(1790,76,7,121,1,1,1,1,1),(1791,77,7,108,1,1,1,1,1),(1792,77,7,109,1,1,1,1,1),(1793,77,7,122,1,1,1,1,1),(1794,77,7,123,1,1,1,1,1),(1795,77,7,117,1,1,1,1,1),(1796,77,7,118,1,1,1,1,1),(1797,77,7,119,1,1,1,1,1),(1798,77,7,120,1,1,1,1,1),(1799,77,7,121,1,1,1,1,1),(1800,13,6,103,1,1,1,1,1),(1801,13,6,104,1,1,1,1,1),(1802,13,7,108,1,1,1,1,1),(1803,13,7,109,1,1,1,1,1),(1804,13,7,110,1,1,1,1,1),(1805,13,7,111,1,1,1,1,1),(1806,13,7,112,1,1,1,1,1),(1807,13,7,113,1,1,1,1,1),(1808,13,7,114,1,1,1,1,1),(1809,13,7,115,1,1,1,1,1),(1810,13,7,116,1,1,1,1,1),(1811,13,7,117,1,1,1,1,1),(1812,13,7,118,1,1,1,1,1),(1813,13,7,119,1,1,1,1,1),(1814,13,7,120,1,1,1,1,1),(1815,13,7,121,1,1,1,1,1),(1816,13,10,142,1,1,1,1,1),(1817,13,10,143,1,1,1,1,1),(1818,13,10,144,1,1,1,1,1),(1819,13,10,145,1,1,1,1,1),(1820,13,10,146,1,1,1,1,1),(1821,13,10,147,1,1,1,1,1),(1822,13,10,148,1,1,1,1,1),(1823,13,10,149,1,1,1,1,1),(1824,13,10,150,1,1,1,1,1),(1825,13,10,151,1,1,1,1,1),(1826,13,10,152,1,1,1,1,1),(1827,13,10,153,1,1,1,1,1),(1828,77,11,154,1,1,1,1,1),(1829,77,11,155,1,1,1,1,1),(1830,77,11,156,1,1,1,1,1),(1831,77,11,157,1,1,1,1,1),(1832,78,11,154,1,1,1,1,1),(1833,78,11,155,1,1,1,1,1),(1834,78,11,156,1,1,1,1,1),(1835,78,11,157,1,1,1,1,1),(1836,79,11,154,1,1,1,1,1),(1837,79,11,155,1,1,1,1,1),(1838,79,11,156,1,1,1,1,1),(1839,79,11,157,1,1,1,1,1),(1840,80,11,154,1,1,1,1,1),(1841,80,11,155,1,1,1,1,1),(1842,80,11,156,1,1,1,1,1),(1843,80,11,157,1,1,1,1,1),(1844,81,11,154,1,1,1,1,1),(1845,81,11,155,1,1,1,1,1),(1846,81,11,156,1,1,1,1,1),(1847,81,11,157,1,1,1,1,1),(1848,82,11,154,1,1,1,1,1),(1849,82,11,155,1,1,1,1,1),(1850,82,11,156,1,1,1,1,1),(1851,82,11,157,1,1,1,1,1),(1852,83,11,154,1,1,1,1,1),(1853,83,11,155,1,1,1,1,1),(1854,83,11,156,1,1,1,1,1),(1855,83,11,157,1,1,1,1,1),(1856,84,11,154,1,1,1,1,1),(1857,84,11,155,1,1,1,1,1),(1858,84,11,156,1,1,1,1,1),(1859,84,11,157,1,1,1,1,1),(1860,85,11,154,1,1,1,1,1),(1861,85,11,155,1,1,1,1,1),(1862,85,11,156,1,1,1,1,1),(1863,85,11,157,1,1,1,1,1),(1864,86,11,154,1,1,1,1,1),(1865,86,11,155,1,1,1,1,1),(1866,86,11,156,1,1,1,1,1),(1867,86,11,157,1,1,1,1,1),(1868,87,11,154,1,1,1,1,1),(1869,87,11,155,1,1,1,1,1),(1870,87,11,156,1,1,1,1,1),(1871,87,11,157,1,1,1,1,1),(1872,88,11,154,1,1,1,1,1),(1873,88,11,155,1,1,1,1,1),(1874,88,11,156,1,1,1,1,1),(1875,88,11,157,1,1,1,1,1),(1876,89,11,154,1,1,1,1,1),(1877,89,11,155,1,1,1,1,1),(1878,89,11,156,1,1,1,1,1),(1879,89,11,157,1,1,1,1,1),(1880,90,11,154,1,1,1,1,1),(1881,90,11,155,1,1,1,1,1),(1882,90,11,156,1,1,1,1,1),(1883,90,11,157,1,1,1,1,1),(1884,91,11,154,1,1,1,1,1),(1885,91,11,155,1,1,1,1,1),(1886,91,11,156,1,1,1,1,1),(1887,91,11,157,1,1,1,1,1),(1888,92,11,154,1,1,1,1,1),(1889,92,11,155,1,1,1,1,1),(1890,92,11,156,1,1,1,1,1),(1891,92,11,157,1,1,1,1,1),(1892,93,11,154,1,1,1,1,1),(1893,93,11,155,1,1,1,1,1),(1894,93,11,156,1,1,1,1,1),(1895,93,11,157,1,1,1,1,1),(1896,94,11,154,1,1,1,1,1),(1897,94,11,155,1,1,1,1,1),(1898,94,11,156,1,1,1,1,1),(1899,94,11,157,1,1,1,1,1),(1900,95,11,154,1,1,1,1,1),(1901,95,11,155,1,1,1,1,1),(1902,95,11,156,1,1,1,1,1),(1903,95,11,157,1,1,1,1,1),(1904,16,11,154,1,1,1,1,1),(1905,16,11,155,1,1,1,1,1),(1906,16,11,156,1,1,1,1,1),(1907,16,11,157,1,1,1,1,1),(1908,77,7,158,1,1,1,1,1),(1909,77,7,159,1,1,1,1,1),(1910,77,7,160,1,1,1,1,1),(1911,77,7,161,1,1,1,1,1),(1912,77,7,162,1,1,1,1,1),(1913,77,7,163,1,1,1,1,1),(1914,78,7,158,1,1,1,1,1),(1915,78,7,159,1,1,1,1,1),(1916,78,7,160,1,1,1,1,1),(1917,78,7,164,1,1,1,1,1),(1918,78,7,165,1,1,1,1,1),(1919,78,7,166,1,1,1,1,1),(1920,78,7,167,1,1,1,1,1),(1921,78,7,168,1,1,1,1,1),(1922,78,7,169,1,1,1,1,1),(1923,78,7,170,1,1,1,1,1),(1924,78,7,171,1,1,1,1,1),(1925,78,7,172,1,1,1,1,1),(1926,78,7,173,1,1,1,1,1),(1927,78,7,174,1,1,1,1,1),(1928,78,7,175,1,1,1,1,1),(1929,78,7,176,1,1,1,1,1),(1930,78,7,177,1,1,1,1,1),(1931,78,7,178,1,1,1,1,1),(1932,78,7,179,1,1,1,1,1),(1933,78,7,180,1,1,1,1,1),(1934,78,7,181,1,1,1,1,1),(1935,78,7,182,1,1,1,1,1),(1936,78,7,183,1,1,1,1,1),(1937,78,7,184,1,1,1,1,1),(1938,78,7,185,1,1,1,1,1),(1939,78,7,186,1,1,1,1,1),(1940,78,7,187,1,1,1,1,1),(1941,78,7,188,1,1,1,1,1),(1942,78,7,189,1,1,1,1,1),(1943,78,7,190,1,1,1,1,1),(1944,78,7,191,1,1,1,1,1),(1945,78,7,192,1,1,1,1,1),(1946,78,7,193,1,1,1,1,1),(1947,78,7,194,1,1,1,1,1),(1948,78,7,195,1,1,1,1,1),(1949,78,7,196,1,1,1,1,1),(1950,78,7,161,1,1,1,1,1),(1951,78,7,197,1,1,1,1,1),(1952,78,7,198,1,1,1,1,1),(1953,78,7,162,1,1,1,1,1),(1954,78,7,163,1,1,1,1,1),(1955,79,7,158,1,1,1,1,1),(1956,79,7,199,1,1,1,1,1),(1957,79,7,163,1,1,1,1,1),(1958,79,7,200,1,1,1,1,1),(1959,80,7,158,1,1,1,1,1),(1960,80,7,159,1,1,1,1,1),(1961,80,7,160,1,1,1,1,1),(1962,80,7,164,1,1,1,1,1),(1963,80,7,165,1,1,1,1,1),(1964,80,7,166,1,1,1,1,1),(1965,80,7,167,1,1,1,1,1),(1966,80,7,168,1,1,1,1,1),(1967,80,7,169,1,1,1,1,1),(1968,80,7,170,1,1,1,1,1),(1969,80,7,171,1,1,1,1,1),(1970,80,7,172,1,1,1,1,1),(1971,80,7,173,1,1,1,1,1),(1972,80,7,174,1,1,1,1,1),(1973,80,7,175,1,1,1,1,1),(1974,80,7,176,1,1,1,1,1),(1975,80,7,177,1,1,1,1,1),(1976,80,7,178,1,1,1,1,1),(1977,80,7,179,1,1,1,1,1),(1978,80,7,180,1,1,1,1,1),(1979,80,7,181,1,1,1,1,1),(1980,80,7,182,1,1,1,1,1),(1981,80,7,183,1,1,1,1,1),(1982,80,7,184,1,1,1,1,1),(1983,80,7,185,1,1,1,1,1),(1984,80,7,186,1,1,1,1,1),(1985,80,7,187,1,1,1,1,1),(1986,80,7,188,1,1,1,1,1),(1987,80,7,189,1,1,1,1,1),(1988,80,7,190,1,1,1,1,1),(1989,80,7,191,1,1,1,1,1),(1990,80,7,192,1,1,1,1,1),(1991,80,7,193,1,1,1,1,1),(1992,80,7,194,1,1,1,1,1),(1993,80,7,195,1,1,1,1,1),(1994,80,7,196,1,1,1,1,1),(1995,80,7,161,1,1,1,1,1),(1996,80,7,197,1,1,1,1,1),(1997,80,7,198,1,1,1,1,1),(1998,80,7,162,1,1,1,1,1),(1999,80,7,163,1,1,1,1,1),(2000,81,7,158,1,1,1,1,1),(2001,81,7,201,1,1,1,1,1),(2002,81,7,202,1,1,1,1,1),(2003,81,7,203,1,1,1,1,1),(2004,81,7,204,1,1,1,1,1),(2005,81,7,205,1,1,1,1,1),(2006,81,7,206,1,1,1,1,1),(2007,81,7,207,1,1,1,1,1),(2008,81,7,208,1,1,1,1,1),(2009,81,7,163,1,1,1,1,1),(2010,82,7,158,1,1,1,1,1),(2011,82,7,209,1,1,1,1,1),(2012,82,7,210,1,1,1,1,1),(2013,82,7,211,1,1,1,1,1),(2014,82,7,212,1,1,1,1,1),(2015,82,7,213,1,1,1,1,1),(2016,82,7,214,1,1,1,1,1),(2017,82,7,215,1,1,1,1,1),(2018,82,7,216,1,1,1,1,1),(2019,82,7,217,1,1,1,1,1),(2020,83,12,218,1,1,1,1,1),(2021,83,12,219,1,1,1,1,1),(2022,83,12,220,1,1,1,1,1),(2023,83,12,221,1,1,1,1,1),(2024,83,12,222,1,1,1,1,1),(2025,83,12,223,1,1,1,1,1),(2026,83,12,224,1,1,1,1,1),(2027,83,12,225,1,1,1,1,1),(2028,83,12,226,1,1,1,1,1),(2029,83,12,227,1,1,1,1,1),(2030,83,12,228,1,1,1,1,1),(2031,83,12,229,1,1,1,1,1),(2032,83,12,230,1,1,1,1,1),(2033,83,12,231,1,1,1,1,1),(2034,16,13,232,1,1,1,1,1),(2035,16,13,233,1,1,1,1,1),(2036,16,13,234,1,1,1,1,1),(2037,16,13,235,1,1,1,1,1),(2038,16,13,236,1,1,1,1,1),(2039,16,13,237,1,1,1,1,1),(2040,16,13,238,1,1,1,1,1),(2041,16,13,239,1,1,1,1,1),(2042,16,13,240,1,1,1,1,1),(2043,16,13,241,1,1,1,1,1),(2044,16,13,242,1,1,1,1,1),(2045,16,13,243,1,1,1,1,1),(2046,16,13,244,1,1,1,1,1),(2047,16,13,245,1,1,1,1,1),(2048,16,13,246,1,1,1,1,1),(2049,16,13,247,1,1,1,1,1),(2050,16,13,248,1,1,1,1,1),(2051,16,13,249,1,1,1,1,1),(2052,16,14,250,1,1,1,1,1),(2053,16,14,251,1,1,1,1,1),(2054,16,14,252,1,1,1,1,1),(2055,16,14,253,1,1,1,1,1),(2056,17,15,254,1,1,1,1,1),(2057,17,15,255,1,1,1,1,1),(2058,17,15,256,1,1,1,1,1),(2059,17,15,257,1,1,1,1,1),(2060,17,15,258,1,1,1,1,1),(2061,17,15,259,1,1,1,1,1),(2062,17,15,260,1,1,1,1,1),(2063,17,15,261,1,1,1,1,1),(2064,17,15,262,1,1,1,1,1),(2065,17,15,263,1,1,1,1,1),(2066,17,15,264,1,1,1,1,1),(2067,17,15,265,1,1,1,1,1),(2068,17,15,266,1,1,1,1,1),(2069,17,15,267,1,1,1,1,1),(2070,17,15,268,1,1,1,1,1),(2071,17,15,269,1,1,1,1,1),(2072,18,15,254,1,1,1,1,1),(2073,18,15,255,1,1,1,1,1),(2074,18,15,256,1,1,1,1,1),(2075,18,15,257,1,1,1,1,1),(2076,18,15,258,1,1,1,1,1),(2077,18,15,259,1,1,1,1,1),(2078,18,15,260,1,1,1,1,1),(2079,18,15,261,1,1,1,1,1),(2080,18,15,262,1,1,1,1,1),(2081,18,15,263,1,1,1,1,1),(2082,18,15,264,1,1,1,1,1),(2083,18,15,265,1,1,1,1,1),(2084,18,15,266,1,1,1,1,1),(2085,18,15,267,1,1,1,1,1),(2086,18,15,268,1,1,1,1,1),(2087,18,15,269,1,1,1,1,1),(2088,19,15,254,1,1,1,1,1),(2089,19,15,255,1,1,1,1,1),(2090,19,15,256,1,1,1,1,1),(2091,19,15,257,1,1,1,1,1),(2092,19,15,258,1,1,1,1,1),(2093,19,15,259,1,1,1,1,1),(2094,19,15,260,1,1,1,1,1),(2095,19,15,261,1,1,1,1,1),(2096,19,15,262,1,1,1,1,1),(2097,19,15,263,1,1,1,1,1),(2098,19,15,264,1,1,1,1,1),(2099,19,15,265,1,1,1,1,1),(2100,19,15,266,1,1,1,1,1),(2101,19,15,267,1,1,1,1,1),(2102,19,15,268,1,1,1,1,1),(2103,19,15,269,1,1,1,1,1),(2104,20,15,254,1,1,1,1,1),(2105,20,15,255,1,1,1,1,1),(2106,20,15,256,1,1,1,1,1),(2107,20,15,257,1,1,1,1,1),(2108,20,15,258,1,1,1,1,1),(2109,20,15,259,1,1,1,1,1),(2110,20,15,260,1,1,1,1,1),(2111,20,15,261,1,1,1,1,1),(2112,20,15,262,1,1,1,1,1),(2113,20,15,263,1,1,1,1,1),(2114,20,15,264,1,1,1,1,1),(2115,20,15,265,1,1,1,1,1),(2116,20,15,266,1,1,1,1,1),(2117,20,15,267,1,1,1,1,1),(2118,20,15,268,1,1,1,1,1),(2119,20,15,269,1,1,1,1,1),(2120,21,15,254,1,1,1,1,1),(2121,21,15,255,1,1,1,1,1),(2122,21,15,256,1,1,1,1,1),(2123,21,15,257,1,1,1,1,1),(2124,21,15,258,1,1,1,1,1),(2125,21,15,259,1,1,1,1,1),(2126,21,15,260,1,1,1,1,1),(2127,21,15,261,1,1,1,1,1),(2128,21,15,262,1,1,1,1,1),(2129,21,15,263,1,1,1,1,1),(2130,21,15,264,1,1,1,1,1),(2131,21,15,265,1,1,1,1,1),(2132,21,15,266,1,1,1,1,1),(2133,21,15,267,1,1,1,1,1),(2134,21,15,268,1,1,1,1,1),(2135,21,15,269,1,1,1,1,1),(2136,22,15,254,1,1,1,1,1),(2137,22,15,255,1,1,1,1,1),(2138,22,15,256,1,1,1,1,1),(2139,22,15,257,1,1,1,1,1),(2140,22,15,258,1,1,1,1,1),(2141,22,15,259,1,1,1,1,1),(2142,22,15,260,1,1,1,1,1),(2143,22,15,261,1,1,1,1,1),(2144,22,15,262,1,1,1,1,1),(2145,22,15,263,1,1,1,1,1),(2146,22,15,264,1,1,1,1,1),(2147,22,15,265,1,1,1,1,1),(2148,22,15,266,1,1,1,1,1),(2149,22,15,267,1,1,1,1,1),(2150,22,15,268,1,1,1,1,1),(2151,22,15,269,1,1,1,1,1),(2152,23,15,254,1,1,1,1,1),(2153,23,15,255,1,1,1,1,1),(2154,23,15,256,1,1,1,1,1),(2155,23,15,257,1,1,1,1,1),(2156,23,15,258,1,1,1,1,1),(2157,23,15,259,1,1,1,1,1),(2158,23,15,260,1,1,1,1,1),(2159,23,15,261,1,1,1,1,1),(2160,23,15,262,1,1,1,1,1),(2161,23,15,263,1,1,1,1,1),(2162,23,15,264,1,1,1,1,1),(2163,23,15,265,1,1,1,1,1),(2164,23,15,266,1,1,1,1,1),(2165,23,15,267,1,1,1,1,1),(2166,23,15,268,1,1,1,1,1),(2167,23,15,269,1,1,1,1,1),(2168,24,15,254,1,1,1,1,1),(2169,24,15,255,1,1,1,1,1),(2170,24,15,256,1,1,1,1,1),(2171,24,15,257,1,1,1,1,1),(2172,24,15,258,1,1,1,1,1),(2173,24,15,259,1,1,1,1,1),(2174,24,15,260,1,1,1,1,1),(2175,24,15,261,1,1,1,1,1),(2176,24,15,262,1,1,1,1,1),(2177,24,15,263,1,1,1,1,1),(2178,24,15,264,1,1,1,1,1),(2179,24,15,265,1,1,1,1,1),(2180,24,15,266,1,1,1,1,1),(2181,24,15,267,1,1,1,1,1),(2182,24,15,268,1,1,1,1,1),(2183,24,15,269,1,1,1,1,1),(2184,25,15,254,1,1,1,1,1),(2185,25,15,255,1,1,1,1,1),(2186,25,15,256,1,1,1,1,1),(2187,25,15,257,1,1,1,1,1),(2188,25,15,258,1,1,1,1,1),(2189,25,15,259,1,1,1,1,1),(2190,25,15,260,1,1,1,1,1),(2191,25,15,261,1,1,1,1,1),(2192,25,15,262,1,1,1,1,1),(2193,25,15,263,1,1,1,1,1),(2194,25,15,264,1,1,1,1,1),(2195,25,15,265,1,1,1,1,1),(2196,25,15,266,1,1,1,1,1),(2197,25,15,267,1,1,1,1,1),(2198,25,15,268,1,1,1,1,1),(2199,25,15,269,1,1,1,1,1),(2200,26,15,254,1,1,1,1,1),(2201,26,15,255,1,1,1,1,1),(2202,26,15,256,1,1,1,1,1),(2203,26,15,257,1,1,1,1,1),(2204,26,15,258,1,1,1,1,1),(2205,26,15,259,1,1,1,1,1),(2206,26,15,260,1,1,1,1,1),(2207,26,15,261,1,1,1,1,1),(2208,26,15,262,1,1,1,1,1),(2209,26,15,263,1,1,1,1,1),(2210,26,15,264,1,1,1,1,1),(2211,26,15,265,1,1,1,1,1),(2212,26,15,266,1,1,1,1,1),(2213,26,15,267,1,1,1,1,1),(2214,26,15,268,1,1,1,1,1),(2215,26,15,269,1,1,1,1,1),(2216,17,16,270,1,1,1,1,1),(2217,17,16,271,1,1,1,1,1),(2218,17,16,272,1,1,1,1,1),(2219,17,16,273,1,1,1,1,1),(2220,17,16,274,1,1,1,1,1),(2221,17,16,275,1,1,1,1,1),(2222,17,16,276,1,1,1,1,1),(2223,17,16,277,1,1,1,1,1),(2224,17,16,278,1,1,1,1,1),(2225,17,16,279,1,1,1,1,1),(2226,17,16,280,1,1,1,1,1),(2227,17,16,281,1,1,1,1,1),(2228,17,16,282,1,1,1,1,1),(2229,18,16,270,1,1,1,1,1),(2230,18,16,271,1,1,1,1,1),(2231,18,16,272,1,1,1,1,1),(2232,18,16,273,1,1,1,1,1),(2233,18,16,274,1,1,1,1,1),(2234,18,16,275,1,1,1,1,1),(2235,18,16,276,1,1,1,1,1),(2236,18,16,277,1,1,1,1,1),(2237,18,16,278,1,1,1,1,1),(2238,18,16,279,1,1,1,1,1),(2239,18,16,280,1,1,1,1,1),(2240,18,16,281,1,1,1,1,1),(2241,18,16,282,1,1,1,1,1),(2242,19,16,270,1,1,1,1,1),(2243,19,16,271,1,1,1,1,1),(2244,19,16,272,1,1,1,1,1),(2245,19,16,273,1,1,1,1,1),(2246,19,16,274,1,1,1,1,1),(2247,19,16,275,1,1,1,1,1),(2248,19,16,276,1,1,1,1,1),(2249,19,16,277,1,1,1,1,1),(2250,19,16,278,1,1,1,1,1),(2251,19,16,279,1,1,1,1,1),(2252,19,16,280,1,1,1,1,1),(2253,19,16,281,1,1,1,1,1),(2254,19,16,282,1,1,1,1,1),(2255,20,16,270,1,1,1,1,1),(2256,20,16,271,1,1,1,1,1),(2257,20,16,272,1,1,1,1,1),(2258,20,16,273,1,1,1,1,1),(2259,20,16,274,1,1,1,1,1),(2260,20,16,275,1,1,1,1,1),(2261,20,16,276,1,1,1,1,1),(2262,20,16,277,1,1,1,1,1),(2263,20,16,278,1,1,1,1,1),(2264,20,16,279,1,1,1,1,1),(2265,20,16,280,1,1,1,1,1),(2266,20,16,281,1,1,1,1,1),(2267,20,16,282,1,1,1,1,1),(2268,21,16,270,1,1,1,1,1),(2269,21,16,271,1,1,1,1,1),(2270,21,16,272,1,1,1,1,1),(2271,21,16,273,1,1,1,1,1),(2272,21,16,274,1,1,1,1,1),(2273,21,16,275,1,1,1,1,1),(2274,21,16,276,1,1,1,1,1),(2275,21,16,277,1,1,1,1,1),(2276,21,16,278,1,1,1,1,1),(2277,21,16,279,1,1,1,1,1),(2278,21,16,280,1,1,1,1,1),(2279,21,16,281,1,1,1,1,1),(2280,21,16,282,1,1,1,1,1),(2281,22,16,270,1,1,1,1,1),(2282,22,16,271,1,1,1,1,1),(2283,22,16,272,1,1,1,1,1),(2284,22,16,273,1,1,1,1,1),(2285,22,16,274,1,1,1,1,1),(2286,22,16,275,1,1,1,1,1),(2287,22,16,276,1,1,1,1,1),(2288,22,16,277,1,1,1,1,1),(2289,22,16,278,1,1,1,1,1),(2290,22,16,279,1,1,1,1,1),(2291,22,16,280,1,1,1,1,1),(2292,22,16,281,1,1,1,1,1),(2293,22,16,282,1,1,1,1,1),(2294,23,16,270,1,1,1,1,1),(2295,23,16,271,1,1,1,1,1),(2296,23,16,272,1,1,1,1,1),(2297,23,16,273,1,1,1,1,1),(2298,23,16,274,1,1,1,1,1),(2299,23,16,275,1,1,1,1,1),(2300,23,16,276,1,1,1,1,1),(2301,23,16,277,1,1,1,1,1),(2302,23,16,278,1,1,1,1,1),(2303,23,16,279,1,1,1,1,1),(2304,23,16,280,1,1,1,1,1),(2305,23,16,281,1,1,1,1,1),(2306,23,16,282,1,1,1,1,1),(2307,24,16,270,1,1,1,1,1),(2308,24,16,271,1,1,1,1,1),(2309,24,16,272,1,1,1,1,1),(2310,24,16,273,1,1,1,1,1),(2311,24,16,274,1,1,1,1,1),(2312,24,16,275,1,1,1,1,1),(2313,24,16,276,1,1,1,1,1),(2314,24,16,277,1,1,1,1,1),(2315,24,16,278,1,1,1,1,1),(2316,24,16,279,1,1,1,1,1),(2317,24,16,280,1,1,1,1,1),(2318,24,16,281,1,1,1,1,1),(2319,24,16,282,1,1,1,1,1),(2320,25,16,270,1,1,1,1,1),(2321,25,16,271,1,1,1,1,1),(2322,25,16,272,1,1,1,1,1),(2323,25,16,273,1,1,1,1,1),(2324,25,16,274,1,1,1,1,1),(2325,25,16,275,1,1,1,1,1),(2326,25,16,276,1,1,1,1,1),(2327,25,16,277,1,1,1,1,1),(2328,25,16,278,1,1,1,1,1),(2329,25,16,279,1,1,1,1,1),(2330,25,16,280,1,1,1,1,1),(2331,25,16,281,1,1,1,1,1),(2332,25,16,282,1,1,1,1,1),(2333,26,16,270,1,1,1,1,1),(2334,26,16,271,1,1,1,1,1),(2335,26,16,272,1,1,1,1,1),(2336,26,16,273,1,1,1,1,1),(2337,26,16,274,1,1,1,1,1),(2338,26,16,275,1,1,1,1,1),(2339,26,16,276,1,1,1,1,1),(2340,26,16,277,1,1,1,1,1),(2341,26,16,278,1,1,1,1,1),(2342,26,16,279,1,1,1,1,1),(2343,26,16,280,1,1,1,1,1),(2344,26,16,281,1,1,1,1,1),(2345,26,16,282,1,1,1,1,1),(2346,18,17,283,1,1,1,1,1),(2347,18,17,284,1,1,1,1,1),(2348,18,17,285,1,1,1,1,1),(2349,18,17,286,1,1,1,1,1),(2350,18,17,287,1,1,1,1,1),(2351,18,17,288,1,1,1,1,1),(2352,18,17,289,1,1,1,1,1),(2353,18,17,290,1,1,1,1,1),(2354,18,17,291,1,1,1,1,1),(2355,18,17,67,1,1,1,1,1),(2356,18,17,292,1,1,1,1,1),(2357,18,17,71,1,1,1,1,1),(2358,18,17,82,1,1,1,1,1),(2359,18,17,293,1,1,1,1,1),(2360,17,7,294,1,1,1,1,1),(2361,17,7,295,1,1,1,1,1),(2362,17,7,296,1,1,1,1,1),(2363,17,7,297,1,1,1,1,1),(2364,17,7,298,1,1,1,1,1),(2365,17,7,299,1,1,1,1,1),(2366,17,7,300,1,1,1,1,1),(2367,17,7,301,1,1,1,1,1),(2368,17,7,302,1,1,1,1,1),(2369,17,7,303,1,1,1,1,1),(2370,17,7,304,1,1,1,1,1),(2371,17,7,305,1,1,1,1,1),(2372,17,7,306,1,1,1,1,1),(2373,17,7,307,1,1,1,1,1),(2374,17,7,308,1,1,1,1,1),(2375,17,7,309,1,1,1,1,1),(2376,17,7,310,1,1,1,1,1),(2377,17,7,311,1,1,1,1,1),(2378,17,7,312,1,1,1,1,1),(2379,17,7,313,1,1,1,1,1),(2380,18,7,294,1,1,1,1,1),(2381,18,7,295,1,1,1,1,1),(2382,18,7,296,1,1,1,1,1),(2383,18,7,297,1,1,1,1,1),(2384,18,7,298,1,1,1,1,1),(2385,18,7,299,1,1,1,1,1),(2386,18,7,300,1,1,1,1,1),(2387,18,7,301,1,1,1,1,1),(2388,18,7,302,1,1,1,1,1),(2389,18,7,303,1,1,1,1,1),(2390,18,7,304,1,1,1,1,1),(2391,18,7,305,1,1,1,1,1),(2392,18,7,306,1,1,1,1,1),(2393,18,7,307,1,1,1,1,1),(2394,18,7,308,1,1,1,1,1),(2395,18,7,309,1,1,1,1,1),(2396,18,7,310,1,1,1,1,1),(2397,18,7,311,1,1,1,1,1),(2398,18,7,312,1,1,1,1,1),(2399,18,7,313,1,1,1,1,1),(2400,19,7,294,1,1,1,1,1),(2401,19,7,295,1,1,1,1,1),(2402,19,7,296,1,1,1,1,1),(2403,19,7,297,1,1,1,1,1),(2404,19,7,298,1,1,1,1,1),(2405,19,7,299,1,1,1,1,1),(2406,19,7,300,1,1,1,1,1),(2407,19,7,301,1,1,1,1,1),(2408,19,7,302,1,1,1,1,1),(2409,19,7,303,1,1,1,1,1),(2410,19,7,304,1,1,1,1,1),(2411,19,7,305,1,1,1,1,1),(2412,19,7,306,1,1,1,1,1),(2413,19,7,307,1,1,1,1,1),(2414,19,7,308,1,1,1,1,1),(2415,19,7,309,1,1,1,1,1),(2416,19,7,310,1,1,1,1,1),(2417,19,7,311,1,1,1,1,1),(2418,19,7,312,1,1,1,1,1),(2419,19,7,313,1,1,1,1,1),(2420,20,7,294,1,1,1,1,1),(2421,20,7,295,1,1,1,1,1),(2422,20,7,296,1,1,1,1,1),(2423,20,7,297,1,1,1,1,1),(2424,20,7,298,1,1,1,1,1),(2425,20,7,299,1,1,1,1,1),(2426,20,7,300,1,1,1,1,1),(2427,20,7,301,1,1,1,1,1),(2428,20,7,302,1,1,1,1,1),(2429,20,7,303,1,1,1,1,1),(2430,20,7,304,1,1,1,1,1),(2431,20,7,305,1,1,1,1,1),(2432,20,7,306,1,1,1,1,1),(2433,20,7,307,1,1,1,1,1),(2434,20,7,308,1,1,1,1,1),(2435,20,7,309,1,1,1,1,1),(2436,20,7,310,1,1,1,1,1),(2437,20,7,311,1,1,1,1,1),(2438,20,7,312,1,1,1,1,1),(2439,20,7,313,1,1,1,1,1),(2440,21,7,294,1,1,1,1,1),(2441,21,7,295,1,1,1,1,1),(2442,21,7,296,1,1,1,1,1),(2443,21,7,297,1,1,1,1,1),(2444,21,7,298,1,1,1,1,1),(2445,21,7,299,1,1,1,1,1),(2446,21,7,300,1,1,1,1,1),(2447,21,7,301,1,1,1,1,1),(2448,21,7,302,1,1,1,1,1),(2449,21,7,303,1,1,1,1,1),(2450,21,7,304,1,1,1,1,1),(2451,21,7,305,1,1,1,1,1),(2452,21,7,306,1,1,1,1,1),(2453,21,7,307,1,1,1,1,1),(2454,21,7,308,1,1,1,1,1),(2455,21,7,309,1,1,1,1,1),(2456,21,7,310,1,1,1,1,1),(2457,21,7,311,1,1,1,1,1),(2458,21,7,312,1,1,1,1,1),(2459,21,7,313,1,1,1,1,1),(2460,22,7,294,1,1,1,1,1),(2461,22,7,295,1,1,1,1,1),(2462,22,7,296,1,1,1,1,1),(2463,22,7,297,1,1,1,1,1),(2464,22,7,298,1,1,1,1,1),(2465,22,7,299,1,1,1,1,1),(2466,22,7,300,1,1,1,1,1),(2467,22,7,301,1,1,1,1,1),(2468,22,7,302,1,1,1,1,1),(2469,22,7,303,1,1,1,1,1),(2470,22,7,304,1,1,1,1,1),(2471,22,7,305,1,1,1,1,1),(2472,22,7,306,1,1,1,1,1),(2473,22,7,307,1,1,1,1,1),(2474,22,7,308,1,1,1,1,1),(2475,22,7,309,1,1,1,1,1),(2476,22,7,310,1,1,1,1,1),(2477,22,7,311,1,1,1,1,1),(2478,22,7,312,1,1,1,1,1),(2479,22,7,313,1,1,1,1,1),(2480,23,7,294,1,1,1,1,1),(2481,23,7,295,1,1,1,1,1),(2482,23,7,296,1,1,1,1,1),(2483,23,7,297,1,1,1,1,1),(2484,23,7,298,1,1,1,1,1),(2485,23,7,299,1,1,1,1,1),(2486,23,7,300,1,1,1,1,1),(2487,23,7,301,1,1,1,1,1),(2488,23,7,302,1,1,1,1,1),(2489,23,7,303,1,1,1,1,1),(2490,23,7,304,1,1,1,1,1),(2491,23,7,305,1,1,1,1,1),(2492,23,7,306,1,1,1,1,1),(2493,23,7,307,1,1,1,1,1),(2494,23,7,308,1,1,1,1,1),(2495,23,7,309,1,1,1,1,1),(2496,23,7,310,1,1,1,1,1),(2497,23,7,311,1,1,1,1,1),(2498,23,7,312,1,1,1,1,1),(2499,23,7,313,1,1,1,1,1),(2500,24,7,294,1,1,1,1,1),(2501,24,7,295,1,1,1,1,1),(2502,24,7,296,1,1,1,1,1),(2503,24,7,297,1,1,1,1,1),(2504,24,7,298,1,1,1,1,1),(2505,24,7,299,1,1,1,1,1),(2506,24,7,300,1,1,1,1,1),(2507,24,7,301,1,1,1,1,1),(2508,24,7,302,1,1,1,1,1),(2509,24,7,303,1,1,1,1,1),(2510,24,7,304,1,1,1,1,1),(2511,24,7,305,1,1,1,1,1),(2512,24,7,306,1,1,1,1,1),(2513,24,7,307,1,1,1,1,1),(2514,24,7,308,1,1,1,1,1),(2515,24,7,309,1,1,1,1,1),(2516,24,7,310,1,1,1,1,1),(2517,24,7,311,1,1,1,1,1),(2518,24,7,312,1,1,1,1,1),(2519,24,7,313,1,1,1,1,1),(2520,25,7,294,1,1,1,1,1),(2521,25,7,295,1,1,1,1,1),(2522,25,7,296,1,1,1,1,1),(2523,25,7,297,1,1,1,1,1),(2524,25,7,298,1,1,1,1,1),(2525,25,7,299,1,1,1,1,1),(2526,25,7,300,1,1,1,1,1),(2527,25,7,301,1,1,1,1,1),(2528,25,7,302,1,1,1,1,1),(2529,25,7,303,1,1,1,1,1),(2530,25,7,304,1,1,1,1,1),(2531,25,7,305,1,1,1,1,1),(2532,25,7,306,1,1,1,1,1),(2533,25,7,307,1,1,1,1,1),(2534,25,7,308,1,1,1,1,1),(2535,25,7,309,1,1,1,1,1),(2536,25,7,310,1,1,1,1,1),(2537,25,7,311,1,1,1,1,1),(2538,25,7,312,1,1,1,1,1),(2539,25,7,313,1,1,1,1,1),(2540,26,7,294,1,1,1,1,1),(2541,26,7,295,1,1,1,1,1),(2542,26,7,296,1,1,1,1,1),(2543,26,7,297,1,1,1,1,1),(2544,26,7,298,1,1,1,1,1),(2545,26,7,299,1,1,1,1,1),(2546,26,7,300,1,1,1,1,1),(2547,26,7,301,1,1,1,1,1),(2548,26,7,302,1,1,1,1,1),(2549,26,7,303,1,1,1,1,1),(2550,26,7,304,1,1,1,1,1),(2551,26,7,305,1,1,1,1,1),(2552,26,7,306,1,1,1,1,1),(2553,26,7,307,1,1,1,1,1),(2554,26,7,308,1,1,1,1,1),(2555,26,7,309,1,1,1,1,1),(2556,26,7,310,1,1,1,1,1),(2557,26,7,311,1,1,1,1,1),(2558,26,7,312,1,1,1,1,1),(2559,26,7,313,1,1,1,1,1),(2560,96,18,314,1,1,1,1,1),(2561,96,18,315,1,1,1,1,1),(2562,96,18,316,1,1,1,1,1),(2563,96,18,317,1,1,1,1,1),(2564,96,18,318,1,1,1,1,1),(2565,96,18,319,1,1,1,1,1),(2566,96,18,320,1,1,1,1,1),(2567,96,18,321,1,1,1,1,1),(2568,96,18,322,1,1,1,1,1),(2569,97,18,323,1,1,1,1,1),(2570,97,18,324,1,1,1,1,1),(2571,97,18,325,1,1,1,1,1),(2572,97,18,326,1,1,1,1,1),(2573,97,18,327,1,1,1,1,1),(2574,97,18,328,1,1,1,1,1),(2575,98,18,314,1,1,1,1,1),(2576,98,18,316,1,1,1,1,1),(2577,98,18,330,1,1,1,1,1),(2578,99,18,323,1,1,1,1,1),(2579,99,18,324,1,1,1,1,1),(2580,99,18,325,1,1,1,1,1),(2581,99,18,326,1,1,1,1,1),(2582,99,18,327,1,1,1,1,1),(2583,99,18,328,1,1,1,1,1),(2584,96,7,336,1,1,1,1,1),(2585,96,7,337,1,1,1,1,1),(2586,96,7,338,1,1,1,1,1),(2587,96,7,339,1,1,1,1,1),(2588,96,7,340,1,1,1,1,1),(2589,96,7,341,1,1,1,1,1),(2590,96,7,342,1,1,1,1,1),(2591,96,7,343,1,1,1,1,1),(2592,96,7,344,1,1,1,1,1),(2593,96,7,345,1,1,1,1,1),(2594,96,7,346,1,1,1,1,1),(2595,96,7,347,1,1,1,1,1),(2596,96,7,348,1,1,1,1,1),(2597,96,7,349,1,1,1,1,1),(2598,96,7,350,1,1,1,1,1),(2599,96,7,351,1,1,1,1,1),(2600,96,7,352,1,1,1,1,1),(2601,96,7,353,1,1,1,1,1),(2602,96,7,354,1,1,1,1,1),(2603,96,7,355,1,1,1,1,1),(2604,97,7,336,1,1,1,1,1),(2605,97,7,337,1,1,1,1,1),(2606,97,7,338,1,1,1,1,1),(2607,97,7,339,1,1,1,1,1),(2608,97,7,340,1,1,1,1,1),(2609,97,7,341,1,1,1,1,1),(2610,97,7,342,1,1,1,1,1),(2611,97,7,343,1,1,1,1,1),(2612,97,7,344,1,1,1,1,1),(2613,97,7,345,1,1,1,1,1),(2614,97,7,346,1,1,1,1,1),(2615,97,7,347,1,1,1,1,1),(2616,97,7,348,1,1,1,1,1),(2617,97,7,349,1,1,1,1,1),(2618,97,7,350,1,1,1,1,1),(2619,97,7,351,1,1,1,1,1),(2620,97,7,352,1,1,1,1,1),(2621,97,7,353,1,1,1,1,1),(2622,97,7,354,1,1,1,1,1),(2623,97,7,355,1,1,1,1,1),(2624,98,7,336,1,1,1,1,1),(2625,98,7,337,1,1,1,1,1),(2626,98,7,338,1,1,1,1,1),(2627,98,7,339,1,1,1,1,1),(2628,98,7,340,1,1,1,1,1),(2629,98,7,341,1,1,1,1,1),(2630,98,7,342,1,1,1,1,1),(2631,98,7,343,1,1,1,1,1),(2632,98,7,344,1,1,1,1,1),(2633,98,7,345,1,1,1,1,1),(2634,98,7,346,1,1,1,1,1),(2635,98,7,347,1,1,1,1,1),(2636,98,7,348,1,1,1,1,1),(2637,98,7,349,1,1,1,1,1),(2638,98,7,350,1,1,1,1,1),(2639,98,7,351,1,1,1,1,1),(2640,98,7,352,1,1,1,1,1),(2641,98,7,353,1,1,1,1,1),(2642,98,7,354,1,1,1,1,1),(2643,98,7,355,1,1,1,1,1),(2644,99,7,336,1,1,1,1,1),(2645,99,7,337,1,1,1,1,1),(2646,99,7,338,1,1,1,1,1),(2647,99,7,339,1,1,1,1,1),(2648,99,7,340,1,1,1,1,1),(2649,99,7,341,1,1,1,1,1),(2650,99,7,342,1,1,1,1,1),(2651,99,7,343,1,1,1,1,1),(2652,99,7,344,1,1,1,1,1),(2653,99,7,345,1,1,1,1,1),(2654,99,7,346,1,1,1,1,1),(2655,99,7,347,1,1,1,1,1),(2656,99,7,348,1,1,1,1,1),(2657,99,7,349,1,1,1,1,1),(2658,99,7,350,1,1,1,1,1),(2659,99,7,351,1,1,1,1,1),(2660,99,7,352,1,1,1,1,1),(2661,99,7,353,1,1,1,1,1),(2662,99,7,354,1,1,1,1,1),(2663,99,7,355,1,1,1,1,1),(2664,28,7,356,1,1,1,1,1),(2665,28,7,357,1,1,1,1,1),(2666,28,7,358,1,1,1,1,1),(2667,28,7,359,1,1,1,1,1),(2668,28,7,360,1,1,1,1,1),(2669,28,7,361,1,1,1,1,1),(2670,33,7,362,1,1,1,1,1),(2671,33,7,363,1,1,1,1,1),(2672,33,7,364,1,1,1,1,1),(2673,33,7,365,1,1,1,1,1),(2674,33,7,366,1,1,1,1,1),(2675,33,7,367,1,1,1,1,1),(2676,33,7,368,1,1,1,1,1),(2677,33,7,369,1,1,1,1,1),(2678,33,7,370,1,1,1,1,1),(2679,33,7,371,1,1,1,1,1),(2680,33,7,372,1,1,1,1,1),(2681,33,7,373,1,1,1,1,1),(2682,33,7,374,1,1,1,1,1),(2683,33,7,375,1,1,1,1,1),(2684,33,7,376,1,1,1,1,1),(2685,33,7,377,1,1,1,1,1),(2686,28,19,378,1,1,1,1,1),(2687,28,19,379,1,1,1,1,1),(2688,28,19,380,1,1,1,1,1),(2689,28,19,381,1,1,1,1,1),(2690,28,19,382,1,1,1,1,1),(2691,29,19,383,1,1,1,1,1),(2692,29,19,384,1,1,1,1,1),(2693,29,19,385,1,1,1,1,1),(2694,29,19,386,1,1,1,1,1),(2695,29,19,387,1,1,1,1,1),(2696,30,19,388,1,1,1,1,1),(2697,30,19,389,1,1,1,1,1),(2698,30,19,390,1,1,1,1,1),(2699,30,19,391,1,1,1,1,1),(2700,31,19,384,1,1,1,1,1),(2701,31,19,385,1,1,1,1,1),(2702,31,19,386,1,1,1,1,1),(2703,31,19,387,1,1,1,1,1),(2704,31,19,392,1,1,1,1,1),(2705,32,19,393,1,1,1,1,1),(2706,32,19,394,1,1,1,1,1),(2707,32,19,385,1,1,1,1,1),(2708,32,19,395,1,1,1,1,1); + '); + } + + + public function down() + { + // + } +} diff --git a/database/migrations/2021_01_06_113020_update_data_place_fact_icon_field.php b/database/migrations/2021_01_06_113020_update_data_place_fact_icon_field.php new file mode 100644 index 0000000..2c731df --- /dev/null +++ b/database/migrations/2021_01_06_113020_update_data_place_fact_icon_field.php @@ -0,0 +1,28 @@ + Error; 1 =>Success; 2=> Start, 3=> Pending, 4 => Cancel/Refund, 5 => LNK' ; + ") ; + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + } +} diff --git a/database/migrations/2021_02_01_144004_update_data_general_timezone.php b/database/migrations/2021_02_01_144004_update_data_general_timezone.php new file mode 100644 index 0000000..806fee0 --- /dev/null +++ b/database/migrations/2021_02_01_144004_update_data_general_timezone.php @@ -0,0 +1,24 @@ +where('config_key' , 'profile_rate_mapping')->update(['config_value_json' => $profileRateMapping]); + + } + + + public function down() + { + // + } +} diff --git a/database/migrations/2021_02_10_133328_property_payment_transaction_status.php b/database/migrations/2021_02_10_133328_property_payment_transaction_status.php new file mode 100644 index 0000000..d74c497 --- /dev/null +++ b/database/migrations/2021_02_10_133328_property_payment_transaction_status.php @@ -0,0 +1,57 @@ +9qdwz#l!Y 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�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::class); + $this->call(PropertyFactSeeder::class); + $this->call(PropertyAdditionalInfoKeySeeder::class); + $this->call(PropertyAdditionalInfoKeyLocaleSeeder::class); + $this->call(PropertyExecuteTypeSeeder::class); + $this->call(PropertyExecuteTypeLocaleSeeder::class); + $this->call(PropertyExecuteSeeder::class); +// $this->call(PropertyPhotoSeeder::class); + $this->call(PropertyTypeSeeder::class); + $this->call(PropertyGoogleLabelSeeder::class); + $this->call(PropertyChainSeeder::class); + } +} diff --git a/database/seeds/DestinationTableSeeder.php b/database/seeds/DestinationTableSeeder.php new file mode 100644 index 0000000..bed94b4 --- /dev/null +++ b/database/seeds/DestinationTableSeeder.php @@ -0,0 +1,34 @@ +truncate(); +/* + $unixTimestamp = time(); + $destinationId = DB::table('destination')->insertGetId([ + 'name'=> 'Turkey', 'parent_id' => 0, 'country_code' => 'TR', 'created_at'=> $unixTimestamp, 'updated_at' => $unixTimestamp + ]); + $destinationId = DB::table('destination')->insertGetId([ + 'name'=> 'Istanbul', 'parent_id' => $destinationId, 'country_code' => 'TR', 'created_at'=> $unixTimestamp, 'updated_at' => $unixTimestamp + ]); + $destinationId = DB::table('destination')->insertGetId([ + 'name'=> 'Fatih', 'parent_id' => $destinationId, 'country_code' => 'TR','created_at'=> $unixTimestamp, 'updated_at' => $unixTimestamp + ]); + + DB::table('destination')->insertGetId([ + 'name'=> 'Sultanahmet', 'parent_id' => $destinationId, 'country_code' => 'TR', 'created_at'=> $unixTimestamp, 'updated_at' => $unixTimestamp + ]); +*/ + + } +} diff --git a/database/seeds/FirstRunSeeder.php b/database/seeds/FirstRunSeeder.php new file mode 100644 index 0000000..540e6d2 --- /dev/null +++ b/database/seeds/FirstRunSeeder.php @@ -0,0 +1,775 @@ +insertGetId( + [ + 'id' => 1, + 'gender' => 'M', + 'name' => 'Test', + 'surname' => 'User -1-', + 'email' => 'test1@rezervasyon.com', + 'password' => Hash::make('123456'), + "hash_key" => hash('sha512', Str::random(32) ), + 'phone' => '02123668989', + 'user_type' => 1, + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + + + ] + ); + DB::table('user')->insertGetId( + [ + 'id' => 2, + 'gender' => 'M', + 'name' => 'Test', + 'surname' => 'User -2-', + 'email' => 'test2@rezervasyon.com', + 'password' => Hash::make('1234567'), + "hash_key" => hash('sha512', Str::random(32) ), + 'phone' => '02123668989', + 'user_type' => 0, + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + + ); + DB::table('user')->insertGetId( + + [ + 'id' => 3, + 'gender' => 'M', + 'name' => 'Test', + 'surname' => 'User -3-', + 'email' => 'test3@rezervasyon.com', + 'password' => Hash::make('123456'), + "hash_key" => hash('sha512', Str::random(32) ), + 'phone' => '02123668989', + 'user_type' => 0, + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + + // LANGUAGE DATA + DB::table('language')->insertGetId( + [ + 'id' => 7, + 'code' => 'en', + 'name' => 'English', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('language')->insertGetId( + [ + 'id' => 10, + 'code' => 'de', + 'name' => 'German', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('language')->insertGetId( + [ + 'id' => 22, + 'code' => 'tr', + 'name' => 'Turkish', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + + + // CURRENCY DATA + DB::table('currency')->insertGetId( + [ + 'id' => 1, + 'code' => 'TRY', + 'name' => 'Turkish Lira', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('currency')->insertGetId( + [ + 'id' => 2, + 'code' => 'USD', + 'name' => 'US Dollar', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('currency')->insertGetId( + [ + 'id' => 3, + 'code' => 'EUR', + 'name' => 'Euro', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + + // CURRENCY LOCALE DATA + DB::table('currency_locale')->insertGetId( + [ + 'id' => 1, + 'currency_code' => 'TRY', + 'name' => 'Turkish Lira', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('currency_locale')->insertGetId( + [ + 'id' => 2, + 'currency_code' => 'USD', + 'name' => 'US Dollar', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('currency_locale')->insertGetId( + [ + 'id' => 3, + 'currency_code' => 'EUR', + 'name' => 'Euro', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + + // PROPERTY DATA + DB::table('property')->insertGetId( + [ + 'id' => 1, + 'name' => 'Test Hotel -1-', + 'destination_id' => 1, + 'rating' => 1, + 'currency_type' => 'EUR', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + + // PROPERTY MAPPING DATA + DB::table('user_property_mapping')->insertGetId( + [ + 'id' => 1, + 'user_id' => 1, + 'property_id' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + + // PROPERTY CONTENT CATEGORY + DB::table('property_content_category')->insertGetId( + [ + 'id' => 1, + 'name' => 'Location', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_content_category')->insertGetId( + [ + 'id' => 2, + 'name' => 'Facilities', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_content_category')->insertGetId( + [ + 'id' => 3, + 'name' => 'Rooms', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_content_category')->insertGetId( + [ + 'id' => 4, + 'name' => 'Sports/Entertainment', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_content_category')->insertGetId( + [ + 'id' => 5, + 'name' => 'Meals', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_content_category')->insertGetId( + [ + 'id' => 6, + 'name' => 'Payment', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + + + // PROPERTY CONTENT CATEGORY LOCALE + DB::table('property_content_category_locale')->insertGetId( + [ + 'id' => 1, + 'content_category_id' => 1, + 'name' => 'Location', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_content_category_locale')->insertGetId( + [ + 'id' => 2, + 'content_category_id' => 2, + 'name' => 'Facilities', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_content_category_locale')->insertGetId( + [ + 'id' => 3, + 'content_category_id' => 3, + 'name' => 'Rooms', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_content_category_locale')->insertGetId( + [ + 'id' => 4, + 'content_category_id' => 4, + 'name' => 'Sports/Entertainment', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_content_category_locale')->insertGetId( + [ + 'id' => 5, + 'content_category_id' => 5, + 'name' => 'Meals', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 1, + 'category_name' => "General", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 2, + 'category_name' => "View of exterior", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 3, + 'category_name' => "Bar", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 4, + 'category_name' => "Sport/ leisure", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 5, + 'category_name' => "Conference facilities", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 6, + 'category_name' => "Lobby", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 7, + 'category_name' => "Pool", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 8, + 'category_name' => "Restaurant", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 9, + 'category_name' => "Beach", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 10, + 'category_name' => "Terrace", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category')->insertGetId( + [ + 'id' => 11, + 'category_name' => "Accommodation", + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + + + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 1, + 'property_photo_category_id' => 1, + 'category_name' => 'Genel', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 2, + 'property_photo_category_id' => 2, + 'category_name' => 'dış görünüm', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 3, + 'property_photo_category_id' => 3, + 'category_name' => 'bar', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 4, + 'property_photo_category_id' => 4, + 'category_name' => 'Spor/eğlence', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 5, + 'property_photo_category_id' => 5, + 'category_name' => 'Konferans salonu', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 6, + 'property_photo_category_id' => 6, + 'category_name' => 'Lobi', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 7, + 'property_photo_category_id' => 7, + 'category_name' => 'Havuz', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 8, + 'property_photo_category_id' => 8, + 'category_name' => 'Restoran', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 9, + 'property_photo_category_id' => 9, + 'category_name' => 'Kumsal', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 10, + 'property_photo_category_id' => 10, + 'category_name' => 'Teras', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 11, + 'property_photo_category_id' => 11, + 'category_name' => 'Konaklama', + 'locale' => 'tr', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 12, + 'property_photo_category_id' => 1, + 'category_name' => 'General', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 13, + 'property_photo_category_id' => 2, + 'category_name' => 'View of exterior', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 14, + 'property_photo_category_id' => 3, + 'category_name' => 'Bar', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 15, + 'property_photo_category_id' => 4, + 'category_name' => 'Sport/leisure', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 16, + 'property_photo_category_id' => 5, + 'category_name' => 'Conference facilities', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 17, + 'property_photo_category_id' => 6, + 'category_name' => 'Lobby', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 18, + 'property_photo_category_id' => 7, + 'category_name' => 'Pool', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 19, + 'property_photo_category_id' => 8, + 'category_name' => 'Restaurant', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 20, + 'property_photo_category_id' => 9, + 'category_name' => 'Beach', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 21, + 'property_photo_category_id' => 10, + 'category_name' => 'Terrace', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + DB::table('property_photo_category_locale')->insertGetId( + [ + 'id' => 22, + 'property_photo_category_id' => 11, + 'category_name' => 'Accommodation', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + ); + + } +} diff --git a/database/seeds/PropertyAdditionalInfoKeyLocaleSeeder.php b/database/seeds/PropertyAdditionalInfoKeyLocaleSeeder.php new file mode 100644 index 0000000..e314b0f --- /dev/null +++ b/database/seeds/PropertyAdditionalInfoKeyLocaleSeeder.php @@ -0,0 +1,60 @@ +delete(); + + \DB::table('property_additional_info_key_locale')->insert(array ( + 0 => + array ( + 'id' => 1, + 'additional_info_key_id' => 1, + 'additional_info_key' => 'Building Year', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 1 => + array ( + 'id' => 2, + 'additional_info_key_id' => 2, + 'additional_info_key' => 'Restoration Year', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 2 => + array ( + 'id' => 3, + 'additional_info_key_id' => 3, + 'additional_info_key' => 'Room Count', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + )); + + + } +} diff --git a/database/seeds/PropertyAdditionalInfoKeySeeder.php b/database/seeds/PropertyAdditionalInfoKeySeeder.php new file mode 100644 index 0000000..731e5a7 --- /dev/null +++ b/database/seeds/PropertyAdditionalInfoKeySeeder.php @@ -0,0 +1,53 @@ +delete(); + + \DB::table('property_additional_info_key')->insert(array ( + 0 => + array ( + 'id' => 1, + 'additional_info_key' => 'Building Year', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 1 => + array ( + 'id' => 2, + 'additional_info_key' => 'Restoration Year', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 2 => + array ( + 'id' => 3, + 'additional_info_key' => 'Room Count', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + )); + + + } +} diff --git a/database/seeds/PropertyChainSeeder.php b/database/seeds/PropertyChainSeeder.php new file mode 100644 index 0000000..1ba6951 --- /dev/null +++ b/database/seeds/PropertyChainSeeder.php @@ -0,0 +1,133 @@ +delete(); + + \DB::table('property_chain')->insert(array ( + 0 => + array ( + 'id' => 1, + 'name' => 'Independent', + 'loyalty' => NULL, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 1 => + array ( + 'id' => 2, + 'name' => 'Adam\'s Mark', + 'loyalty' => NULL, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 2 => + array ( + 'id' => 3, + 'name' => 'Shilo Inn', + 'loyalty' => NULL, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 3 => + array ( + 'id' => 4, + 'name' => 'Renaissance', + 'loyalty' => 'Marriott', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 4 => + array ( + 'id' => 5, + 'name' => 'Best Western', + 'loyalty' => NULL, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 5 => + array ( + 'id' => 6, + 'name' => 'Clarion', + 'loyalty' => NULL, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 6 => + array ( + 'id' => 7, + 'name' => 'Comfort Inn', + 'loyalty' => NULL, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 7 => + array ( + 'id' => 8, + 'name' => 'Courtyard', + 'loyalty' => 'Marriott', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 8 => + array ( + 'id' => 9, + 'name' => 'Doral', + 'loyalty' => NULL, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 9 => + array ( + 'id' => 10, + 'name' => 'Days Inn', + 'loyalty' => NULL, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + )); + + + } +} diff --git a/database/seeds/PropertyExecuteSeeder.php b/database/seeds/PropertyExecuteSeeder.php new file mode 100644 index 0000000..8d12c5f --- /dev/null +++ b/database/seeds/PropertyExecuteSeeder.php @@ -0,0 +1,42 @@ +delete(); + + \DB::table('property_executive')->insert(array ( + 0 => + array ( + 'id' => 1, + 'property_id' => 1, + 'executive_type_id' => 1, + 'name_surname' => 'Burhan Çetin', + 'email' => 'mail@burhancetin.com', + 'phone_code' => NULL, + 'phone' => NULL, + 'mobile_code' => NULL, + 'mobile' => NULL, + 'fax_code' => NULL, + 'fax' => NULL, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1571573358, + 'updated_at' => 1571573358, + ), + )); + + + } +} diff --git a/database/seeds/PropertyExecuteTypeLocaleSeeder.php b/database/seeds/PropertyExecuteTypeLocaleSeeder.php new file mode 100644 index 0000000..0bde96e --- /dev/null +++ b/database/seeds/PropertyExecuteTypeLocaleSeeder.php @@ -0,0 +1,83 @@ +delete(); + + \DB::table('property_executive_type_locale')->insert(array ( + 0 => + array ( + 'id' => 1, + 'executive_id' => 1, + 'name' => 'Finance', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 1 => + array ( + 'id' => 2, + 'executive_id' => 2, + 'name' => 'Reservation', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 2 => + array ( + 'id' => 3, + 'executive_id' => 3, + 'name' => 'Sales', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 3 => + array ( + 'id' => 4, + 'executive_id' => 4, + 'name' => 'Activity', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 4 => + array ( + 'id' => 5, + 'executive_id' => 5, + 'name' => 'Group', + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + )); + + + } +} diff --git a/database/seeds/PropertyExecuteTypeSeeder.php b/database/seeds/PropertyExecuteTypeSeeder.php new file mode 100644 index 0000000..66656d7 --- /dev/null +++ b/database/seeds/PropertyExecuteTypeSeeder.php @@ -0,0 +1,73 @@ +delete(); + + \DB::table('property_executive_type')->insert(array ( + 0 => + array ( + 'id' => 1, + 'name' => 'Finance', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 1 => + array ( + 'id' => 2, + 'name' => 'Reservation', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 2 => + array ( + 'id' => 3, + 'name' => 'Sales', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 3 => + array ( + 'id' => 4, + 'name' => 'Activity', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 4 => + array ( + 'id' => 5, + 'name' => 'Group', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + )); + + + } +} diff --git a/database/seeds/PropertyFactSeeder.php b/database/seeds/PropertyFactSeeder.php new file mode 100644 index 0000000..05b6d53 --- /dev/null +++ b/database/seeds/PropertyFactSeeder.php @@ -0,0 +1,5623 @@ +delete(); + + \DB::table('property_fact')->insert(array ( + 0 => + array ( + 'id' => 1, + 'name' => 'Amenities', + 'parent_id' => NULL, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 1 => + array ( + 'id' => 2, + 'name' => 'Payment', + 'parent_id' => 1, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 2 => + array ( + 'id' => 3, + 'name' => 'American Express', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fa fa-cc-amex', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 3 => + array ( + 'id' => 4, + 'name' => 'VISA', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fab fa-cc-visa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 4 => + array ( + 'id' => 5, + 'name' => 'MasterCard', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fab fa-cc-mastercard', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 5 => + array ( + 'id' => 6, + 'name' => 'Diner\'s Club', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fab fa-cc-diners-club', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 6 => + array ( + 'id' => 7, + 'name' => 'JCB', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fab fa-cc-jcb', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 7 => + array ( + 'id' => 8, + 'name' => 'EC-Card', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-credit-card', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 8 => + array ( + 'id' => 9, + 'name' => 'Cash', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-money-bill-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 9 => + array ( + 'id' => 10, + 'name' => 'Cheque', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-money-check-edit-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 10 => + array ( + 'id' => 11, + 'name' => 'Traveller Cheuque', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-money-check-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 11 => + array ( + 'id' => 12, + 'name' => 'Company Invoice', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-file-invoice-dollar', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 12 => + array ( + 'id' => 13, + 'name' => 'SZÉP Card', + 'parent_id' => 2, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-credit-card', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 13 => + array ( + 'id' => 14, + 'name' => 'Accommodation', + 'parent_id' => 1, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 14 => + array ( + 'id' => 15, + 'name' => 'All inclusive', + 'parent_id' => 14, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 15 => + array ( + 'id' => 16, + 'name' => 'Bed and Breakfast', + 'parent_id' => 14, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bread-slice', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 16 => + array ( + 'id' => 17, + 'name' => 'Lunch', + 'parent_id' => 14, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 17 => + array ( + 'id' => 18, + 'name' => 'Only Room', + 'parent_id' => 14, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 18 => + array ( + 'id' => 19, + 'name' => 'Full Board', + 'parent_id' => 14, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-check-double', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 19 => + array ( + 'id' => 20, + 'name' => 'Half Board', + 'parent_id' => 14, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-check', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 20 => + array ( + 'id' => 21, + 'name' => 'Services', + 'parent_id' => 1, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 21 => + array ( + 'id' => 22, + 'name' => 'Reception', + 'parent_id' => 21, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-concierge-bell', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 22 => + array ( + 'id' => 23, + 'name' => '24 Hour Room Service', + 'parent_id' => 21, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-clock', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 23 => + array ( + 'id' => 24, + 'name' => '24 Hour Check-in Service', + 'parent_id' => 21, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-door-open', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 24 => + array ( + 'id' => 25, + 'name' => 'Room Service', + 'parent_id' => 21, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-phone', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 25 => + array ( + 'id' => 26, + 'name' => 'Dry Clean Service', + 'parent_id' => 21, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tshirt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 26 => + array ( + 'id' => 27, + 'name' => 'Cleaning Service', + 'parent_id' => 21, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-broom', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 27 => + array ( + 'id' => 28, + 'name' => 'Shoeshine', + 'parent_id' => 21, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-shoe-prints', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 28 => + array ( + 'id' => 29, + 'name' => 'Housekeeping', + 'parent_id' => 21, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-broom', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 29 => + array ( + 'id' => 30, + 'name' => 'Ironing Service', + 'parent_id' => 21, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tshirt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 30 => + array ( + 'id' => 31, + 'name' => 'Internet', + 'parent_id' => 1, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 31 => + array ( + 'id' => 32, + 'name' => 'WiFi', + 'parent_id' => 31, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wifi', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 32 => + array ( + 'id' => 33, + 'name' => 'Cable Internet', + 'parent_id' => 31, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ethernet', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 33 => + array ( + 'id' => 34, + 'name' => 'Business Center', + 'parent_id' => 1, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 34 => + array ( + 'id' => 35, + 'name' => 'Fax', + 'parent_id' => 34, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fax', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 35 => + array ( + 'id' => 36, + 'name' => 'Photocopy', + 'parent_id' => 34, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-copy', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 36 => + array ( + 'id' => 37, + 'name' => 'Computer', + 'parent_id' => 34, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-laptop', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 37 => + array ( + 'id' => 38, + 'name' => 'Printer', + 'parent_id' => 34, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-print', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 38 => + array ( + 'id' => 39, + 'name' => 'Scanner', + 'parent_id' => 34, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-file-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 39 => + array ( + 'id' => 40, + 'name' => 'Transportation', + 'parent_id' => 1, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 40 => + array ( + 'id' => 41, + 'name' => 'Rent a car', + 'parent_id' => 40, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-car', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 41 => + array ( + 'id' => 42, + 'name' => 'Bicycle Rental', + 'parent_id' => 40, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bicycle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 42 => + array ( + 'id' => 43, + 'name' => 'Airport Transfer', + 'parent_id' => 40, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-shuttle-van', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 43 => + array ( + 'id' => 44, + 'name' => 'Motorcycle Rental', + 'parent_id' => 40, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-motorcycle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 44 => + array ( + 'id' => 45, + 'name' => 'Scooter Rental', + 'parent_id' => 40, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-motorcycle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 45 => + array ( + 'id' => 46, + 'name' => 'Taxi', + 'parent_id' => 40, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-taxi', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 46 => + array ( + 'id' => 47, + 'name' => 'Disabled', + 'parent_id' => 1, + 'type' => 1, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 47 => + array ( + 'id' => 48, + 'name' => 'Disable Entrance', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wheelchair', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 48 => + array ( + 'id' => 49, + 'name' => 'Disabled Lift', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wheelchair', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 49 => + array ( + 'id' => 50, + 'name' => 'Disabled Bath', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-shower', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 50 => + array ( + 'id' => 51, + 'name' => 'Toilet for Disabled Use', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-toilet', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 51 => + array ( + 'id' => 52, + 'name' => 'Disabled Room', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 52 => + array ( + 'id' => 53, + 'name' => 'Disabled Parking', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-parking', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 53 => + array ( + 'id' => 54, + 'name' => 'Disabled Ramp', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wheelchair', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 54 => + array ( + 'id' => 55, + 'name' => 'Disabled Service', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wheelchair', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 55 => + array ( + 'id' => 56, + 'name' => 'Terrace for the Disabled', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wheelchair', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 56 => + array ( + 'id' => 57, + 'name' => 'Mobile Patient Bed', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 57 => + array ( + 'id' => 58, + 'name' => 'Companion Bed', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 58 => + array ( + 'id' => 59, + 'name' => 'Wheelchair', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wheelchair', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 59 => + array ( + 'id' => 60, + 'name' => 'Wheelchair Ramps', + 'parent_id' => 47, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wheelchair', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 60 => + array ( + 'id' => 61, + 'name' => 'Facility', + 'parent_id' => NULL, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 61 => + array ( + 'id' => 62, + 'name' => 'Public', + 'parent_id' => 61, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 62 => + array ( + 'id' => 63, + 'name' => 'Elevator', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-arrows-alt-v', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 63 => + array ( + 'id' => 64, + 'name' => 'Ballroom', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-music', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 64 => + array ( + 'id' => 65, + 'name' => 'Refrigerator', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-square', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 65 => + array ( + 'id' => 66, + 'name' => 'Coffee Tea Maker', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coffee', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 66 => + array ( + 'id' => 67, + 'name' => 'Dance Studio', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-music', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 67 => + array ( + 'id' => 68, + 'name' => 'Lounge', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-couch', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 68 => + array ( + 'id' => 69, + 'name' => 'Electrical Car Charging Station', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-charging-station', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 69 => + array ( + 'id' => 70, + 'name' => 'Football Field', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-futbol', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 70 => + array ( + 'id' => 71, + 'name' => 'Sun Terrace', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-sun', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 71 => + array ( + 'id' => 72, + 'name' => 'Gym', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dumbbell', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 72 => + array ( + 'id' => 73, + 'name' => 'Auditorium', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-microphone', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 73 => + array ( + 'id' => 74, + 'name' => 'Library', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-book', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 74 => + array ( + 'id' => 75, + 'name' => 'Mosque', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-star-and-crescent', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 75 => + array ( + 'id' => 76, + 'name' => 'Microwave', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-square', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 76 => + array ( + 'id' => 77, + 'name' => 'Music Library', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-music', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 77 => + array ( + 'id' => 78, + 'name' => 'Public Kitchen', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 78 => + array ( + 'id' => 79, + 'name' => 'Playground', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dice', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 79 => + array ( + 'id' => 80, + 'name' => 'Game Room', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-chess-queen', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 80 => + array ( + 'id' => 81, + 'name' => 'Designated Smoking Area', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-smoking', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 81 => + array ( + 'id' => 82, + 'name' => 'Panoramic Terrace', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-star', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 82 => + array ( + 'id' => 83, + 'name' => 'Lobby', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-concierge-bell', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 83 => + array ( + 'id' => 84, + 'name' => 'Chapel / Shrine', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-church', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 84 => + array ( + 'id' => 85, + 'name' => 'Tennis court', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-table-tennis', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 85 => + array ( + 'id' => 86, + 'name' => 'Terrace', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-star', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 86 => + array ( + 'id' => 87, + 'name' => 'Meeting room', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-user-friends', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 87 => + array ( + 'id' => 88, + 'name' => 'Tv Room', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tv', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 88 => + array ( + 'id' => 89, + 'name' => 'Video Library', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-video', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 89 => + array ( + 'id' => 90, + 'name' => 'Theater Hall', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-theater-masks', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 90 => + array ( + 'id' => 91, + 'name' => 'Safe Box', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-square', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 91 => + array ( + 'id' => 92, + 'name' => 'Cafeteria', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coffee', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 92 => + array ( + 'id' => 93, + 'name' => 'Shops', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-store', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 93 => + array ( + 'id' => 94, + 'name' => 'Newspaper Buffet', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-newspaper', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 94 => + array ( + 'id' => 95, + 'name' => 'Minimarket', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-store-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 95 => + array ( + 'id' => 96, + 'name' => 'Bar (s)', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-beer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 96 => + array ( + 'id' => 97, + 'name' => 'Roofbar', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-beer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 97 => + array ( + 'id' => 98, + 'name' => 'Bicycle Room', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bicycle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 98 => + array ( + 'id' => 99, + 'name' => 'Car park', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-parking', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 99 => + array ( + 'id' => 100, + 'name' => 'Children\'s Club', + 'parent_id' => 62, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-child', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 100 => + array ( + 'id' => 101, + 'name' => 'Food & Drink', + 'parent_id' => 61, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 101 => + array ( + 'id' => 102, + 'name' => 'Garden Bar', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-cocktail', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 102 => + array ( + 'id' => 103, + 'name' => 'Bar', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-beer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 103 => + array ( + 'id' => 104, + 'name' => 'BBQ Facilities', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hamburger', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 104 => + array ( + 'id' => 105, + 'name' => 'Beach Bar', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-beer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 105 => + array ( + 'id' => 106, + 'name' => 'Cafe', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coffee', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 106 => + array ( + 'id' => 107, + 'name' => 'Disco Bar', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-cocktail', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 107 => + array ( + 'id' => 108, + 'name' => 'Icecream', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ice-cream', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 108 => + array ( + 'id' => 109, + 'name' => 'Pancake Corner', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-cookie', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 109 => + array ( + 'id' => 110, + 'name' => 'Pool Bar', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wine-bottle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 110 => + array ( + 'id' => 111, + 'name' => 'Beverage Vending Machine', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coffee', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 111 => + array ( + 'id' => 112, + 'name' => 'Cafeteria', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coffee', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 112 => + array ( + 'id' => 113, + 'name' => 'Breakfast room', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bread-slice', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 113 => + array ( + 'id' => 114, + 'name' => 'Indoor Restaurant', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-door-closed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 114 => + array ( + 'id' => 115, + 'name' => 'Lobby Bar', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wine-bottle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 115 => + array ( + 'id' => 116, + 'name' => 'Hookah cafe', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-joint', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 116 => + array ( + 'id' => 117, + 'name' => 'Food Service In Room', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 117 => + array ( + 'id' => 118, + 'name' => 'Take Away', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-box', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 118 => + array ( + 'id' => 119, + 'name' => 'Pastry Shop', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-cookie', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 119 => + array ( + 'id' => 120, + 'name' => 'Restaurant', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 120 => + array ( + 'id' => 121, + 'name' => 'Open Buffet Restaurant', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 121 => + array ( + 'id' => 122, + 'name' => 'A La Carte Restaurant', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 122 => + array ( + 'id' => 123, + 'name' => 'Snack bar', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-candy-cane', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 123 => + array ( + 'id' => 124, + 'name' => 'Terrace Bar', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-cocktail', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 124 => + array ( + 'id' => 125, + 'name' => 'Vitamin Bar', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-apple-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 125 => + array ( + 'id' => 126, + 'name' => 'Special Diet', + 'parent_id' => 101, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-carrot', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 126 => + array ( + 'id' => 127, + 'name' => 'Pool', + 'parent_id' => 61, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 127 => + array ( + 'id' => 128, + 'name' => 'Aqua Park', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 128 => + array ( + 'id' => 129, + 'name' => 'Male / Female Separate Swimming Pool', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimmer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 129 => + array ( + 'id' => 130, + 'name' => 'Pool with Water Slide', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 130 => + array ( + 'id' => 131, + 'name' => 'Hot Water Pool', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 131 => + array ( + 'id' => 132, + 'name' => 'Locker Cabinet', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-restroom', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 132 => + array ( + 'id' => 133, + 'name' => 'Sunbed', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 133 => + array ( + 'id' => 134, + 'name' => 'Freshwater Pool', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 134 => + array ( + 'id' => 135, + 'name' => 'Themed Pool', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 135 => + array ( + 'id' => 136, + 'name' => 'Thermal Pool', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 136 => + array ( + 'id' => 137, + 'name' => 'Saltwater Pool', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 137 => + array ( + 'id' => 138, + 'name' => 'Swimming pool', + 'parent_id' => 127, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimmer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 138 => + array ( + 'id' => 139, + 'name' => 'Spa, Wellness & Care', + 'parent_id' => 61, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 139 => + array ( + 'id' => 140, + 'name' => 'Acupuncture', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-slash', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 140 => + array ( + 'id' => 141, + 'name' => 'Aromatic Balneo Therapy', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 141 => + array ( + 'id' => 142, + 'name' => 'Asthma Cave', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 142 => + array ( + 'id' => 143, + 'name' => 'Bio Sauna', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 143 => + array ( + 'id' => 144, + 'name' => 'Steam Bath', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 144 => + array ( + 'id' => 145, + 'name' => 'Steam Bathhouse', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 145 => + array ( + 'id' => 146, + 'name' => 'Steam Room', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 146 => + array ( + 'id' => 147, + 'name' => 'Skin Care', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-allergies', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 147 => + array ( + 'id' => 148, + 'name' => 'Mud Bath', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bath', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 148 => + array ( + 'id' => 149, + 'name' => 'Doctor', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-briefcase-medical', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 149 => + array ( + 'id' => 150, + 'name' => 'Finnish Sauna', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 150 => + array ( + 'id' => 151, + 'name' => 'Fitness Center', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dumbbell', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 151 => + array ( + 'id' => 152, + 'name' => 'Nurse', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-user-md', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 152 => + array ( + 'id' => 153, + 'name' => 'Hydrocolon Therapy', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 153 => + array ( + 'id' => 154, + 'name' => 'Hydromassage Bathtub', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 154 => + array ( + 'id' => 155, + 'name' => 'Jacuzzi', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 155 => + array ( + 'id' => 156, + 'name' => 'Hot Spring', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 156 => + array ( + 'id' => 157, + 'name' => 'Snow Room', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-snowflake', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 157 => + array ( + 'id' => 158, + 'name' => 'Hairdresser', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-cut', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 158 => + array ( + 'id' => 159, + 'name' => 'Laser Therapy', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 159 => + array ( + 'id' => 160, + 'name' => 'Massage Services', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 160 => + array ( + 'id' => 161, + 'name' => 'Ozone Sauna', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 161 => + array ( + 'id' => 162, + 'name' => 'Infirmary', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-clinic-medical', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 162 => + array ( + 'id' => 163, + 'name' => 'Sauna', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 163 => + array ( + 'id' => 164, + 'name' => 'Solarium', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-sun', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 164 => + array ( + 'id' => 165, + 'name' => 'Spa Bar', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 165 => + array ( + 'id' => 166, + 'name' => 'Shock Pool', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 166 => + array ( + 'id' => 167, + 'name' => 'Thalasso Therapy Center', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 167 => + array ( + 'id' => 168, + 'name' => 'Turkish Bath', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 168 => + array ( + 'id' => 169, + 'name' => 'Yoga', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 169 => + array ( + 'id' => 170, + 'name' => 'Seaweed Treatment', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 170 => + array ( + 'id' => 171, + 'name' => 'Effusion Therapy', + 'parent_id' => 139, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 171 => + array ( + 'id' => 172, + 'name' => 'Activities', + 'parent_id' => 61, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 172 => + array ( + 'id' => 173, + 'name' => 'Aerobic', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dumbbell', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 173 => + array ( + 'id' => 174, + 'name' => 'Air Hockey', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hockey-puck', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 174 => + array ( + 'id' => 175, + 'name' => 'Aquaroic', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimmer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 175 => + array ( + 'id' => 176, + 'name' => 'Horse Riding', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-horse', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 176 => + array ( + 'id' => 177, + 'name' => 'Shooting', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bullseye', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 177 => + array ( + 'id' => 178, + 'name' => 'Atv Safari', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-truck-monster', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 178 => + array ( + 'id' => 179, + 'name' => 'Badminton', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-table-tennis', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 179 => + array ( + 'id' => 180, + 'name' => 'Fishing', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fish', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 180 => + array ( + 'id' => 181, + 'name' => 'Balloon', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-circle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 181 => + array ( + 'id' => 182, + 'name' => 'Basketball', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-basketball-ball', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 182 => + array ( + 'id' => 183, + 'name' => 'Beach Football', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-futbol', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 183 => + array ( + 'id' => 184, + 'name' => 'Beach Volleyball', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-volleyball-ball', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 184 => + array ( + 'id' => 185, + 'name' => 'Billiards', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dot-circle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 185 => + array ( + 'id' => 186, + 'name' => 'Bicycle', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bicycle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 186 => + array ( + 'id' => 187, + 'name' => 'Boccia', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'far fa-futbol', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 187 => + array ( + 'id' => 188, + 'name' => 'Bowling', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bowling-ball', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 188 => + array ( + 'id' => 189, + 'name' => 'Ice Skating', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-skating', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 189 => + array ( + 'id' => 190, + 'name' => 'Dance Lessons', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bowling-ball', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 190 => + array ( + 'id' => 191, + 'name' => 'Darts', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bullseye-arrow', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 191 => + array ( + 'id' => 192, + 'name' => 'Pedalo', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bowling-ball', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 192 => + array ( + 'id' => 193, + 'name' => 'Golf', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bicycle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 193 => + array ( + 'id' => 194, + 'name' => 'Air Rifle', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bullseye', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 194 => + array ( + 'id' => 195, + 'name' => 'Pool Games', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 195 => + array ( + 'id' => 196, + 'name' => 'Motorboat', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ship', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 196 => + array ( + 'id' => 197, + 'name' => 'Jeep Safari', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-truck-monster', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 197 => + array ( + 'id' => 198, + 'name' => 'Jet Ski', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-water', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 198 => + array ( + 'id' => 199, + 'name' => 'Gymnastics', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dumbbell', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 199 => + array ( + 'id' => 200, + 'name' => 'Jogging', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-running', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 200 => + array ( + 'id' => 201, + 'name' => 'Canoe', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-water', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 201 => + array ( + 'id' => 202, + 'name' => 'Karaoke', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-microphone', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 202 => + array ( + 'id' => 203, + 'name' => 'Card Games', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-th-large', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 203 => + array ( + 'id' => 204, + 'name' => 'Ski', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-skiing-nordic', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 204 => + array ( + 'id' => 205, + 'name' => 'Kitesurf', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wind', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 205 => + array ( + 'id' => 206, + 'name' => 'Pinball', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-circle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 206 => + array ( + 'id' => 207, + 'name' => 'Ping Pong', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-table-tennis', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 207 => + array ( + 'id' => 208, + 'name' => 'Mini Billiards', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dot-circle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 208 => + array ( + 'id' => 209, + 'name' => 'Mini Football', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'far fa-futbol', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 209 => + array ( + 'id' => 210, + 'name' => 'Mini Golf', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-golf-ball', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 210 => + array ( + 'id' => 211, + 'name' => 'Motorized Water Sports', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ship', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 211 => + array ( + 'id' => 212, + 'name' => 'Banana Boat', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ship', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 212 => + array ( + 'id' => 213, + 'name' => 'Archery', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bullseye', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 213 => + array ( + 'id' => 214, + 'name' => 'Okey Team', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dice', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 214 => + array ( + 'id' => 215, + 'name' => 'Paintball', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ellipsis-h', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 215 => + array ( + 'id' => 216, + 'name' => 'Parasailing', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-parachute-box', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 216 => + array ( + 'id' => 217, + 'name' => 'Parachute', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-parachute-box', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 217 => + array ( + 'id' => 218, + 'name' => 'Pedal Boat', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ship', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 218 => + array ( + 'id' => 219, + 'name' => 'Pilates', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-circle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 219 => + array ( + 'id' => 220, + 'name' => 'Pinball', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-circle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 220 => + array ( + 'id' => 221, + 'name' => 'Rafting', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-water', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 221 => + array ( + 'id' => 222, + 'name' => 'Ringo', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-life-ring', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 222 => + array ( + 'id' => 223, + 'name' => 'Windsurf', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wind', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 223 => + array ( + 'id' => 224, + 'name' => 'Safari', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-compass', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 224 => + array ( + 'id' => 225, + 'name' => 'Chess', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-chess-board', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 225 => + array ( + 'id' => 226, + 'name' => 'Scuba Diving', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimmer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 226 => + array ( + 'id' => 227, + 'name' => 'Surf', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-water', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 227 => + array ( + 'id' => 228, + 'name' => 'Squash', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-table-tennis', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 228 => + array ( + 'id' => 229, + 'name' => 'Step', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-shoe-prints', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 229 => + array ( + 'id' => 230, + 'name' => 'Water Aerobics', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimmer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 230 => + array ( + 'id' => 231, + 'name' => 'Water Skiing', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-water', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 231 => + array ( + 'id' => 232, + 'name' => 'Water Polo', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-volleyball-ball', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 232 => + array ( + 'id' => 233, + 'name' => 'Backgammon', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dice', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 233 => + array ( + 'id' => 234, + 'name' => 'Tennis', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-table-tennis', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 234 => + array ( + 'id' => 235, + 'name' => 'Climb', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-mountain', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 235 => + array ( + 'id' => 236, + 'name' => 'Trampolin', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-circle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 236 => + array ( + 'id' => 237, + 'name' => 'Trekking', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hiking', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 237 => + array ( + 'id' => 238, + 'name' => 'Volleyball', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-volleyball-ball', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 238 => + array ( + 'id' => 239, + 'name' => 'Paragliding', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-parachute-box', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 239 => + array ( + 'id' => 240, + 'name' => 'Sailing', + 'parent_id' => 172, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wind', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 240 => + array ( + 'id' => 241, + 'name' => 'Entertainment', + 'parent_id' => 61, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 241 => + array ( + 'id' => 242, + 'name' => 'Outdoor Activities', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-sun', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 242 => + array ( + 'id' => 243, + 'name' => 'Amphitheatre', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-theater-masks', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 243 => + array ( + 'id' => 244, + 'name' => 'Animation Team', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-crown', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 244 => + array ( + 'id' => 245, + 'name' => 'Beach Party', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-umbrella-beach', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 245 => + array ( + 'id' => 246, + 'name' => 'Bowling Alley', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bowling-ball', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 246 => + array ( + 'id' => 247, + 'name' => 'Live Entertainment', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-theater-masks', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 247 => + array ( + 'id' => 248, + 'name' => 'Live music', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-music', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 248 => + array ( + 'id' => 249, + 'name' => 'Dance Performances', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-music', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 249 => + array ( + 'id' => 250, + 'name' => 'Entertainment Programs', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-theater-masks', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 250 => + array ( + 'id' => 251, + 'name' => 'Night club', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-glass-cheers', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 251 => + array ( + 'id' => 252, + 'name' => 'Night Shows', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-moon', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 252 => + array ( + 'id' => 253, + 'name' => 'Zoo', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-paw', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 253 => + array ( + 'id' => 254, + 'name' => 'Foam party', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-glass-cheers', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 254 => + array ( + 'id' => 255, + 'name' => 'Casino', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coins', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 255 => + array ( + 'id' => 256, + 'name' => 'Culture Show', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-theater-masks', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 256 => + array ( + 'id' => 257, + 'name' => 'Fun fair', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dot-circle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 257 => + array ( + 'id' => 258, + 'name' => 'Beach Parties', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-umbrella-beach', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 258 => + array ( + 'id' => 259, + 'name' => 'Pub', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-beer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 259 => + array ( + 'id' => 260, + 'name' => 'Cinema', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-film', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 260 => + array ( + 'id' => 261, + 'name' => 'Circus Acrobatics', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-campground', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 261 => + array ( + 'id' => 262, + 'name' => 'Soft Animation', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-crow', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 262 => + array ( + 'id' => 263, + 'name' => 'Boat Tour', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ship', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 263 => + array ( + 'id' => 264, + 'name' => 'Themed Nights', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-mask', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 264 => + array ( + 'id' => 265, + 'name' => 'Yacht tour', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ship', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 265 => + array ( + 'id' => 266, + 'name' => 'Sailboat', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-ship', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 266 => + array ( + 'id' => 267, + 'name' => 'Cooking Course', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 267 => + array ( + 'id' => 268, + 'name' => 'Theater Show', + 'parent_id' => 241, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-theater-masks', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 268 => + array ( + 'id' => 269, + 'name' => 'Kids & Baby', + 'parent_id' => 61, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 269 => + array ( + 'id' => 270, + 'name' => 'Sitter', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-baby', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 270 => + array ( + 'id' => 271, + 'name' => 'Children\'s Buffet', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tint', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 271 => + array ( + 'id' => 272, + 'name' => 'Children\'s Disco', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 272 => + array ( + 'id' => 273, + 'name' => 'Kiddy Pool ', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bicycle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 273 => + array ( + 'id' => 274, + 'name' => 'Child Cot', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 274 => + array ( + 'id' => 275, + 'name' => 'Children\'s park', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-child', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 275 => + array ( + 'id' => 276, + 'name' => 'Stroller', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-baby-carriage', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 276 => + array ( + 'id' => 277, + 'name' => 'Baby cot', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 277 => + array ( + 'id' => 278, + 'name' => 'Baby Bathtub', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bath', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 278 => + array ( + 'id' => 279, + 'name' => 'Baby Food Sales', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-cookie', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 279 => + array ( + 'id' => 280, + 'name' => 'Baby Milk Sales', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tint', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 280 => + array ( + 'id' => 281, + 'name' => 'Cradle', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 281 => + array ( + 'id' => 282, + 'name' => 'Bottle Sterilization', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tint', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 282 => + array ( + 'id' => 283, + 'name' => 'Baby Care Area in Public Toilets', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-baby', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 283 => + array ( + 'id' => 284, + 'name' => 'Water Heater For Baby Formula', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tint', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 284 => + array ( + 'id' => 285, + 'name' => 'Mini Club', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-child', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 285 => + array ( + 'id' => 286, + 'name' => 'Baby Chair', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-chair', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 286 => + array ( + 'id' => 287, + 'name' => 'Children\'s Programs', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-child', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 287 => + array ( + 'id' => 288, + 'name' => 'Children\'s Menu', + 'parent_id' => 269, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-cookie', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 288 => + array ( + 'id' => 289, + 'name' => 'Room', + 'parent_id' => NULL, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 289 => + array ( + 'id' => 290, + 'name' => 'Common Amenities of Rooms', + 'parent_id' => 289, + 'type' => 0, + 'order_number' => 0, + 'icon' => '', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 290 => + array ( + 'id' => 291, + 'name' => '220 Volts', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-plug', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 291 => + array ( + 'id' => 292, + 'name' => 'Outdoor Dining Table', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-sun', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 292 => + array ( + 'id' => 293, + 'name' => 'Wood / Parquet Flooring', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-square', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 293 => + array ( + 'id' => 294, + 'name' => 'Alarm', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bell', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 294 => + array ( + 'id' => 295, + 'name' => 'Antiallergic', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spray-can', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 295 => + array ( + 'id' => 296, + 'name' => 'Mirror', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-square', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 296 => + array ( + 'id' => 297, + 'name' => 'Separate Bedroom', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 297 => + array ( + 'id' => 298, + 'name' => 'Connecting Rooms', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spray-can', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 298 => + array ( + 'id' => 299, + 'name' => 'Garden View', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tree', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 299 => + array ( + 'id' => 300, + 'name' => 'Balcony', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-border-all', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 300 => + array ( + 'id' => 301, + 'name' => 'Bathroom', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bath', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 301 => + array ( + 'id' => 302, + 'name' => 'Bathroom Products', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bath', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 302 => + array ( + 'id' => 303, + 'name' => 'Telephone in Bathroom', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-phone', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 303 => + array ( + 'id' => 304, + 'name' => 'BBQ', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hamburger', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 304 => + array ( + 'id' => 305, + 'name' => 'Baby Table', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-baby', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 305 => + array ( + 'id' => 306, + 'name' => 'Bidet', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-toilet', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 306 => + array ( + 'id' => 307, + 'name' => 'Computer', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-laptop', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 307 => + array ( + 'id' => 308, + 'name' => 'Blu-ray player', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-compact-disc', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 308 => + array ( + 'id' => 309, + 'name' => 'Bosphorus View', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-water', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 309 => + array ( + 'id' => 310, + 'name' => 'Bathrobe', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-washer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 310 => + array ( + 'id' => 311, + 'name' => 'Dishwasher', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-washer', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 311 => + array ( + 'id' => 312, + 'name' => 'Refrigerator', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-temperature-low', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 312 => + array ( + 'id' => 313, + 'name' => 'CD Player', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-compact-disc', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 313 => + array ( + 'id' => 314, + 'name' => 'Alarm Clock', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-clock', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 314 => + array ( + 'id' => 315, + 'name' => 'Study Desk', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-pencil-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 315 => + array ( + 'id' => 316, + 'name' => 'Clothes Dryer ', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tshirt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 316 => + array ( + 'id' => 317, + 'name' => 'Washing machine', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-dryer-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 317 => + array ( + 'id' => 318, + 'name' => 'Drawers', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tshirt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 318 => + array ( + 'id' => 319, + 'name' => 'Coffee / Tea Maker', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coffee', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 319 => + array ( + 'id' => 320, + 'name' => 'Mountain View ', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-mountain', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 320 => + array ( + 'id' => 321, + 'name' => 'Sea View', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-water', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 321 => + array ( + 'id' => 322, + 'name' => 'Direct Dial Telephone', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-phone', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 322 => + array ( + 'id' => 323, + 'name' => 'Laptop', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-laptop', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 323 => + array ( + 'id' => 324, + 'name' => 'Landscape', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tree', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 324 => + array ( + 'id' => 325, + 'name' => 'Smoke Alarm', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bell', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 325 => + array ( + 'id' => 326, + 'name' => 'Shower', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-shower', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 326 => + array ( + 'id' => 327, + 'name' => 'Flat Screen Tv', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tv', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 327 => + array ( + 'id' => 328, + 'name' => 'DVD Player', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-compact-disc', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 328 => + array ( + 'id' => 329, + 'name' => 'Toaster', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bread-slice', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 329 => + array ( + 'id' => 330, + 'name' => 'Extra Bed', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 330 => + array ( + 'id' => 331, + 'name' => 'Warming Pad', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 331 => + array ( + 'id' => 332, + 'name' => 'Electric Water Heater', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coffee', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 332 => + array ( + 'id' => 333, + 'name' => 'Extra Long Bed (> 2 mt)', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 333 => + array ( + 'id' => 334, + 'name' => 'Fan', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fan', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 334 => + array ( + 'id' => 335, + 'name' => 'Fax', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fax', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 335 => + array ( + 'id' => 336, + 'name' => 'Owen', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fire', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 336 => + array ( + 'id' => 337, + 'name' => 'Widescreen Tv', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tv', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 337 => + array ( + 'id' => 338, + 'name' => 'Lake View ', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-water', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 338 => + array ( + 'id' => 339, + 'name' => 'Carpeted Floor', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-square', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 339 => + array ( + 'id' => 340, + 'name' => 'Air / Water Filter', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-filter', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 340 => + array ( + 'id' => 341, + 'name' => 'Pool View', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 341 => + array ( + 'id' => 342, + 'name' => 'Heating', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fire', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 342 => + array ( + 'id' => 343, + 'name' => 'Light / Noise / Heat Insulated Curtains', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-lightbulb', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 343 => + array ( + 'id' => 344, + 'name' => 'Second Bathroom', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bath', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 344 => + array ( + 'id' => 345, + 'name' => 'Internet Connection', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wifi', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 345 => + array ( + 'id' => 346, + 'name' => 'iPad', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-mobile-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 346 => + array ( + 'id' => 347, + 'name' => 'iPod Docking Station', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-mobile-alt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 347 => + array ( + 'id' => 348, + 'name' => 'Jacuzzi', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hot-tub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 348 => + array ( + 'id' => 349, + 'name' => 'Cable Channels', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tv', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 349 => + array ( + 'id' => 350, + 'name' => 'Coffee machine', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coffee', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 350 => + array ( + 'id' => 351, + 'name' => 'Sofa', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-couch', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 351 => + array ( + 'id' => 352, + 'name' => 'Clothes Cabinet', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tshirt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 352 => + array ( + 'id' => 353, + 'name' => 'Air Conditioner', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wind', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 353 => + array ( + 'id' => 354, + 'name' => 'Dryer', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tshirt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 354 => + array ( + 'id' => 355, + 'name' => 'Tub', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bath', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 355 => + array ( + 'id' => 356, + 'name' => 'Make up Desk', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fab fa-sketch', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 356 => + array ( + 'id' => 357, + 'name' => 'Massage Service', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-spa', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 357 => + array ( + 'id' => 358, + 'name' => 'Central Heating', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-thermometer-three-quarters', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 358 => + array ( + 'id' => 359, + 'name' => 'Marble Floor', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-square', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 359 => + array ( + 'id' => 360, + 'name' => 'Microwave Oven', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fire', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 360 => + array ( + 'id' => 361, + 'name' => 'Mini Bar', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wine-bottle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 361 => + array ( + 'id' => 362, + 'name' => 'Mini Refrigerator', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-temperature-low', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 362 => + array ( + 'id' => 363, + 'name' => 'Kitchenette', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 363 => + array ( + 'id' => 364, + 'name' => 'Kitchen', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 364 => + array ( + 'id' => 365, + 'name' => 'Kitchenware', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 365 => + array ( + 'id' => 366, + 'name' => 'Music Player', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-music', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 366 => + array ( + 'id' => 367, + 'name' => 'River View', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-water', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 367 => + array ( + 'id' => 368, + 'name' => 'Furnace', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fire', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 368 => + array ( + 'id' => 369, + 'name' => 'Pool in the Room', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-couch', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 369 => + array ( + 'id' => 370, + 'name' => 'Sitting Group', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-couch', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 370 => + array ( + 'id' => 371, + 'name' => 'Game Console', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-gamepad', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 371 => + array ( + 'id' => 372, + 'name' => 'Paid Movies', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tv', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 372 => + array ( + 'id' => 373, + 'name' => 'Private Entrance', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-door-open', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 373 => + array ( + 'id' => 374, + 'name' => 'Private Pool', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-swimming-pool', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 374 => + array ( + 'id' => 375, + 'name' => 'Radio', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-music', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 375 => + array ( + 'id' => 376, + 'name' => 'Clock', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-clock', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 376 => + array ( + 'id' => 377, + 'name' => 'Hair Dryer', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wind', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 377 => + array ( + 'id' => 378, + 'name' => 'Safe Box', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-square', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 378 => + array ( + 'id' => 379, + 'name' => 'Sound insulation', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 379 => + array ( + 'id' => 380, + 'name' => 'Mosquito Wire', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-hashtag', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 380 => + array ( + 'id' => 381, + 'name' => 'Dressing Room', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-restroom', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 381 => + array ( + 'id' => 382, + 'name' => 'Split Air Conditioner', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wind', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 382 => + array ( + 'id' => 383, + 'name' => 'Local / International Calling', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-phone', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 383 => + array ( + 'id' => 384, + 'name' => 'Bottled Water', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-wine-bottle', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 384 => + array ( + 'id' => 385, + 'name' => 'Fireplace', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fire', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 385 => + array ( + 'id' => 386, + 'name' => 'Telephone', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-phone', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 386 => + array ( + 'id' => 387, + 'name' => 'Television', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tv', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 387 => + array ( + 'id' => 388, + 'name' => 'Cleaning Products', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-broom', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 388 => + array ( + 'id' => 389, + 'name' => 'Slipper', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-shoe-prints', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 389 => + array ( + 'id' => 390, + 'name' => 'Turkish Coffee Machine', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-coffee', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 390 => + array ( + 'id' => 391, + 'name' => 'Satellite Channels', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tv', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 391 => + array ( + 'id' => 392, + 'name' => 'Iron', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tshirt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 392 => + array ( + 'id' => 393, + 'name' => 'Ironing Facilities', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-tshirt', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 393 => + array ( + 'id' => 394, + 'name' => 'Fan', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-fan', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 394 => + array ( + 'id' => 395, + 'name' => 'Porch / Courtyard', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-square', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 395 => + array ( + 'id' => 396, + 'name' => 'Video Player', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-compact-disc', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 396 => + array ( + 'id' => 397, + 'name' => 'Pillow Menu', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-bed', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 397 => + array ( + 'id' => 398, + 'name' => 'Dining Areas', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 398 => + array ( + 'id' => 399, + 'name' => 'Dinner Table', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 399 => + array ( + 'id' => 400, + 'name' => 'Dinner Set', + 'parent_id' => 290, + 'type' => 1, + 'order_number' => 0, + 'icon' => 'fas fa-utensils', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + )); + + + } +} diff --git a/database/seeds/PropertyGoogleLabelSeeder.php b/database/seeds/PropertyGoogleLabelSeeder.php new file mode 100644 index 0000000..f1a60b4 --- /dev/null +++ b/database/seeds/PropertyGoogleLabelSeeder.php @@ -0,0 +1,123 @@ +delete(); + + \DB::table('photo_google_label')->insert(array ( + 0 => + array ( + 'id' => 1, + 'label' => 'Resort', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 1 => + array ( + 'id' => 2, + 'label' => 'Building', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 2 => + array ( + 'id' => 3, + 'label' => 'Property', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 3 => + array ( + 'id' => 4, + 'label' => 'Hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 4 => + array ( + 'id' => 5, + 'label' => 'Home', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 5 => + array ( + 'id' => 6, + 'label' => 'Bar', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 6 => + array ( + 'id' => 7, + 'label' => 'Pub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 7 => + array ( + 'id' => 8, + 'label' => 'Disco', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 8 => + array ( + 'id' => 9, + 'label' => 'Tavern', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 9 => + array ( + 'id' => 10, + 'label' => 'Nightclub', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + )); + + + } +} diff --git a/database/seeds/PropertyPhotoSeeder.php b/database/seeds/PropertyPhotoSeeder.php new file mode 100644 index 0000000..2158132 --- /dev/null +++ b/database/seeds/PropertyPhotoSeeder.php @@ -0,0 +1,263 @@ +delete(); + + \DB::table('property_photo')->insert(array ( + 0 => + array ( + 'id' => 1, + 'property_id' => 1, + 'property_photo_category_id' => 2, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571403990.jpg', + 'photo_name' => '1571403990.jpg', + 'photo_rank' => NULL, + 'file_size' => '224086', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1161', + 'photo_order' => 1, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 0, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571404371, + ), + 1 => + array ( + 'id' => 2, + 'property_id' => 1, + 'property_photo_category_id' => 2, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571404099.jpg', + 'photo_name' => '1571404099.jpg', + 'photo_rank' => NULL, + 'file_size' => '234013', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1159', + 'photo_order' => 2, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 0, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571404370, + ), + 2 => + array ( + 'id' => 3, + 'property_id' => 1, + 'property_photo_category_id' => 9, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571404388.jpg', + 'photo_name' => '1571404388.jpg', + 'photo_rank' => NULL, + 'file_size' => '195845', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1171', + 'photo_order' => 3, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 3 => + array ( + 'id' => 4, + 'property_id' => 1, + 'property_photo_category_id' => 11, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571404427.jpg', + 'photo_name' => '1571404427.jpg', + 'photo_rank' => NULL, + 'file_size' => '164885', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1160', + 'photo_order' => 4, + 'is_default' => 1, + 'is_temp' => 1, + 'status' => 0, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571407419, + ), + 4 => + array ( + 'id' => 5, + 'property_id' => 1, + 'property_photo_category_id' => 6, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571404449.jpg', + 'photo_name' => '1571404449.jpg', + 'photo_rank' => NULL, + 'file_size' => '189233', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1197', + 'photo_order' => 5, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1, + ), + 5 => + array ( + 'id' => 6, + 'property_id' => 1, + 'property_photo_category_id' => 2, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571407195.jpg', + 'photo_name' => '1571407195.jpg', + 'photo_rank' => NULL, + 'file_size' => '167280', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1134', + 'photo_order' => 6, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571407198, + ), + 6 => + array ( + 'id' => 7, + 'property_id' => 1, + 'property_photo_category_id' => 11, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571407199.jpg', + 'photo_name' => '1571407199.jpg', + 'photo_rank' => NULL, + 'file_size' => '195845', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1171', + 'photo_order' => 7, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571407201, + ), + 7 => + array ( + 'id' => 8, + 'property_id' => 1, + 'property_photo_category_id' => 2, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571407202.jpg', + 'photo_name' => '1571407202.jpg', + 'photo_rank' => NULL, + 'file_size' => '224086', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1161', + 'photo_order' => 8, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571407204, + ), + 8 => + array ( + 'id' => 9, + 'property_id' => 1, + 'property_photo_category_id' => 11, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571407206.jpg', + 'photo_name' => '1571407206.jpg', + 'photo_rank' => NULL, + 'file_size' => '164885', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1160', + 'photo_order' => 9, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571407208, + ), + 9 => + array ( + 'id' => 10, + 'property_id' => 1, + 'property_photo_category_id' => 2, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571407209.jpg', + 'photo_name' => '1571407209.jpg', + 'photo_rank' => NULL, + 'file_size' => '234013', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1159', + 'photo_order' => 10, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571407211, + ), + 10 => + array ( + 'id' => 11, + 'property_id' => 1, + 'property_photo_category_id' => 9, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571407212.jpg', + 'photo_name' => '1571407212.jpg', + 'photo_rank' => NULL, + 'file_size' => '246154', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1324', + 'photo_order' => 11, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571407214, + ), + 11 => + array ( + 'id' => 12, + 'property_id' => 1, + 'property_photo_category_id' => 11, + 'photo_path' => '/srv/api.gextranet.com/public/property-photos/1/1571407216.jpg', + 'photo_name' => '1571407216.jpg', + 'photo_rank' => NULL, + 'file_size' => '151624', + 'file_ext' => 'jpg', + 'photo_resolution' => '1740x1160', + 'photo_order' => 12, + 'is_default' => 0, + 'is_temp' => 1, + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 1, + 'updated_at' => 1571407218, + ), + )); + + + } +} diff --git a/database/seeds/PropertyTypeSeeder.php b/database/seeds/PropertyTypeSeeder.php new file mode 100644 index 0000000..2047745 --- /dev/null +++ b/database/seeds/PropertyTypeSeeder.php @@ -0,0 +1,153 @@ +delete(); + + \DB::table('property_type')->insert(array ( + 0 => + array ( + 'id' => 1, + 'name' => '1 Stars Hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 1 => + array ( + 'id' => 2, + 'name' => '2 Stars Hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 2 => + array ( + 'id' => 3, + 'name' => '3 Stars Hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 3 => + array ( + 'id' => 4, + 'name' => '4 Stars Hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 4 => + array ( + 'id' => 5, + 'name' => '5 Stars Hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 5 => + array ( + 'id' => 6, + 'name' => 'Boutique hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 6 => + array ( + 'id' => 7, + 'name' => 'Apart Hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 7 => + array ( + 'id' => 8, + 'name' => 'Residence', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 8 => + array ( + 'id' => 9, + 'name' => 'Hostel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 9 => + array ( + 'id' => 10, + 'name' => 'Resort', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 10 => + array ( + 'id' => 11, + 'name' => 'Thermal Facility', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 11 => + array ( + 'id' => 12, + 'name' => 'Motel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + 12 => + array ( + 'id' => 13, + 'name' => 'Suit Hotel', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => 0, + 'updated_at' => 0, + ), + )); + + + } +} diff --git a/database/seeds/UserTableSeeder.php b/database/seeds/UserTableSeeder.php new file mode 100644 index 0000000..26feb50 --- /dev/null +++ b/database/seeds/UserTableSeeder.php @@ -0,0 +1,54 @@ +insertGetId([ + 'id' => 1, + 'gender' => 'M', + 'name' => 'Test', + 'surname' => 'User -1-', + 'email' => 'test1@rezervasyon.com', + 'password' => Hash::make('123456'), + 'phone' => '02123668989', + 'user_type' => 1, + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + + + ]); + DB::table('user')->insertGetId( + [ + 'id' => 2, + 'gender' => 'M', + 'name' => 'Test', + 'surname' => 'User -2-', + 'email' => 'test2@rezervasyon.com', + 'password' => Hash::make('1234567'), + 'phone' => '02123668989', + 'user_type' => 0, + 'locale' => 'en', + 'status' => 1, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => time(), + 'updated_at' => time(), + ] + + ); + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4801ccd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,58 @@ +version: "3.8" + +services: + app: + build: + context: . + dockerfile: Dockerfile + container_name: extranetwork_app + restart: unless-stopped + working_dir: /var/www/html + volumes: + - .:/var/www/html + - extranetwork_uploads_data:/home/uploads + ports: + - "8073:80" + depends_on: + - mysql + networks: + - extranetwork_network + + mysql: + image: mysql:8.0 + container_name: extranetwork_mysql + restart: unless-stopped + ports: + - "3307:3306" + environment: + MYSQL_DATABASE: extranetwork_api + MYSQL_ROOT_PASSWORD: "144652qweBk??" + command: --default-authentication-plugin=mysql_native_password + volumes: + - extranetwork_mysql_data:/var/lib/mysql + networks: + - extranetwork_network + + phpmyadmin: + image: phpmyadmin/phpmyadmin + container_name: extranetwork_phpmyadmin + restart: unless-stopped + ports: + - "8081:80" + environment: + PMA_HOST: mysql + PMA_PORT: 3306 + PMA_USER: root + PMA_PASSWORD: "144652qweBk??" + depends_on: + - mysql + networks: + - extranetwork_network + +volumes: + extranetwork_mysql_data: + extranetwork_uploads_data: + +networks: + extranetwork_network: + driver: bridge diff --git a/php b/php new file mode 100644 index 0000000..e69de29 diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..8ef59e8 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,26 @@ + + + + + ./tests + + + + + ./app + + + + + + + + diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..4be9f04 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,21 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Handle Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058432_BU4ZCOX3BL.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058432_BU4ZCOX3BL.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..9243190e1916cf45fd931f046a75ab536a810834 GIT binary patch literal 63842 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058432_BU4ZCOX3BL_medium.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058432_BU4ZCOX3BL_medium.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..86c36082c9a38d8633d3c6bd49d9dc81873df427 GIT binary patch literal 47416 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

&Q4^+n=WlZ+B5cJ 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058432_BU4ZCOX3BL_thumbnail.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058432_BU4ZCOX3BL_thumbnail.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..c0cd734e588d951acfe6176b638d67315038134a GIT binary patch literal 13272 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058498_KXQFEAR72N.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058498_KXQFEAR72N.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..9243190e1916cf45fd931f046a75ab536a810834 GIT binary patch literal 63842 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058498_KXQFEAR72N_medium.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058498_KXQFEAR72N_medium.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..86c36082c9a38d8633d3c6bd49d9dc81873df427 GIT binary patch literal 47416 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

&Q4^+n=WlZ+B5cJ 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058498_KXQFEAR72N_thumbnail.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058498_KXQFEAR72N_thumbnail.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..c0cd734e588d951acfe6176b638d67315038134a GIT binary patch literal 13272 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058692_2PTNCQ6D0Y.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058692_2PTNCQ6D0Y.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..9243190e1916cf45fd931f046a75ab536a810834 GIT binary patch literal 63842 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058692_2PTNCQ6D0Y_medium.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058692_2PTNCQ6D0Y_medium.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..86c36082c9a38d8633d3c6bd49d9dc81873df427 GIT binary patch literal 47416 zcmeEu2|Sel+V=66Rk*NJ31OOeKVD*|(WemJlHnG1+&MJz2)Sgk;~B zvF~QA!)))}d7k%q&pH3+InQ~Yb55W4Iq!qp$NbE4U-$3&UEk~by{_L)9i~o$PHJmt zX@F>GXh08uZxEFNx~gWNe(kQFfznx<#~1CKt!&PENL{*oR>a-a*3ssymbT_u?K|q4 zXFcSlMJ3L*jq{lr69@tV(a-~?{o`*yQKfQM!Z5loC2ua5!=#Cf( zf*zxS#vG|2B6RN_A!w}pgBukjh^$M}+(kDr9o{NHZ0L33r8cK@+C)-A_w;0e-=Kfy zgAxu{M-W)O9xC$b$S*p;AE=<|)Y14oc=PBV$5KL2MvplN`0wZ{&B z{L0lc?L-iFM9+JH>wF$K+Vp3A5no|D`-uvgUBF0-Z&E=7agVYELTjDUou>y}3fzJ| zPEjx_=rQ`a`08h-VJ|A^zFtyu)!;*Q4w!cvhrT-KpZP#)0M_}j?xv`7+@bkJ2g4ka z#K~<==_RM1FJg(xvgN`@E!``jf-Z34$-ay06KE=^L5*lkIJ`5KR$dv}q-4Hch`Ecj)CLi0gLrw>hn z=_2LaYCG16#w+sPvm?W{L)^-T=}+qr?|Q2~CzfYnxm(^KDQuBp$x;IqPTW3aou87Y z+lI;F;aNK3-Qw*6T%JN7A`SEmbrF2Xx ze+3sUlyKSk-zvB{QuaySyD4UQ*6rMgBpDw=c4>Jjoj#%coiX!=1xna|~O_@6z zsw-{V4b#&A<0K3_A~j;9d_g{Z)~j^B{u?qJN5qp=OXryj9pjFRM4xmy^Nt0&8ed?_6}K@;OjXxR1kjAZf9$08-{u8 zJZoiVyy4dfocaJiNMD`fpZ%aApVC+7#Ayx&a_Ij?{|k;|ovb=iG{3bAmmz3KPw+1I z#bz}3DlsgBlH(%#mP1k&$_8H`gclu+%(Je~E%eTK->p`FxILb75)uc#v!O1K}j zM&H);Jup`lB}EEAB!kWaHf-|W{Ga*y`_Can2wAUN{X|)gDA%1Ku#URlf3}o)JhdWW z>JUU)-D0byf`IT>N|;(#mBhhbrNxNb+FVGJe%X`m{~FaGPgJB0pZprXeB?FT^w~?HK4GEeS^e3*roeTC5CnX_{=$A;^WbuVEr2FOTQViV6sTCTa2 z_on0H%Bc5qaUK$S3wDA2U5Z=RG>f)M;$=Tp0X7-`&r17yM@qj|0K7Qy`=oQwk_(mg zyor}~+f(}lKwCzMZYaV^6k(_@_87bvu^``LxhGS zr;Wy5X03}i`%z{S*;Ta-Z0Z2)^?$Jq0P!#59sgb`@THc{YsSy9Eh3D zgR)_Y5IkP4^>JG>vnJIFFedQLhrBi`YtYRYcs%D$1&Kp^#)%~1G^%GFFzF<7`$v@Q z7tob6Z>NtXvkT}a7YS>uI)`uyR^L)L?MR}(F&fM|p#}_S{KDya?H9;|sZF5WLG$?kG z3gU+@X+sFoK&+U3hit5&C?kKogW|00si0PIaK1jpeLqdl5_SL|_ghr=KTQRl*eBqr zpzC)4!(suUhPO~=CTpz#uP}`cP90Kyi0#R+jlNyJsxWpgUi#$CQ<}n`RB~f|3_J{A zyX+bI;$JXdw(LYw%VmC-}GhHTGdp>&Dcomihtl6 zo-1LG)|<81Fwx#Hf~U+|=05oplARiQNZa}sr{oYrVT`4Mgs<6BK@So!b7fnB{%|UY z?i&>p^Qn{y`m8&(Wjf!CJc0-!AZJ?p$$o%rHKUiqL>QwmB?98!K_+t85klq`@{fU^ zLDm~0=cZ)4i7bs&Q0iP^r!XG7;`#{rQc`Wp1JK4xRUiC!4^Y1*2#M9EWt%N4`FHJ( z=dl!&>dF=?sH>kAq#St;y$T=+h8y8@oOGoarOXGrqpqTqFNVbLk@B`vYruOi2q`Z3 z$XRfxGE7D)bPrGI3`fi{c{E}6+aMA4-Qw$)`)l5a)E$HlsAOmYx}A7>(_fks@fAQ@ zzW^g8(PxnZfErcw&^OLqB#B*|ayy`pQm=q)w~yx}F&>`5QbA!5!`&@lnqRA?i_ba{ z#GFWp3qV=r;TPnNdF-C9p97Fh>H?1C*9Tow08_%Et1hEjgLArGQ;yew+ed+-!jFUJ z|IL-eUnA@u=uw!i;NSYm5SU$3P4(vOD59fgNuGCaG>5&FO8X7x(Hok_awzty1?sDDYXzn(JmAYyelDQ z<$A6B&q^8<)Lv_g+B*|W1*x~g9}l%m;qFcyD0InC8l9=2rAqMb3b&`b>z&w1?)W53u(H(SA$Cl+N=LxC@<>JLn-kBw`*%1pggCo|KlMKI z(ByuYHDOord1S;)$J0}WF5=k9sw)2HH0^S`t@V+F3-L{QA$P`BR7H}(^tJ||Nu%Bm zlBf03)dH!Y7bpGmHlZU`d#X6t+Gd8@#e#gF)=(2E*Mf-)=E*a1S6I<~G2;pQp$BUk z)?@Q&`^jCrOr`_G$aj7WK=AA*s9+V-{0;i<;B}|#ZRMC#qZUUM_rm?P(2sgB*2q95 zx`z_A5HEkwrcl65r2Nqu>Ci19i+qZx%#Sx>E~6qda|ChR;gdK895mQe4tsam`GLQx z>AashQGmpczu{Pc0qD;bWMpuQKKQdD_@9zc(_QW)1${B+7{@RE&0 zFIfm@wyo9YaEEe?;&Mb(vPYaoB2Z&&<^>FM>X>*zBt1CGh< zc=_FuVy*UE^NP*B9ab6&P)GMu^!|OsOUQa?^+o*b^g{eBpI+0~!X$4`iJr2We3y53 zBJMtjN*=(KA=&_EyMmb;@Vi0Bjc_2saR8M@Ne)Ucv|KpC z`jvbnR>wEnc+(_78b;Q(I?wCm^us1$vwrr%qOhr{;Il{0J^I@3+$(Y2LCT`}KtB0W z>xWxM0o>r@dc^2=0fCC?tG+{{NCwPwT(TIR!x>H>n!Ic@M!zJZY>32sWGP~X;sEz2 zag$MgoH*{^bQK!D-tgEejx2?DY>$sCJlpUl9R=;6Z|0ys+%`4nK?oVtwJ7xupcK8% zP#AY{uzD%YszyEZINI!uH8xWMIdKLc7<}kE6$EBn_pNeq$n$CiUJ+?e+qWNSHjJwd zXDG6S4}NEGku6GN^4*`8VKPU73!~w3I8)tiZ=9Y#H!`udTyM*PI7KoXxaf|4oh)8k zR1wtMGJCf&dUEnugP_Z4vq!lRAm$g%&-#}iOSf&bN;DN6COG(iHP(Vi05zm?KcVRf z*H)j2+DPoW*y_FhMa78G-t!y&ge8@3&Lva{a&2e^KC0Ftp0Tv#Ae;BBAlt6@oRrx) zC}BIqQ~QOs=i{4PL}K2`6g{O5O0RsHjRp%} zuk3eKEpqNF@rMPH;%Y-vYdtchYhd{WOTL#I4T9RH*Hn*?LJhw2b~A(9i&k+;!M zw#v*E2nfMrS5$o8i@C8l&z%Z-{a8$IhFfk*-+Z#ycOr31vuHc`4Q3hzr&qS&Nc59F zWNAEcN!B;$RSp&8Hjl)Q9*EB>kl3c0RZmxZ>Gz|}SD&%JS?{#AS~NE?OODy+1)N*% z@U^CzgCmI4DD9AaXGVj@!$^-Zx#;8I-sr+odrQs%!6lI^u@vE1qujIAm0F z&j=T}eg->ouD_s956>YLWmV9CstGKr^Rx8v$stL>BjDV%Y+k|Bs0b&L$Aqocp?*Gya~eq{Wf}}pK^|eIwwq8|uwxA!b zR@LO>o|W=TiwFqBAf|Mpr`e2=DHPv>dd_LD(~EYmB0P0l)!IbcJcu$IYk3^i{Yk$a zody)f9&Cp2pjwm6u2AByCQxp_Q?chqey0p3D%(n3 zihLA5x_y>gX<~FE*)YOm?~Q*vzRTAL-=E$5Y-MD@py77f&<#IUoF#=ZYIcgZ`DofK zbDQ#+%r7i^whW0rjpX{RtQpsJIGn&tVeG=$*GJtLN>Cqqy~!PiGOiTT+Iu6P4Y^~# z!X&=9GHX9TG`7$z(qq;}=^Q5eW>UJL$KBr3$=8B#Yc4OIpV58Yz3@%L3RBmw=4@2E=?wNF3m++&Y;7nAe_!74Iy{X7fm~9PS62nAi(7K z-c$c$SKJQ+5I-j%Y&tlsmfkC62g+sp#zty^`Tf=pbM$_i-!rKGIBe71d$Sc9V8Kt= zoj$Y4V!!uo@)1{QHSK;}%!BnPy`mK*)_SxK%lo^T>-rN2VV0-77tk!H2^ZDf;T*NR zc2kUg{`mwwvyfI~=J!X|EnMm=#gasc%bJ6gribtPV|%7c`$~Wm>-~S4_x&e%A$X|) zAOHcXrGJT2{H_1PcaILu@*?R}=?P&LCfDs^t`90b5sq;^AA21-F^`#+BRIGy#~ZfG zet#UNacQGf==BxBp%GvX3^8w}aEX3KWjJ}1Dp|5oobr}~g!Y)`I$SrG>@V{(fy17s zt>w6?jBFFWe%zHP|5-XpclnRXE&n$Dd&+!p{m&ERXET@=P&Gd&-f{&|3sevB!u^52 zXGFE_XaEXwFk4OuHsl~6p!7V0S+f&xyH|>!_3znfyYJ`y z0I%r!y#WG434!}ebDp1m|9YcRmF+0(J;w9J!Z+yU=TuM)Jhg25N`%mN#J8&#$$GBz z>Kdv&8$TzQWA{ktcRVS>A7g%ykMrN>VO$9h77Dj02=ezLy|lk`!>mF`doyTkGd{G=NM|%`jMz z^8&5ws`x>TQ`@l}W!`f$pHEGc{$`s%%|&Ovhj3oR)Ksx9rWwgNPMB>p)=s|M`%qH( zPUeXe&`Q=o|KpeAM^wbue6G{bvA*@?8I3j3PxyE%*YM4~FKra}586d^44&<9Dw;2DhCaxTDy@9j4vWalL1atjO7=Imzu zEVLSf!~A@)7@84g46A({&&A5`{Cn6(6FcpTxJ*F((;YK*f_0&AcQ!eZ4N^o2uVgv@ z7`ta<%gf&xC&Ws?#Ce{ARZImHR5x+d`gOH+MXzW*m%QsO=;o&{WcxYD*z8^vPO~)< zMDV?ayT3az6oVLo*}FXbF)em7$!Du|3$4ij0avPH|*eNbh=>SrS=! zL}BwDvU=7LIV`vHK4G#U24xW(d_BHA;q~rRJ@};(xDFLO>KRxna#!=n(aNu4bn%9e z8iC|FRNDiXKd^?L#l$>|TkK0u%?}6_Z=r&0BS$?OktMKKF?R-NFoTi`7f=~DhP{Wu z)vbg(p3vu6$F$SmJt`d5)@DvH<8N0-$daxB==_TG7~hg2TmJRX%epOQe$ zRM|od@zxUTvfk4K%`mrJ7VoS)wuoTbu8?Gp#Q?AX9-XqC|^s_49o4Yn|&8s9)GQRdtS0! z&u~pNeKN$%@u@!iBni-b0ovdQB7w>HB_a&XwtfWW6tZVl*a;8dkKAxoD8+yWI*)t@a`k{6Ljut z|BxJ$&8udd{_gCBeK)%a?yd3pvdDP-wzRR$-N%_|?oU9dr9Z}TR02e|tGKG8?u_E3 zC=kS_lf&gCPuahKiqSz2ycAP071Y;+ zEUpOeIBP4xc4mnjs$B`)Z?_|vqVqef7q54x6>Sd>KO6YE0)U23@ZZ{A^p9`f0TP|f zFnKr~g5B$7^H6w3{yk zsWGwrTdGFT4N=nYA{{iTc%sNU|=Fb zi_~S18cMtvneZD+eqek9WMc_XXx(I*we2bA6%S+dtw>kLmWb#Ye5sS-5nT$ z2y|V>8*etFKK0Tqu^i6mg|MWFkUwxNeD`)sA=_{Z{UdpIOHRk#MQFaW?_2YHY)2J; zdQ7yP7%}1QtOgV7l3#|_*XUU&&fbScodxmH zBhJTf?2Fd(w~p3spc|;5k*h7PUf{jgxJ!8)0I2jtjG9AgcrjHE{67y6Q{ckyd+z2= zUX@vLcIVUf@IL}1ClZ^gKZE*5zpHwji0;*yW2r2@*wv-QaR${}!5uCK@1TJQE!YMX zv@Vk({2FRQ1%+^(sCxNiNcx1#w{y-&IdlWlVR)fu`s_&Z303**j0yUGM{b&R|NUTJ zC-Av7$j*Cz4B}s$!r(mB5S(UXWPw)&EKKhu!?))r`^FXaEhFO^I$(wBzMKEcznKT(CuMo)DnshGo5K6 zhi|Ij@qBR(=re?>3ub>ktFo52V)hoLzBpN{IwVKdYZ`s`H3*G#&6(1Ew$E))8Zl*X z+b)oE_g!{hY5~3FqMcEaruSzcq+3ei8ur-*^>e)W0&=t41#$EUB8eX-2t8iQ?Pb}3 z%gyeSIR9dAN7|tS)%;qH5R7BKuE$1tij)6velueBUb!(`f8hu+7O^+fy--3px2+Oh z3*`U^1sbLDqWET!#`iT&4lcnR5DXi+u7{6Uodt09q*s8}hn=1Gqqg{%CXb$Ic8JA2 zVi{t={2E_Bo0_yl)XfaOQ;(&i0$9AANYbeK?!ya+Q-JjM6ppnB0<#{M{g__(U0L_E zv9^>4lqnlM0Vh~NU!rzyk)(jBX!|Mo{psTx__`_bTQ`)bitLPR(;G3IxkUvr)v9`_ znMW9MR#xliq$Km4zTL{O-90V7uTfnrIbu0YVoQ%A$>F};d?j7HToiC@$p@C>h88~^ z5_CaZ-&p0i-03@E;YrQFMeM!`4na=mP9D8)hGhsQ>GOJ{ZuU1Z(wYET=Bs=eco0P9DP|^Wphul^JGfLDwl(XqTFMR& zoGOL4Msky!@CzyKGxyaq%1bNb#IGK!HS>LZ{5PiBFT6!_LmPpuB;O9oGR26r=aom% zcwyhC5Pal2ORd=F>@MjTA>9`jz>qxO@$<%uNv~x z%<0avtGH#V6Bg8dNb`ec_yj#-)6WvP99pw_MB4kn<%mPqnI1~yi0keuKdrB71}K^Z zehzY-QjcMEq(CUWwOltdtcnM%C>>Sd{Ctm3nUf*5=utb;Y4XS6tk$VuD=h{hq9Y2} zFA|6G&RcN3nPDLLRi%^Gk;OX@crVcvel_C#=SXYlYwtyhU_9iI1#ud-;k51v)I!_w zl9alC9qO}!H^%2+7a+WUh)GYj0NV67ZD#t1GwW?CC=$xGrh3dzY^WNJxJwS@HEU=k zyRC1vyj*EQoJKc*SqTBeW*`?X&Xr5CNe)_~;a&yp7=Nbf)|ECCsT+QN|YdjakJ(mMt|#_f zgBL?_x1A2;;3tqnT|<;Na)gQB4Q1MpI9Dl;A3#IN;LPmQ&;!dUCemGU4Rmf*8DP!# zQvens3NLd9IwpLO?So#clqxl#$-toIBbw0Nx2yRflESmAiUiJgx%=2pg=sf({SRZ~_U~uZSvO2Xcx=DkwDV&pvML8|B*W ziiOQG!}^mHj5Q|Y({s%|^(@+#wSE+}K~FS$Jhf-{`ILnQPf<@eHlYXa%=LDH4~!yhANDJt_og#%D{ zD89-@RFHrMAXLL+kW1E>l&g`1{djU8Deqtn4J`s@pt&}D7Vq-mg>&g@8E)~Q;2{)y@1TLQ;gqT|E zD?bT={@8m)&e(zga@upBMLv8aMN9Y%bd3cF5=jc;3Fk@5h#l(b$fD+%`Bu^#yR6 zrQSg9{5N&g{zJP}Ip?Ozz%8qK&yfseXtuT$_wzwxy3doXDxI&pEMdHeOuNy)(ZxK! zIy4JN6yke9R8U|$-eHID(qb1-2Q^uLAcD|ZRNS`OpL5{vNZuWLm9oB)zGwFFOG528 z?v?KNG6#cI^8rb@Gs_7xdmA=il5MWocmO@Dzkyrfl2tOZ zkcq=cd%M`1eb=QEDtVB46i$0Hm`A9$3vKVy8uEkp;7r^Ic*v&ffE9kHM2xq>6JuHL zC00>%V5sf`Zru!$Ch-v(XCfJuq?@xQK37~sB^;%hxuSy5Fx_6+NfQ!GFSlX7eK8X; zf@nf=3J?m2N`y!28Eh*&cLlVn>E>U(&CM2GNd(VYY{TCI!nq!J^a2!!^c?WUgs!%i zjTVC9% zO80B)ysY3iXrx{c<%H%Mzs>$4q@+&nX@Ym9}H64Jn8Wh63%v9J77Zd{_*~ ztW&iwyPKTh&XasYYTghE69g1o@$E}B!h5d?t={;tnZe4Lj)Hdjrevdc$6e>f(5&eB z_WcXhqqTj^n!^Tne0NnXSq(?~pPBu&M0SY(4`^(2o0fq7acBoPe4Gjn-K;m(N4Z_n z3f~=tklA91FnB1cB8!w{6M}TZx>fUPH9Y}D`-H;pH6_6^1{KjXU7TbCY2|b zYR^A#;MZtq_EH3pj}{`?f6@CxAh+?G!BxX52G~e{$p|VBSoVHVJJ;av;W&Ndh?@Qn zWg~Aqv{7EqHDyImHII(Tq(Hb;`Yprw)8omls%3}1`r~HJo^)^Zcyy14>d!{0iPQF< z^a)*V9E#)j!(T4YGL967CWVUcG0dPz>|t2FCbNSw7I|j$v8hg-n{E}I60MkbBeY9j zIOhORqKoWP?TIBsdYq*!_YQe3E?DlM(<3aeYp)OEg#z-ELl+k&loXR~7WF`(;=zR?e}p=^>|>U(ZnG7AL@gdKT!Bzd;zOpt(4&tCYGy z%ev2-GYp6QgmZx;N%yVpgTMyY4rHipO0jI!vsqjHg>8i9{yykJ@Ww9 z{F^pM{sVV{84T_wN}gES|C*WR99PceT+_7O=A0cW-9$O^rVc1&Y>`u8(>4vK*Gk8t zAfbN0p{*XBB;is{MLRisnuZvth~rZx(mkw&K165<3nuJhM#E;yHrE;Ou10vl{N;=^ z|HNBmmQBX14fvHQFJi)$67e`6_~Gqt&&uuh3AKZsW@h871Hl`hA*yhv-RiNGPo33bDht2|;DVR`lJL?zAGgv`+3e4p#5m?ggrc`-T1uc3?v#}oxpI1i(UM-ijtlPbMW zfv&?z@eF^&Hn-Vv}k=+}Y0!H&03}Yn7tvZOAkG65qAy}32XD6rry7Lk0C&Gn_#V^ZG2*8lltbr~MQ6 zVm+54x!J=O&EM_)BmpHIOynqlFu5Yza|y1LT1U)|ExKjp_hSMw6efU&$n?}9IS(Pa zKy7EMwU+bGsVv5B6>qq=1CWD!{1Dj#Aa^%1e6PUqsH~Mp83;*k1&Ct6CF)B)`E$R> z-MAs?k#TipHd&b_>}=GD)Ax7-z>ioo)j^1AL?f!44+RuNkB~G zE<*+tb#pDpOyVg7AlfIqBQRswWzrI!!M0?)gl=THgy*;5Nmf=bdk$xIl<=n z^dJHNlLA#Y@!yp?;oaol;afu&;*?*vnIs|I-3`b#unSo6put0WH;n_$=}ZpwV?l2R3~XLH>7SQRO@=stxZ74m}5W(N}NbRK$18 zmIVdqz8gjWcc;?KKx_|9x2~;HK|-6@DRS@{X2%D%sp2b!4Y3J=O!QeU34K5XcCT4vRNbJ&_;OOZNc~_wYq^H zp2{^hI)^fRBDN0mkmT?-q3)7ryy6Xp%EIES`*#^9XJ%}aON!b_DtW^lIMUW$@d&~u zF}$DyVqIaa3vAcc_5$er{~fWnh{7TFg7N!xRST#Fo}IzVH^(xH-!eL6P(eQOv#PTU zlhu=Dqk|Xf&dQh*69-840!#5rSxcDL$mxQx`C)Nck#;*gw$G@Y8S2sRCFG?uJ?sqyN@@c!F*D(JlULa6w?2TO(nn!D%kC6r96 zC+Qeg4N7L#V7?~oZCFnT?w`0nei)~mP@v8^?D_{a*o~i=@M1PxTyrt2PDMI+KkV#3((jJrFX`$?TkYCG+iK@* z*K+|NB6g<%6b*~vM=rVPkvK<@_*^^$LLizGP%}U%U?W81;HwIqJTucbqBhT6kBt3| z_PG+%Vs>7jnA-%FqxOb&`a;2lVF1W-X#U8189Z?vv#1EliHXzI=Vr_%cx4>D1pI$g z0=K@C|AzWjn~l>XaoV&h-O}QIT`iMgU*~L7Ze8}4Li>yoP|KTIx$w)u)`yxOun zo3n$gS99^~kIx6)GXv9ijd2Zkvhp8UNvNA_p4-+HYC=mC_THQ;x%x3qrCs)$NMXsx zpMgw#NmpQxK)|Nh8k;)AhqIlzs%S8IiV7-mFta-!s2{_B8^mY7_*G2!Hm9oW2bYg2 zxGIPy71Y>>PzLI2ODKXkz;|cwBL8f_XgiQIejpoIIm9T$D0&-OeRu^kor2`&(?;=} zqk90PQ-yt0&>PtIAx{JNeO*@>pjXhAuPD6eitx4^Cuh271FD`QfvmCFPfzIFC*_CJBa-N8hPeFF1bV1R5f|2!9??Khksmm9KLIXc4U z!GXhvPzwJU{30dz2CzczXHh{sfSCNJ1L%(;`IG|91Mcy#y#eG0^5PHE?)6nXrin*= z;{$SLC*j)`cSSOEZOmvqJmcPD&8W9}+6Q|;L@*45z7Vt#{f1-*GCt17394uCfZM;S zzLsV0V?JfwUrK!SwLI~OL$ttA!+>LoXPwo3_eshMv)$wqKeg~GJ&GJ$QQzcytWv^v z(RiMi;D$AS2q2EL5P(9KS}Jn-4;M-QcO$Ux9L7lFG*Z}xI?)c(N!gwtJtR8&S>YE0 zccEZ=gt$(ZfWeD`!cRQz#okdBbdLAxfu`UFk<_w4MML9&qK{dktHMB8M6CUteSCCQQv(5DIoaMvHpA48RXR`KAS_1p78z(2w6#S(kqiY?#M?lG30Vr?8-4oV zZ3!=SQDC5`#RsIay2vAfF3!3wo0`HGwJnqQ@nbp=#h(2CaKDcKsFms;&?05*8B(p5 z#2aVLBH30A z9zOBgZ4=7FE1LVd7e^%yn;FaK!S*ds&NReP+q=RW%2f(^lb8W~h7a%bEWfSn1NZJlS&WY%iv-PlRW zxPeX$g&v9WWW^BLgV3cX=w`bG#czFkF?korCgc}ESW*_F)}6G#fJlNNaSVo(Y4-CZ zs*ZLzT$~C%osx9T(nf#{y_UWO9<1h?nKaXy3wkmtYWb)z;_}^yPebFe+e7Qz*n6d9 z<8IT6-vYL!Izh8uOGs8y-p&c0?mXlC&-Zd%;aDesCDlc2@_FamV8&N*PH~Mfv4vg* z`4fflI`V~aaYLE4R{(yzytwW4m+$BC|NK4v{(cB%vwBEL!a8v6!v+dxKKDJHu`Rr0 z{Z!(~Q7t(S@7b~dt<0!Lp>mft7MVUh(^3gU=prWJ<>shUyC3}6vErF}^usztVn{oK z{s*no`{5pZ-Kwzz!HpfPszw!t$z@g=3cep1)?`z;s zOY>rV7pa?CD~L{|(D_{tHtao*BhRHs7HFIuM-H|wsQ?=%Ud)jAv!mC4ut*%dr3!l#9fMMx5s{Tyb@$E1|}KS}+jhj-mpm?b?&f5!Up z-O_l-ljHk&023EN%4-TA4Wd1t7Z^jHx7QFaC`?4WMqK2*2&fQWAT8Jao5T3u2Zq@| zf*)zdd*FP?dQH{K1R{2^aacBz$(FnP&^Y%Ef5yzsqw^JNt0*PDP-j{mG2{g-tzQJkJoe!GOUtU=0nVK5!!j&1C? zmkgF4J^MzejQRAs<*S>PO`X@;T0g0lb-Zr+(a^W|{!J|qAkZG!1MR%sG!nsry^rgKF5?F5P;YZP=oIl0zlJI z9+*g&BI!c@^r4@wsh^Cg-ZBJQVpy}yFAr02--py0(XOVchdIF{)9&vmSeyheSJxSlZ^hRv` zazCtho={y5UmHHHsrwWov)xIQm^r2J-sbLNUWIJcj)g^-8UTHnIOuaZFvNV~+CvFe zm9GqzS5F=j=6%4kYY)o`&RiLYO|7u$oW^;D`+H$Oe|G&?uH)Ii@C0oP=$_B;5EA(w zatct?X8;lKPafO1Q6@T|izK}qD#+kV80C4^KC)gpGj!L9U?a&&1$7SulSxjL3zcwQ zl0tv=k@Da>ag*@UIL0|C#TJ>5FILTdbbVG#2#w{*HSj+n;l#XX;?DTU#mnS6>qJ>< zRmH+HH=_ato$A3$(WBo+&2q-4HQD0iRuE^%NpcR?*zTKdsmOs<4ZzZRi!V%PWq2>p zz}fXRJJ920SGEcvT{4x==686BHPO2zw2#tZaak)_pfjIPvP`_2w>qSwUxU^vc?kO^=5e0hd}SseR4vbhQEN3 zQ1DI17&Pe^GSFM$b@iEiFO9|a{RFlqzp|Bd;zjs)p}!-n!F$q$Q< zbg@$<7RAU^n~oh=?-0DCBFGx@HExS`MUaKm?D{opt-Db^slavQ(nDZ>!V5}lk51^b zuMyuJRdXouiY}@=E*JPLWYeIyc0{@IrO@?iHJocRKc8{b8p|B>pqbjjaepOBY_pQ= zQ=@lzCyoiw!FumB-)YuNe$^izQ+cp)^?&sS7Oll6NEAw4UL-0&)yWoKTwkJfI+ z0`x)ergy%MuDX8vD;8xU(Nh8lNO%?Su6PJ$i3tgGeSLdU*b1y*<}y7RQ?qe+m&5tn z^BmWkKE6nPM(;APg2i(b-5oGxTL@c;{LuP9%EZAlsKwAZp{#G}{vBZNl<^xJx5eO2 zqRMsVM4_M+&JVOzkB^={Jv&*_Z0Sr8$$4}k73G?C>6_F+^}HU9R=*3()=e{^#{vNN zi)696@q>38kw_hk%iqHX*8%-k1@uC*6UpmfpsFgpF{swckdn7)R(P|V?|LxL_ld# zu_FR1y+lwzKtw=5YQBJU5kX3TkSHifjdVpM0wN+pl-?sn0TJm$x*_zM1On;4o4I%H z?^|Z>o!>ii-#c^v;6u(1hn$?V_gc?-p0(Dtig9)c`zW&(6(v))ykzy3!SN*n>!CpDLP;gpNgIBcZs^zd&qC`&-0f_U(|IIi9G~;E{k`{i z&hA`Tk6v}3pRBY3LHZSX=RZx5o1Zu9AJmUgkIVGfvzXifWS@$QlnmvcD^ot-XVZEelbC;O)>n%ebaA71K)wReV4;siVSdPsINW9vvFf2zv>t6Uvf+v zB=D%+Rte=hg3Hn;VFxd*K}kl72Vd8euMw}1&$va0+-c`Z;!i&l#<7x7=hJv-Q5ybL zOntDbaQYc+d(N_H7v^!2MLr^!omS()RGlAlAWMhO@3e_FG#{EOtCVOTzzrzLPs*># zB{^4SEulq>z5O@HE3N-i5`lgY3;)9iGP(&xIbAS=yMWyG5kfKjg$;7A zUR_~q2h-;xdg%~Wjg~`NnjYVtnbnXG9jvrFR;@W<)6jixpFr%TtZLb(Z!1oJsTFpU z8k%|+X!+VQ;+t$yy+hyZ7reDyyDDN48y`vV+CHnn`@z~%q0d2yJ|%hXN{D6tohT`u zQWvW`IeH3X=Kcgy@7<*AOMKf7=w41J0zrkj?nN_0Q@!H;g;ynY<#BVPn&ENUE&0A? zIwoNz(Z!c<R^3$N7g_-+!E2ko*Mj1(D@D*R=dM@Z=F+#miwsFdwc;;F#yjlL{(iSI|bN~CG@9@o3C))%II5$?$Y3HdVeV& z9tAn4L(lr)O?k5b>t!PST3I_tw~$B4=f_5Lju!WP40)&I#y7$}8;as#+`-9@$53*1 zhUo5I;PlL6wHcVsQBl$zh^P_MMK0okm#g-|Voln7VWekvNcg4Q{^T*q)-Zz|*L z>pV+UO;BjS9SI2F2trJ4pXxu2^2?^0_YM_(n3yV&l_&WLIUV?tVA%G|HDQDvRA7`? z<3-rX;>Q}iwlNg1zb<|&hHBa)`C;uHPj$h(unH&N?~QF`!Z@Mr11Ide?^`prXoQ)g z{^{bHg+e?sm$Y5wrrY>i18!jv!@3(IgR!gCtLjo~Az*SxK-fWTY^m8fdc<#!Spm@? zOYj!10jGengqwnbav-&06IfZ`D}|UC13#P>Qhui=V9pCUY@VQr%?M2OO zu2aH(;7FiaA_&V2QrJ(fi-Hs0xy(`s8QQ|Y?SM|$NB!j5{FAFYE*xBXOUK-G?28h< zuvFxEvNJXWY1ei|xX1p(O8ZLr$l5wK%Z-aic02b=tmCFMi#A7N`_-gwi^bU~#OuID zxW=Kp2>7DCUU^%$;Sfq%Va~->L#zjJ-k~hb^OgSx-fHeMA$oI$ZpPYyrs;3>zTH`* zy7U-l8}S*MWD6cB*r(qb1d``Y@6oF45T%J;BY#d>E02GazGY(g!OG{6%2ldhSC_SM zfhnJ{*{j84#maJU{R4CR8XbPg*(r5l&E>NdCaly@v8l*aXJ8U%sc>57JgyV%pp3mu z3nA_6xq1bQ&=R67hYP)MFj~jepo|KF%GV4rYw^7r?T{e^PO4*Q#q*nYS~<-AsrNN0 zVVw(k!D+$mcgC#!VUctlZNh8o8|1Fog zQ)N__g;OWdvsk zO!d#)rZIUOM+%k?JEMjb7i-?RTWe%_qJNr%-gm3G<-Gp)ovLrDKdndiL92DAo&tw5 zhdH;ge*d1If8_2>+jXPWj%CxPFIIQSFRJXZ(681?w6=LYY0u`Xn6Q`%STq9AezdQK zf30I5>%3)Y?alxLg{XnxYJ2=TYNaoTydF`Dd}VBVLrze3i5%oFo3A67{0gOFs(GxW zV}DnPN#^O9AK@mzabLQM{k}XHvBpP2H$T1O_d?vB)Ev`tMkdo}ctp|WYFHk0OpNV8 z$sWhoqlFRs$QPCWd6yx?v7sR1g>d1>wsh4k87z})W8tuvL$MM@h8_4Y})URqsN!&LV&AjqeAUS#T

&Q4^+n=WlZ+B5cJ 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058692_2PTNCQ6D0Y_thumbnail.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058692_2PTNCQ6D0Y_thumbnail.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..c0cd734e588d951acfe6176b638d67315038134a GIT binary patch literal 13272 zcmbul2Ut_xwk{k5MG!%H5eNzhihzKKQW6^=A|N0{Y6PS=5$PdOQF;*&kRlO~CMD84 zp(7yD34|VcPpAP>{(SpC-?``PbIZB=u4F#*$&)qLnq$r}$2-P526c=&3%KxDLrVid zM@I*ELHhux6oASD1ND2)^bC|m>|aVdy|T3z@s^XhAtLGJ>F8!JqV-r)j2mcz9X4IKJ@{Q4_gygPA%3cmSY3b?UD#?P8#PPoF(~nt|aoGb7`fvnNWbF;AV^6_x-ad2^S{q+z!dfFI<(@dvNGjW|icb@D2 z`l2=g*w4~kr@Ky1cNuVsosOQJj@kwQ0swTUX`=om?Y~`gr)YCL!+4g7`5f(riVJ{K zboBJ680i0+HSO*|+W!Cs_R}1f3vrZi4~5n1c1|_e{(@ zynOru;#VXjuU@;UcuVQ_9pwiP)ipF9X+1W4W@HR`ZenWl($>!2!O_X{jhDBNub+R= zhmXM_p<&^1@t+eClfERUWM+NO&dJTo|4~|2UQt<9T~piA+ScCD`KzmYaAWS}yN?`p3Kxi{T=1O$T!=)>)RG%8`@S%6hLdu3D??RDW!#d6+NsP$ zi5+gz!TNFiv(Q=g@}ZBbPq(zVDnm*HgNG zp)u}RsdGV^fu>yg>VUtRul;7-{JmM|ctB@TghZ0sRMLTq`E26X51K$cj|~xmC(h0AGa`cC9c)l_N!_PrxI4W>@nQo+K(j2=)8%aayHdxnEsbb4O1~QQc`S zeanodzhCly+aok+BVg?N8UK)wI~f;q0qd*TYFb>A2Trod!S?v69E&-apzk_n2VQW2 ztJ5Z^MY+T#Kp7HEr|yc_FY zS!O+D`ULfE$6s`7TvLbRwjDkbL-jmJ!^be~P4 z`{a&eV%h5L>hRARifqT7UR&pOX&tS=B51pa0V@B!u2M&WN6!9JLKV zZ}w3E5*7qD3i>h?Fu;!a0Lj-i$44Tcz0y@J_1K`z(c!MXIv3!-(dfv0w8h73j?%RJ zt{|d=Ep!xmmcs8F-+s6V`+BW+`I$}3ttZyEDTRv=0;*{fVO~$TRhPFe5ZQsFP;lA> zVP*xtHcVo0TBrdw;67J%6_*Pxy^5-as(OG}VU+)$6RnlT!DjqdM^^)`#a{nA6 zV&`m}JoOUZ&`xoKP2b-Z08~WEAA72`+C$6y&)SWmi`xh`bk<6)XK|q%IhzHA}Z{36hZWi2o@3Rqt^y)9u}7 zCo#QRRKRQ;NYu=_pY&jGp}O<9YUsOB-$`>9S*istM`?lbfX+Yf%b!|h_YG42z!U#; zKun>~Nu~ZJqyC!rtUqRrl5nC)x{gsC87Ksa5WTkz?RN&J)if?!plfq)+iWnQG(^~g zekpdBv8XF;udfxl;osObv>uHVL79k7yB+YW10-Cqgo$1?qO zdFL$zQF3M@HrH2~o}k%$aMdAns z4ZODHd-=M_d;6`0^hXQP%8($IKR`RL zkcB^i^3G+9;RTjio`Y8f$I{f4Z#-71^iz5Zx=2jLAw#YDNmnzcoc+_%LZ#zXASZu1 znIkAe7o^rvXA#X;K_Y9TjvzEHkh68?c2{MX<<`Rk>s!U}NayXo@%aM;etUy3drpU5 z{he6j8_2Mi{9A25&>d_4O~y-*Z^B0sp17C}#Ay=Wp!G1!9(T@yW7U#Ytm8R0jmt%-P{ zyVy4ze#DEDXa#bSt`N9tfe~-azH%Dgt5$!7mO>rI1{-0fw75?Yc;rD45kY8{LC>)e z#4Ae_^UkYPV!|DU@q8V#wLR`y0lw);Y#!(O<;DC#mfY)nEnwBPTI`n3n)H{ll)jNvyRI9gq z=3fl)A0R4G0SrgP0)nSr(6hwunHK+>v#Jo&33;t+>8s4|iF?<%gxfG1!e!r9r!QA% zTNrelzdwE@+ADaD{#z`c{4F+q7CPZsh(r<0hv+fa`^o{E8(ptv^&@f2WlK@gK;2#A zbfe*ZyPvZhN)jt&*f5)~0daq@yGt{S@1GF6hjjejATO?96V{5=E85E|*E;(_(EU@5 zD#R#!XWI-@J;ZYk&F!e*s9etndh*5Ua(Z;6ypWzX6E_(kT9{WHDa7|uKJC4l`s-;4 zqRAXsz&G_i;Y>}2$y!QdutoVv>X$>lb2ehEA>TyrG%vgu%t{`PAaCV)-MA5VcoQr} zVWT+!VEA>CX-SZQ>n%3sRZhUb)OxhFAJ^gYx3V$!?=qI4oMI?Umrz^=Q~-PVBJ4Hq zlTUBqjW1AjszxXGU^np^C~^YIcm3YrzKC~4Q9H@{A$XI8gpQV~`H8L^x&E!bv^soi z@C}E$Dyxu~fjs~7SNDY4b)K|x)(-e1lAkvBPx!1^WQI%isJt&BS>YN~ni{!B>K_#R zkuL9*$7Ah4Jn;#~;Rf&_bMuX6^`A}fT*R0hU=JE*SUg}iRaaUC1hR711-pOyJ z*p-5H3$Clm`&{f%rs1CjlkpPmR6v+%Ob1&S{1PEJPY+vRmh(kJ_o4n?wA@A>H-dS7 zC9`oroGT7wTRAmGL)x~#U4uY(nrbvr0r6?T1BN;(pg#+Rc}$Ar-nod)BN=tLITsO>}$#~O_(*|VeSNCoiEpx5A(c8+@-ngSo907gc90M@sV9y6c_s2cQ&vt3HjqC{lA4_%UbAnu zuxVEYhNuQ+VYLH!A_NotKflVi3+K5sXwEigJjt|K?cEFOU`((U?ISBpw|fTgzeXnC z{dA9!!H4^VleT{A#EKs^XyB{`ac+vti_K!~t)OS0@MgQ* zP}r9=R2TR=AyeJ{b6yV6ipj~;UkT5ME)xwa|p(AW!KRXNAcg-I!V9G zrzf{GzPlO@Z`}cXS%*47mVhMgT*_nncZdykSxU6x@~o9W!eCsjirnj0-zQB7N@1!N z#lAfT@lP=o0h@Nm50a`ItNqd_*L4DEV9d~oFG7bR97DH@FMtnj#d`)^a2w?K6hve) zZPZ2;(5!v(v}_f=Z&ShD_BBp%bgw50B;QH@`^Z+Gc=Rw(Cs5|9;(aHz7}g;2p4k?> z5z>h)U*6y?qzr;Na|t?_Hm||LxU-vu7#6h)?VFu-PNzi#Z%AhIo_H;RbRjg8!L8*>PExlo-(x!&9bLt zT>m)vD|$BFKX-!)AlPUT5~|wX4BM2OT(16UA<`ma{pQ-!s`Rmif;XypsPcjxcZzTZ zA%OA@&P{W-`O(CEM>L76V7}1*EC$$e%&}5*qe>vc5psF5``#snbhp7VNSM+=kulplzG2&Dp&i%JNeZw${%V~+Ue#Ih}4NB5C_vrSC29anuo*lu&rtkgzzB*84PY~|Qr z26tnWrQWv2CWv{AVQ@WlDm>OEVO?Si@z7EzULSdMbA}s%X;1n~FQXC$60$eUcBueS zK?H#};{$t_*~emtEsPVaN)p_;%yM!q@0fW7;aueYl;%lb2=w}c z+Y&?Db3@PDJzW;XA`_oJ{<$E?`Kwro!Zb${IB9nKc-4>Q(HUU0Pn-~4g)mK8AY(-f zK*B7q#~n{Vr^$M{(qu~tG=un240SU~EPC)!TbY7jT%Z<3VoXfL=j7f3_s(-=zF`-aIb`213g6g>$Vdyqhj z!({?;FA^de&%B|g<1PVn=K5atQUekKa)Jy$NF1mA@-KR+999&j}9h% z$^gQNSlXB?+j=7D+2eC0%qmk=j=VPhU!&{*_YFsaao_CWCi|SLkKq@yRgStb%kIuo zw{r|q`$|iULEX*=J68=H1|LmXD$yl;Ubx_Y!TD$ul?6WspGI?0&N+-8OBZeChQ87E zVmh5zaZ!6g_Rv7x_7&p`E&87*QK6v3OZlyy11&fsu6ebXQN2*$DGHEe)L79#aQ{%= z>%TMNt?uD-R{q+uiVq7%O&?>l4qTryGjrM%be`m2BcS%d)F-5(?&a`x*Ufn*F3l}% zfp<-<`7W&Q+I>=}01`I@7oBa_zgi0n-t&8Iv&qCzrhM{t$CcEK`T~-GSrL*tENm6^ z_?-_;7l&UIUbbdUsWU-JoGa9)}DNCnVB!VcMBo~L71YS=>eR_;FE++WdYeyYMmXCk+6qaW6jwy>K*gZs3Q{hJu09L69;o3p3KX}U@O1o`*t6nTdzBNzW)5_(!1Q}+(bUo zt74KqG>v2!=^B@;Sj#pc;NE!0MSYiA}k=87#Qz;5h07!IT(0t!QERNcN=1Pp(IDZJ=b)46piLY7k~M zi(rG@8$fCiWFl;eSBrfvIum)GF`hKBHy;XOW|cm|%!+)0W>urA;2%>f*zB6$5O0Go z3s9Hn&gvECH6$aV`Dvq!@9RG*uErA&2)CMJ$}O?whQR!=1U>RDRK-j>&9+y4CEzAu zz@y?BJY)r03T{T9pN^;lhA3Z16KXD&uE(?e{PdGM?8M#|*he<4}qIPvl*-{kQ| zJO7OHer^+ulTEp4`Ie{oF5}_)T;Bymvk~a3lXovqYNoHdrZ*pFc0KK6ME&DrBsWUH zTnL>VI_mDKHaP7m$NqRO`NlC?`21SDMRxY;U=vPocum{=%d_?YeUA@5M}q3`d3XmE zu;YwAkKSMh?kW;Jq4$Wq2U|omez`>e68uKZt$XItkOb0v6GuvdhNj=-;);Hz(l*;Wlbl_sC#djzWH zXC_d%!!{J-S~?%5qChH2)G*>#O}Lse-*}S9><#niyQ`J~O-5eiq7f9LZS#4pQ__{H z3N-!l8rhQO2w5V!s-cLnW-l6B^8(c^Z~*-Pt>|n+zMJNWZ=L2Cadj!Lk+Iy&O6k6u z`rFn>!t<2>WrAg*d}P7kLi94LK%v9%QtU|*RE{!*EI3)-7WX3>scxQ!Vw@z`lGdwg8zBj-4)~qlic?(B7gR?sk@yl z=J?XyTV_LBL9R<||4OPl_rB)azhi>`mp)M9#05Tr0hilHH+E)gi-8yi#a<45y8|sp zYgBuz`AdSm0=rPDl9^?>x;Ha3gkn8Y)6%m4;#4#KnNwx7$M?-u^tDNA*m4FYwoIJy zJ6PBqc;=r5%=V8)o!nY=e=+}L*e$c++)ip-VPqgHKy=*%T$U8>;({)}>6~nEE;WqD z;ar+u+KaryS;0W($!%28-h`H$+md#GZ^v?+MoZo?W?_8GJ=w~W_wna6>!R)=^wBy+ zbDx(1pUb**hr#1^-nIB7B^B4)i%L1`iQRpDfiYnf7n$}7P9jvk{Dxe3WE0C~$9Se- zbU9kFx?U5v2{Cy0xY=LGy}yx z$-KL!9~ZY2QF~EW<~Qx>*8lua6=*Pm^&(DaWX~}#@GbTlXj=-t5x*rj*dVZe;5Y1U z{d`N+>?Q#=-;Y5a|XhVHu9ox1H4^0g+uG3;QgzAtH(mABio z`^(Dw%I=TX;V}*3FON`Vka0KI2oC&Kxi|@tYwB19#>RNn*L|t~{y}E?`NICxu}?AT z5jn*r?zD1Fk!l%xH&#-eajhx-i22~W_OtSV!iBoVn&?riTwRW(wo|$vUd@L0( zdWfo&F1Bu{3ilN=$w+wcJ@hr7@CSjNJ}iB6tkbj*W#at}Sr@sb8-n!@XM^@6z+e6O zfgIMBmv5UpFBy32*&(&ldHIO}Nu6}6JrXfu-)hnf-Q=S;3#NOe-wYHdf!t9#cxY3U zP1`|<<#czgPTE^}zpz?}aE5QwF|y@nBZtFN-;mB@i=yr@>5&wBjg<+mpNtbC5DfPQ z;Yxs+Oxmk4SFw?a=^k76>LXb;UnwT!VEz|7r8o03MqHcTajumWl|M!+8}u#*9XR(V z0Fn&Ru_vD;vv#mf!MP_lO>erq*|c+*DV=QN1Gd=!6WoYYz&y=LV_pts4K6gKh#QL5 zr)li*vEvY&Tv6C-$62z~s}VsWi+s}`7O!e)HT8bsJHLXRUU z5BxFYfLzK)s15XDbaq@1ukk>^%YzA{ff+M5xK>ctj*A{|t5mCH4u4mX6NDTj%kpjUdM{$p_;zweE5}_oy&kKAmice~jMAK5>dtV2&k5Xkxs(5|E zRz23Cj_2Y1!J}X`@>#wP%_4sYNkI&`cLPN^g*fo)ga4V?0n^T!Cyb-Q_s+&BXB00WDG7HV+X-G;8z0>LWL=j2!r7 zcyG-LC*NMT44DJIjlrQf%n*<4@tCK zN1S^G0z>0TGAjIQM(rb7Y`?(N`LVAT>q^%z?i)`YrL)idM%~1zdeif2rzC*jM!n+B z&-6`mQipU*<|(p7M({@x*fU@AN9xgK$pTlQe3r#DN%|#FV}gAPvaC2G|8A1N$k-5f zuzLMtVe^3Wh+|BDT&?Cs)yN;=N<21i<)*76sDP;S3qKo96rAkr7|)BbD}18@-qH2V zr5u^^Q@kQFSD)${G)#VW8{B+ZSi4?x?56OpTJw6lCTCP!uFm(*;>OyN>)ue}wtui=G=r&2BnLgM4+ms&7{&p1ywNfWA1e zV7)+UO!49)w<24U&84S=Um=&?t*BogxNmYLK~$R6+|OnY4e&Yr!UqQt zWdk##Q->-FG3QHF|9r4K9l-B!l;xeWp5^WDGE%V>lTMO8QC{=^d=*sYTWhTZdg#2Q zP27EVjIl(QxrekwVS_XEUJEf&dd#p@9otNfN_JBy?CS7b%BcJbc-De#E@gc$(5??U zWQG3u)y>t8UyFBNxDy-c=8>*3@?+O9Vq&V41X*g8oYQ4VP2^1~}>PxXKlCa&; z0_K1(fCP!|IApNf8#b<09_*ilS0D#BUU5A;jkQRX8zSMPT5>+kuz#h9615Kc*ol1R zzPT+@$zoOKY7g2GSba(fviE|BUc>r1rtN@cZ z`&yxVjToHlnLuRA^bJ^-m{Xk#DwSLTT$`i<0yWi|*P)4Mb`Xscp3@B5f`M@jm*+J{ zL{!_ANyRmD10k%rWmhjZBH6-nLoK(+G0a6w1O6MHH}%!M(&qj{f}F8}@*BOwfkCf- zhV~ZBI5(l~phry3GyC1LOC#GALiiBS^nzofx*dA6k9vS1>MKX@9#bl zgiGwA7*W|272k`71bA}pq@zbf*!3qLH=BMG!8d!QI{`umhhMj`M+ItQ|Onx(S5)*>lh#rz3! z%C3VK4}jk(k_|O2bx3C#E&lpG)@dbuqt%J>9kNL5(qvE7n%g%>Pu!<*Ovjp%xrX#D zdl@kUEiv`Thnaiu2n|>jez$FXj*WNq`1=pjvtJ4`{5M`ai{1N!U_Rc~8QxTkkSyLP z7Ok!PWGfcAAV7@1jWywB*Zw!Qw77Bi2?5bkR}qLYb@+6Kqp7Wp^hPaAI2f(nw_|GZ z_0eNRu4~vXaCy>v_OUs|dp26O{ne6ixkN~Uxsv>AkLmLT?v?p2*a^=yS7>XBC0e*w zQ?IfC@Pa>+MYDe&`0-WBHjTKOi_+m6FFZEy%=p-o6F5?K%x>wP1s?!iF2Ppyo31@l zPJ~ML;p*WD+rrF*$G$zSmDnF5jTDaDfe#S{vhje%L$IO9d z5}m8b$+T3lQV{}Ei&!yQgYsUtLKA(8u6MH~u%&%Cuoa0!$ad719oWB6oU--Yz4%fD z(S$zU-`iHmPkG6`Y!Wa~3ir?T+zrJq0fVdOPir#Il;!)0?K@A!IgyQ1yhf+(`@IN= zNc4g`jqX}Ma3`~1u-rSEZ78NSQ_Uzv^|(KF(YVE>VD-p^Sd$1AU?=wy-uBT0c4K*O zS1DNX9!SP&ZKTP{>ArMc@AQ;RIeOBa@}E(rnR3dP({yluTrqWBskH3YCI?do=JlgH z=P-g^ird_a9!5@HWjw20H{(owhL54=Z&cuu8Eis>B0S@@3RF}~)(4QHFmoDw#1zx= z<+L2_xA0?$IW|&!;5kY)l6<~k+5LY5HG#bBh+nDar~o%AV3ma^Lq~fMns)lCk;h)# zjko0Vg8iEfp5ZPvZ!TrSGN2F`?Y=nMLd1kDSs-C2${%)-G6yE0yCwek(m-6a7QDZT zSoH+u5xJ$b@DsX|?UnUE6vo^Y%Lq4PX>lpE6SxvXD&U+|BE>rckJn{sAnuOyA;oVZ zC=I|P$TBg33OM(gmM=k{c%0-|PRy>+_|<+eWgY}VWrFQb@*&Nfd7c&SF9H6rP(%@I z8+wmKn{%wpG6jvK0`5km^>823&9_OvC?0sM?wy^o6Am&P7Au)qzq@q58W@S7hY1m8 zT4ghR>$IYeMVPAUo{0Ar0CC~EKLTroXEA~5&tBrTdGk^XjP2(oHt(IBCw3kLq|Y0d znW$>dHjL{SPlKn;SX<^8r>Fp5Sne=q_|Xq^X4h=^8q=ken<@NUS)G@IjSZU_gfkmi z|6$xyus-4Nah&@{E!Nlq!5(nP*Gkh#;oL48N`B0tUe{`61L@X_>@DM&nt4of#kS!| z%iUub$aXiP1LcgIfxMu~pUJz6Lu;_1AsP#J@w(0O$sdHn=1m zK&eqDVH*w_uMAdfYTtLfsM}aw;s{`MU66dvn%wR{1++91l*o+xKoS#zfa>#v^A~-Y zmk9g#FupRH%%Qn0D20>Z4B1IB@iOK3oYnio3ES1!_}#yX6n%(3U`LPzoHx3O;eR02 zBi|A=4y0GW>9(kz-~h1sWwSQ_(5c%y)%XRI-!`U~;$W168961~w~H z?si+(RepZS9-;~Sg=rnh&+^TM;CJnHhKpUA??`cdxOyBNT)W!{Omx(Tw`&A%;t5op1_;q0pC-;+|;qancwCWE3zD{UZqE5E<+|(_vnsmEU zYmfdID>AAmOvarFl5?jD$d|Rfs=EBFH==`Uc?GLSfRp2{}&jX04d6+i!sCFYXr9qS}!>&NsfeDJCc0;*&a6XmXqYz%*$G z$ypBe7YJJ(&umq#!56jLCoB353W8Je$6r^^kRZA<2dn$czEw|B+iV^LFWL_?xJq>b zFW2z^>kipJ0itQ9qT z)*dJt6%}EiNe}+C_%>)K>hXSr_13+vuoaH5nDNQYK;V{1p96g;bunZH$N=~(Vj*<} zy5B~l<6NOvrl%*Jlnl(Lo*eRW2-cUD*x799IF9@FSq>H1R91|-7j4e-J<9x=@GJn} zyju1oF$YYq$;AJC&NX2CIcYuQHkvKY-nV`*TS9VlsZ__pQ%4(GeGHkWSygEp$~aniMq+j-Z{<53U{V@3N9yL^82tHN6cqbOFR##l{nt*-;%&#L`Qld=rqjdVAxRx+ zTAE?IXKf^hUg^J&`dWKOWpyo32Y->GRS`q>q6z&#v5KHV(KDOXzy_yHM?HFKo(^7iB0+2+u4- zDi)qhk{Qq8bXkKn1+(WC7V-mBrc6ooReN&IE8w@_9dViqi`k_u6+5OcAv@PM-e^$d z#@L#Y@u$RT1OCVF17CsOH&!V%Y0I?+oPGRfb$tPD(K0@z8YK4|`}4K2Q%_)^-ikl1 zXg2l2zZK8XEda}b8&$J0Sfz)zD3?ljYTF(e51ExetukKz*+V-d{3?NJG zqiCGVd6VEWXL8ouD%3Tn&hGm}&kAZM<@udlMl0*k-@e{qi)@v@ z2}iV05_lyc?{0LLhu0BpVf~||1wB}6d zTAJ-OY`ZItk)?u&z4$ceb}KTfeDTD*auI!K8!&kY#@aoMlVx+Yb>Tl2qyqYs@wH46 z`Mun0wqJl78)7%=Cnu{$N2jVP8~T!Qs>|dTzt@&@krH5;q=q7Hp^ZA|3)*~!|9b_> z|D_oDuY%-8UvduG8hM&BU>xrxY)TC1VEz(eS{@1Ci;htCSdvAvs0eyM9}Jk@|DNBI z!umU`MZvt(6!?DTBHDgi0NzItQ`U>PSy`niu~GZsPJmeju>ImZ((ASrjq(epe1Lh) z0@+~N_^Hu%xWFqDCWCGoVwE-Kkq5SFMj>_#>|>ZkTE(>@D5;mFlvcN=f+!lmc#} zP?rjJI&sjJ87`Oz;UNV`aEk@S=BxiQ(J(OTW4jnFq?e~iK80Z;c+OUR0No~9>YQso zerOt1(iLpA)8|4wAw0Tccq}{{Ej7|=v#dFlUUFM%PSkKZI+Dbg*_ckwLt6oPNpy+J zCW9XuCGk(igR>YAjpdd!f9M`%>_{)LrXAJ_)zbZbtuuvng&@IX2rIp(&Sm_8mSw&B z-)X=BTGc9)A<*G}Rp>fyt%dVlP>)kOZ6f&chT1)$^|^nXr?T(FzRh%TqWLEW)4AXA z4%hFSObO?9gLk_6awowX`>5KLqE+Ls{GRucpJfaD7}VVD!J(MeL1!_=dlm?tnn6V~ z&2h!-Nn0Vd{)0WyAZ)4gq0RjEMi0$QNZq>y7yrrbq<@LWzc=rkmrGzOF=^zY=!zez z3yFPjaG!wQn9HwE`C2nluOPZvi7D(ql3pogeWJJFs}_dJX|=I}PWfvekKxznvbBwV zsNSNPqr3C}OgR6~e*c%c_`mDp|6~6nr7^|^wVI^toojz4!|HYL)lD}W4$aHLKe~M{ z7Y*D6rcwca(FFIkp_RVKIf0u4p4TaS2|MePlf8hSoLA literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058726_N4JQ1CR3DD.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058726_N4JQ1CR3DD.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..9243190e1916cf45fd931f046a75ab536a810834 GIT binary patch literal 63842 zcmeEv2Ut^E)^-pOMG+AqMI|aAB_b*!NQ;O80RgFz8Wj}*l`btIDxwmafPm5k5vh@0 zgn)Dq=@5FCUJ_~uDgWWlojWu4`=0NgJKx+H=l?H08#u?qVVAYnde^(w+Aw+;BcT17 zY8q-FCMG7(4d5S$K?fcdqGb@k(7KB}RqepK@s zT>a>y^Jm3R9(8oRdEdsxHT@*F%Ua6|+ma7gIz!GlLn9u+zvDlQ?;50N}ADRx>!Ok8aHB}{B=Z0x(( zx!Kvd#RLuti2dddMg@p#7mFO*V`iq~pdDOH%v?;2Y7hhjVqyh`x;@&TKbUqf1N~*& zwVQnp@Id~4&<-YM<{d1|tgI|7z|&~p|3NHVtOt&rQQ66@ca!b7BhT3=Vj$C$g5t4tEpen&@?bKGDciCF}1pV=dSfV z8(U`=*GF#d9-cnWe4oE~>4yysdmSDT`Q~j@V$%EM4=Ep0({gh2@(T)!ic2c1s%vWN z>Khu{J370%2|c}iqhsR}-zTT0XJ$#uE30ek8{|#O_PCfp%zqlzFC+WaxVV6E?O^VK6(fe> zZ^!)bEu*oP_s75HzGZ-fPekqXM(e@B^Km!y;5?u|+CMZ0Oq4Tl9t76&CYUIP7aMdSuWL0wMlpC;2L-2%OWM#|Cdx_X}PC2ne zEa;E+kIey%FA)o^Q97N76$AZ<4JSo=pQRZ5jDnroY4a`sGhBgrB{K^iV}SDS)I}00 zjUbQ6YpQEZ5GvdTZg~$I)N0+Zst5P1i{sIQgZ^m$*c^BV0IGH@?#+*?W)Usr`8Cbw zS~yL8zG|O*h6X{p@lSDYFm`sCbm7)aXL?rKgb;12j{enbv};yKqGiDPX&W3o6LaiG zRC@m*ePWxg;nA}cpYi3)X7S+gCuGGQ+Z7?>YQk?Hp_%Z)s+ehJWlea=6mr2mM! z@4x92BndFU?kOLBR5R=t&o70^rSp-o=zfiYGd_zGVsuwx7ust}7m1D>_3!ujHaMUc zGRq`I?L^cYP2U8+a}M55n*(IRLhXmd&wrSbe~M{>DAEbMTlc9-DL!9Ib3;}^7}L#y zvgq~HPUQ{~dv}}J_q=SlP}|$L!PCa2cgr-{JKMgP4BZEuke;SLJduCQ6WN}(AKM!V z8oOOs7-6$dC{QbVo%zILWg#-*BgO`GjqsffdYFEZ0eTE7ZxR@toy>l;K`C3Ccp>Ge z&{Q}0CX6~8>vhIhL-2m=U?o=U!_gnn)A|qT6Z7gVtQd}G6`02#kIX7YVxuD0b*pgu zEg$wZ+?!6Jeb`i=tt?4f=Inc+za?5_pZ&ZFX`#+9n3m!}GqRn@vUz`XFtK-820+%u zQ-5HM|D9*?BjXM53Sz-Nb+OZhL)?sJu5Vt%0J$$9X{_lprsS{dFFP5aJiNHA<{|NW zsrQvW6~j_x+I$hQO?ICzu4sxSmI=N5!>;_}cI5}RElfG}*_W5EIK13*W_3q7Oi}OS z`OGnG#5GKrtLw>O?Is6Z0aK|0N!IEO6b$nWk-Ql)oL{VAVt&*nbwK62=SqttBx zWIsCm{mT;}RX<7vq5@E-wS%n0}@tU+>@}6rt1=$2J30v z-u>eW9Fm95%&c#{Y*Byp6azQy=M=Pk*_fNhKV}HV0I5k01_*CAX}XH(W3!<5lcQ62^}Ch@^gnEYu!`+%DH%a%ZJRC zthIg*HuVg-QIRE;g>jjs6u#XYhI_w6M{#bB^3sT6TjwAvl~P+HdTT9godGj$5JA3*^n)82m zUw$++1U*zMKVW&7h)+(}4lMX2{Sj8(v#CDjXeKifpKtG{{4LRDNbm_sWFY=t4Cm%# zsTkEPp3Gq(=iG2y^wjkNREwLV0Q9XK4^+u7n+q#iBBv9vYr2X6IQt`=^5;4D(E^gV=Xfb{B>eymGV4adZ{5b^ z_ES7;D(X5X=rU`_S{yf~Qi`~tSAx}!bYq?`AS*6;pH=n|CE{M%74GJka2gZIyMz_X z>Ui^;7WoLAXJ6uD*kwuBcsFE&chfK*{1=HURqgyqIS_Gu9dx(_H@)190lFx&m_Lfl zPoZKb}L$h7NcS-HqZ+f}C zP_CJcnWT6vykawY{XGe`9*oU&fx3Oe#|mybxSuVw|KhkAMH2X&X)7y$$9a4ly5QxX zYKl;vm`p|BbPk8;Db9R-TL1uMlViVS%`eW1CAb2d>oFS!NbnN_R8-!W{4w(v`5O~V z*yf@TzTVyO%M#P^Ym{LM6V4UKPRoQ%7`4IX*2-6z&92dpOJ@?Lzf*t!nnBBdF=0@H zi^O%@*r9j!i8BP*oDHrM7b$T*B%CFYFu#;(DWQS0HKsTTUUig;747)QyduM$%7?o8 zu~NTR@DLpBoJ8G0^nGREwLYj)hdgibn7#))n?dI#pJ==2U*+j#MfZFc$pBqC@yWeN zGSFDiLidFju5_Fz>nn6Ab+qh=;o6R7WsGJftSWwIQxRtG%u!7C{g0T765hpvRg~OC z6cmn*OhX+YDlHdAe$cpAN6wsbq#S@dSRSQCk4v<>W6bY7Y#oA!FhFP=**K)ez-wBo z>KjC^%_834o1dIs+5f)NrP#6y^L=u7BZMyOLUjaw;{E%qfG9gPU_s9Oh37?NlC?%2 z=8xI$yFPcYAODSKy-o7@bL zUlN_asv5G&-(J2f$^dceEzE34^|WM<<{^qPRdVh=bRT&H9k4WG_ZXn(4kR|(P8S^2 z56JIt1D@oxQm%X6W{DezJvD9dRh0F}7_#+5wVJV>46z?@s&EVmTKf)mjJbjW%Z$9zwkHs@+BVZ1eIg3DNYlmup=j`~H4u!h@@4G@bOWq+WAG+~3Rr zG-}w-G{G>j&?lb(;%OFYU2RxYb7+53AVbL>xz=a;Nuw-)9Xc(=L2s5lo7ONUdFSZb z({`mP`j#SRJt5Hs#$3vMBYo<+Hfp@AdZA^cD9htkDo*;&rTgSb>{i#@gX>ciPa95AMKylh2rf?8JJ1)$w1GoomXyo6$(ga7pc z^O$~Wjuown`b6ftoAHXN4$%TGEPbp>7;E%;%g2H%#+sPR53h59!+DH${bEkCB1ajZ zXAessjDg!vo;q6irn-G8TEU@c{4v_D12Y<b5P6D|%qlRDGTP*h+LE|?LkP6%1Dw6ov}4J|bZNF1B5 z#EPZu*Mr-WkzXhb5Z0P&+Vf6LeY3#D&sNBEK~|3Bbe8CzibVVqV$1w@SBVU~Q1^iW z*VCpa)&Ul;g?U+KnxbC80I~Flt>kUuVld$91?uZ3=F2q(mUkPU^^(fK8|JWmrAOEo zbobh@Uw~;5mrBjFj9bXvbJn2+*fq8l zI!6K&eVg3V-%3V2>C^*^GV|)C4!Y7!2FMW;uBB&+smp*ZN-d|_RV!WGUYEZ$z?O4= z>w6Tuqh7W)8?pYn3Ph7q{;EO;k*6)OVczv1Z{gI=xaH9THUEJv3a96xPv8Z~l+qGx zUpxPaqVR(Ej#-md)2wJVV*kj&RV)KkoAov3>9?@Mvne`OuRT-gv3Ak8Rv6bkPq&wO#V!lCx)q{L*d)eZ zp;hM5H7GR6el&CAiHsy^skklWzKWCjQDAyWtqhR-s@NDI}6tb*|y1)QgPBB2Lwqz`RkbDd%lT4|KUt7eip3k~!T^ST0TirYcB+DtzZ$lr& zt??;pXrLRdhXGo>MW|_26{&96%T*d$%>L0w#BZJz{e^_NQbxc-WqEa4TlbrnqStF!IBo zMf(Uq!Sj9YyXf%s4nJ+2bG?N~lczza(ab9CRM@SED4D>r81^$SU+oy0SQ8!Sjs^Q_ z=A;&9h}RwhFo{V7ctw4|=HpWLIY~LeneXWX(z*w)0vS+9Cn$OCHm|p)PQqz=~FBIt1g!a`)nu&~RPO(p! z`J}kvw(o#kr;{!j(Nn(Lo8N}Mv!mN4{FH04p$9?O+B!`%VPna}I0G=gnl1yql_+WQ z9{c+twu0xXS7Ofg9NXlktwY?m_9^Etoi~o?KHt{gs{$@7wc3673fRz#$w1HY_N?Z^ zWrZYG*6pMm6_r2ZUi0`MZ^*QubpGwG`)v=ipM8GG8V(z=sLLKJX%zo3w)m{wQ+f>P zSI!}W*)g~nb0|}?pUYCM$Y8#Tth))WL`>gJN$InV;5M;6Pa5cJ`g|J;Z~TM{qD!M> z<16}#qFNP8L^>eH4H^Q=r=q74Ow2QY9S{PSPb*pZC8!0W*EZto?Mo7T-@6F8EB`5vc97Qu95+I z!YhDPO?9tsu(Ne&xK(;(oR&)!>c_R0CDx5DMnZyRq`q9XS2u*O@St^5H)OoA&`4*8KX1pQ`&Iba z^4(#zl*?~zLMRCCU$DfkDH&Y`h0YLXpmlp9>W$HI=EeKX18sk&fPG2c|+y! zelMd1PW4WiK}{&f@ouy9Vx5C>b+fuR7wx7wu}IdewRl0ZL9&zF$fwTTb_H^~ACB|i zcN-)y{JmwUtHqLt8 zb!WNi+!U_Qx#Hz|!`Qd;M{|yG4NC-??06-dDtT_KoUB=je$=Xg zudR;OLb)8%7^s@BV;LmRB!c`3w_=XDs1cGVYSDEeABRkaZG%cYgyJaXL=W8({KZuM(_%IMJR$`6!P_i663&&(KC67bkzJrkGrkaZUNJbJ9< z$!M6B`_r!Aw4FCMpP2T^1}UD&yplZ=_5Im$Arni&8Rd#)+Zf@(%c-KB)6RJ!3gRqJQC>}cV^t7wOe(QppU7wV1Q`Ki#QRXTvPfx4cuH7?Q4{%n@5iAO39jQf`RMKmg~*0Y?gAz#G~L%Rxz}4NvXt>()^36c<{n@y4vp zSro~9{we2@DeOr%c8#-l>q}CNvb0uo(S}Xm-F`on>81D(%$fLvtcHe5r>;&0n8$|9 z1I368P*onW_GL+P%6@t;{$j=H$z3hS)ZZ!Tjz9??6HEQReOr-k3otZ#1tjc=u<{t8 zFHs2Exe<%>F{szQ=Ti}C>}kq*Ot*lvfCKbaJmk~ccWoJ2`R5IS&y9(-bSTExBcve< zqRvy^D^;&HP3VWh;%Pezu6qWX@yoYb=)S*)6CwuJ1@E16=No!WKy%^PY8@gZ!)Xa(%KYU_37gGbk~1}L!QO!s@X%md*-kxMYjN;CyQ2w#T$ z?e!!;a=&~uN(ams^v%F8UT0cVP_oqr)yY}ei1lm+$kS#AXLs=miTBHxy%;%Idqqic zlu9wsv%h_!ePDJvtek0o@00}jnQ@a6-;M$M;jf}W#&>gZGxEY-YiP~( zZ&&J9lG$}zUC)W2eXMk_qmZ$wCvA=K`^^q0_}+pkBH#(`Nvb6Fu*BJj$Y*=Qu@|p% zI(Da4Y8gL!AJ^2Ih?Ur;TR>o4(~g9$RsV1D3#w*=ms^XJpWVvRy9kMwUynW&oH;yG z$jl9R*|J^NgJ%q(ofsRAlQzTLi{IgumQ_dUI>&8j#-$}?Z?vMs<8DVn?*W5<2h7=D z_4%Lo{g0AJ53WGjlWq{>ALCB=t`83e=bCv*yV`$o@-E7v;}vL$D04-P@fSJyVXn`^ zO9oH7+eohzPJ*aVTb|9yJI1JN#f(r5;fW}K{!@MSi?IBb@W$`Nd~0GYx(x5U8R0b0 zu!#S%z-L!};GBX6uZy@`oXTwKiWR8>5-6^0*wg+wd#${3i6g}q#gRPo?qZCZNDQTZ zpzwQ)H@K?}?*46XFfP>C7*K=y06pfRzQ8$dqbM~B|J3olH`Uf8@VcPYOZ2)R^|3O( z>zaSe>ubp(;73m7e9E#)zD*P>LvnN=S3MgYETD;-|(dVVegVC&T4#F-R@V*wzl z%$dEOWe#T*`F7;nbeGL?S@0)dy;g!+xA7m&Us=KZw!7Wfk>!ig_m|GNf{uXfOJO@( zMzLEurK0P&Hs87zF0nUxHzIQ106PEExX?0`LQoSx%kV@@)HUXQ2yE@9gwy z8XCV3LjckXTKn+FafRNyFb(3wDVkS2jUzpbpO}=yL6R;4)Ju!Pj-Om)XtS~i%Cwbw z5N8`mdzU`$fBpI`Aah0g5ZOD{Hi=jCwzvk|BFacWavTD^l zNpgG^0j&$a%^a|meGFrOJWC!QgT16$L9N0lRnp z>9U>kGB7{OQ(fog{14F{j(KT4>lMDwyH`mb0fB=Pu|f6E`v;)&!;b|I2?~;3Z!Mqb zFH@zRvNx9HcL zIBTpasv{s@d;l;|6|UPVlWWi%Zuda4rpf>C8F9Bqa_kAW!;pL^OLCycc)^BZna#~tFRy(E zBpPAU1YU*Aw~=<-DHG)Gx+}F8Yg+SW`PuC^^k+^=r_pv(jpYfp`qPx}&y$$J5#26t zfn>AWK6=LNjyB=DoiwMWWxyOUbFMb5)fgv36W(bxTU%1gm2EZ9Oga(1uQ*{=z@okh z;gF}^FXTfOv>SoWncTO3D0zzae(ROCqSYG?cB}IEljO~04$-lQtdE~>6^pyX6J=N! zpz;3nTsBE_o%>f^uCDZnC4O#J{mnkrPF=rJcrw{q3$1GUIjC>Y{ZMezRGYd!I(>3j zkc#S-(GV^m=3#mxEELz7QXHv~#Hpd;r9RPXpG(L|X=di;8X{%5h*!0`U-{0Y$_Y+{ zSz25A?p4&+k4QOU+FmYka#nW3GrYrmmRH)mdGL1l*E3XY<3#M-g>Og>@(V-a(EG?f z$%%qOc8@;yo6}YAAf{W;QQ4pb((rTN$u2B?;APgkBjD_f7h9r*V+x3B@H`}kp0$qL z->*CTGJlV?K0d?8oyLq%J`wfWyd=q1+A+KYV%i1=PagTQ?eSh$K25qCJj!u;XwFTG z{qUw^(Cybjjb#i_xILmCIKG8B7j>201e^KVOhs{@S+bp4R4Poe$Gst+sa3I0U|0C6n|C&?Hu;x3$>s(0&xE zavv0cW&VgS^j&3T>N_YmCrksDZ{H*k`C+~&C84}f>H3;jKjFCdgmiiv{xlFD2(Eph zlOOgdKKY@qgS(B6>`z9!A-$ujW5Rc6el+t>4%pj&2;TgDP8hTwc1M8=7Pxk(7UXYt zoN4v$4cIGNl01cX9e&MTQKx6~*`pCiY8VxIST+B=jrzEH`?kNKp70a;Beg8>XB4E9 z&aWToeO`GkUnTAt$2Uqg)fXLz+(}i786a;}pACx{zAC-y1WR@u1yA(0_<7H&UjS5L zg5$QTWtEq`H-S)-m{nnMWihyyz==ogNyj`SE2wogur5BZa;w`QJ*TN;Hm+cLC!JxH zv7(~NW*U%=eRl28+NiuJB{78>Koli3*+=m5J^l=kujlN@!RN>1rwY1R=x?*ui_86N zA)^E=^WNb3&taRFYt#0%UwsR$iDv`U{e0X#M1MI8Y~nT9nO@dHf8Ip`qdyZ>=^DB( zU~>Zu(DVcIH!Z$L`0V{+Yk+cAkDl#eJ0kh>6}}0LX61W)7*Hx}S4+!D88ZLGCK$v0 z4W)WA_++4PM)SN=Qyox*g0w-Q06$JIivju!p$C|fv&!}gZ1C6lk+=P!Zs(uo0GJ0s z|1H?{%%vI&A%}XOrg)N}94-$!5#Q^|jM8vYhu#r~L}4j?r2}Rg%>(iqmjK$!$^qDY z%x_8dPf!=eW)>z%2V>V8Bs<@$w}dJs#Kdtu&VZ2h%Q=BDfMCS4;|x#`B2mvcO#^yD z>zQSypc@0kjsW6XG!Pqor#Xhy2piR;!Wf_gfQ+yB8ce@n=?53=M)!Ehtt&3kd2m$> z5Cz0Z-3!!sj=#oKhLbTsMg$YUmPJCSPGtwMN*P<9)*a_nt?W1gUmuJ}%^BL&Ek+O? zHN3GmPM5h%8xCguN%T>lx32f@q9Wc~vNAvx_pC%|-7u;V8je)-CU9{IRXMv!%6q@W zWZFM}^f4eE=NK;ssNdPcY#H<>%(9^n0JtucC!;-($Nu)(@}U&@#Y4ylpwEf}D*=$h z>8}iorooI*J_JYwT*PC+lNWHqHldyyTWy%>_c$fot;mWp&hshNz~kw{&{jO?4AMUi zBH`e66((m{x?aXM!b=Tr0cNmVitgu$EoWYpfE-Zn1hikB(otaYLAVeM5X#>apQ4w( zV}N?o=h8NL*W-cZj>F6~LTFJX4)N8et5IyBz*5f~2JHW}ox@Mwqy)O`NMTzb0$%)s zgLn$=i6S=xBoz0A0lK*dL*vWe7<90bRku8J7mzi_*kjvN8* zyf-cZV;8@<+ue>{1e^%;P6S1>%z}{bt<0f*-xC{tgyf+tp>baS$S*^?N=pMD<73cq zoxlW2@}ZY1Zqvd2gjyi?mN%e$RGjoaq9D}1<6P61eVqG%z{>EOx>n;(@s+Rj%^91w zJOYB+ue1mqd?cL9-5^?a9t-W1+wofiNPm9*9}Og#FhIX#vHdrZq;0SmcpQ*l?#tdQ z@P}2?d*jETQ;qAoy>{0QmhXS~z~qzE$wAQCojW%v%-6@jGr5|lI@G(*{Y66)<7*Dx z%_IAbiCw?p-+O4~0zH4@4k~<0XIl3IdDB(+lH{5gc;nFu@AsRVtgt}Wn<6$nQ`OMg zsbFB+O~>5aMm=z_Ol)3Zq(i^iJC6^J0%D8RBkZ6p=@Hh?W>oRE!o40(HP~8*9hSf3$k|a0 z_aP?g+y|UIEkTCqCJ~1?RFGe<<+?FY1xt`I?3xIXe&lamv#JO85{&}?ngT;gz20zLzdO1nC%@}$rqaXLYEVW*0V=TH!ROSheBbu%zzpfL&0)8UOXp1irL6Fi zQa0FD%Amwj-%r-%;DhhMtM1RBcM7yMM*cC2CXk2^>iPnV=~K${C4g) zzsocQB%7XjWZjd8s6!IV6df5LSobh_Zyu&{l~e~fnP!-gvt2a(+q7Gv|Kl@lYZ@=Q zkE8wJC|3q(Pe&bOh4uglseVT`zzT`jTLQF~6pbf*Dt{UYVSpww z=Z2Ty3{YYypq#PD9jl6c@!Y@&oc6LxW*TF|(? z0pYw+bn!|-soMQVd)cQ}j+X^xQ?1^qjM?wCl~*Eg4OU;1-7rfl9JvVdwYcEaaMoJz zULJ}SZcC0<=~R@-7_f=TA#+6-(=~BIAbiq6=XHWpo*u#_Q}kL_ z%Kn)Mgsf9o;HO&TrZFM=t!7!>x*hRwNkkrAwDaci%7&K(`{W|bt%Q2fXvY}?>IE_z z#zPT-KLfMMWjUQ2_#{I2u8=PT*NzYGn9t0c<2yNj^NQrViy^f>Ocxi-T}kW%+4A?= z+$KYA0T7uvV_Tg=r^@@jn(eVpoOYCLS|m6<_F~G%=nsdZcP?fpDZENc5{|3HA0p?D z*^{*t-BNAE`ZHBG&yRl!bY~+0DaakZFU~FXRF-BQa+9O{bZW^zB~`(7J*BQ?Q$%2C z3n2HWYjD`Pwl7r9&arALuwDqg$3Mq&n4Hz{VOP6P+Kv1qC z#dN6uRqMdzoOgl&XQ3<2_+naAsg4Y8N8nlOZ$qz#^w?5gPQ0UA5wotEOMy?-sR7R4sw!?!1cP21EGBqDA`2}x8ww0-4aF(SqcOZ|<4zJBz@Tvz`$!v_ZbIM?Y9h#+J zgDXbSDb?6ukpqFdIKHjh*`ZqSR#%IMHxGuSSXe4MZoT=+w5G)BiSxx+FLBwzMF^rk znuUt5TcS@X`#jm~)O>%FbT(->kd{^hUXOg{%}Lg(a(sFclXoiHM`=+Q_H_InHGQH* za9V0Ko_97%%co%ImXK(PpdtOTp#e@H@V()T95i>wpEU%h3GEGFNIt3qStiaHP0BJ} z^;b7a2au$F1F#AJd^;c7_Rb&&QGlxxT8P7x@zSxHD}Ad{!3@x{@!HZxA_LT>Vd-UA zBlW~n@PV!$@VN06L;`gO@O&}gTWtX8e{E8hXiFdB9=hh#3kGNy$ez7i21h=jfI0mj zwt!zS9tSvrK-O=4IZ(hreZ2y7>>2|!-?i3-^54=0?4~Yk?K>y^n}wY27~d^r<_18d zSA(|Q4`#bBu;mNWkoEnAz_0N~q6rmoyMZEzmWu#g>{|{vn_G)TXQGpE9J#>E7yXn5 z#5`HvH$}f>DZl__Ndt1HfrKGiVgJRsKyXsed92~j1Cwxn+YR|$qU`_f2s%Y>zn7?O}#kipDt*~+<`*MUd#3NHMJJk6SA$QrEjjJ_vKgd}LXHnIQO}HX7+kq=slVy+?G4q& z_WSJqb6x(qK6}V3bw^~Z>uTLRiF83qGJ#l7 zrhF=q>EvB!dNyY2Eo|sWnL4-*n}k6hIVWK@XpIM;=AX!WR<^i2y^G*G9T|6=mV5gZ z;xgLAVT~DZ9!Ih3A$sZ;lXk@ZwLF_o1*0rd$Wm1+ww_^6)xYjNO6^dV)1&F$)IKrz zO7_kp2TY!I5O38Gzr18XIZ3*+GwH_4VI@AAt_qpZAd@+k>Ah7=SsThs)4)%T=3Jak z$_c&!JyB+Qjuvy@qf4{}3M_)@tAC~M0?jAG$JvO;fCc-Q_)krjd2JPHb&($x>EIeY zO+9^MEA12)Z$GndN2CP^uYj>stArlfq`fZ@f^F1;+ihTbsQk%i z^9!EbZI(Y*(TE|<6RhE~#`cfcxy+6$xF0L!DQkRy zUs-`CjmnE(Ir>U%*DQy~&(+w!Jy1{11$Z@Tc$pV%x_nh15l49UD_gNHj@r|@VvE{7 z9U$k@uY8h~*;UA6GSGM$lz~152r`s9<52vL7_9`WwvJ6;{ zySS|M31lCJuIT~rU}Bhc+nGwt{wgk_pa!t9-aE1a+x?K`uWbOHg{Vsn@?@hull&=5 za@h4tz~B-02f*P;;98e$6#%il%5Sr<&kQbTOmBl=w>gqjdSeh1z6{ zqn8&{+gahQ@T1u#9gVcEIfJLqJamad!YZZwHt;NEr*&|yiA}u` z1A+j$Kr7Kc6V4wG&O?|*>!sJvuPZkv0x3abO3>;)TjagM5c7(v*px}t>8g(~({7wh z`n~3f#XrLUa6J`4T%w8y?&xxv&?X2XV*y|NgI|3efsbmJB@EIy$}~V=8;4=>U}8?g zk#7`BODSqeAD7TnYe*&iL>jyK*GQ*3c3ySW)%zXX&l$gXY5!pDh!cIixu>sx;Og>w zk;3~dQ~Vi}b-hVnWpP1%{VTDdmydi2u|&!W>S=|}0Ur!VK2kk2xoEK?qoA4SkQmAq4y(|s@K zj$Uqbd_I+qQXx$Sj|83`uytB0cHD{vvT%f9I#ok>dD2pVtFFMD{YTrJ8=Mh*yo;Cc zk1^6i*=uN>jqBwR8-^tjv$LuZ5ylNLn!Rm`WuO3iT$zqUxcTZdurd#~vvNWIN+YxF zS-CuHD=EEi)H!il?kJ%H$%>bTe^j@BM=o&1u3fw$W&SwVlT9glxSW7%axr!lm%ug^ zX@41nBqls%VmrE#h*u@skkS#voEoe27hUJVE7Nkl*N@S^3MTc?Z{fp{asSPyvh zff{c#zn%4sP+XTT?B!T#-|IWN=%5~>fOUCKvBaEnKlv5gzJ*xEptEIr-XY_nZr43# zVlO=2v+I63qOs1io#UkWiE1{M390n7vz&ADW5HCs{aTL(dq$DskI-8qNGBukocYpP z9Sf1rXOP7d1M>I`QhBa#GI@+cWhp;$Ge3=7laA5 z!-AzQsGDO2g*lP(YH?Su-5?2BPll1Em0py2!4~+(hso+F7|kz5yU?i9V6Wi{kJ4^R z+(oM0MA~9y`_}#MI9Lt<5VER%Tha6XkS5mN>N;ppwUM|4$Hz5~V0g3xwzyh@tt@Ls zaeboKb1(Z_c1W&)5A)x`h|u5P@Y!T1mPm-|NRvbKVv=%|KYgOTLy5~XyV<3h7CKlV z=xR7jm(%>>47yJ#`T&RQ98)&LgpR|4 z>;S9U3s=8_@Sx-M&#@bN%qseY>*es*qsn&C#_h3;8yuBnj_jt-c{E z;?zg%o+Z9cOiohOx3ij7I?JRd8PbM9lVVyvk14TU^L=Dkb?W zK3jSQZLl8F(aM!P&1)cgwu4RKgW zZuMAq^sFV|gsPhq%L-(!eVeEwPrWLp*#`0$cmM67mk66gsvarGFS&oF&oYl_VwTmD zwEuFynsaBW(wu){7&c-`Ml4350prIEoLzMeMukIub?vbRR&GA>YV-TRDuir~ zR|Z0gKs=Ht0HGfb{?&CU1*;UJaC9otIfvkVf?RIdp3>p^;LhU76p#kjQ;|WRkos$$ z*ztRbsc`k%S`W5y!+$;X1I4XjFY%Qn_;8w2*0ZOa**uM6%20BaqURfX=l;>**0Fp3*@-ZS{6@&)&=MtdU&4l~(;-PwEt9sH;JllB^8qn2g@6P8mm1P8BH^Ws?%r9e zYpsicnIo!vua1{DdIp?6UefYkr#EufSL5gJyPN)=rLg?XtEOt%Ewx57Mr$F8WO;K( z8pMq(dO-Vlm|FLB#lyii*MCB(>6C-n=!DsBN?e%3gQW!FIx(XM#Vmrmgx)Eo`SQp( zD6!FbrGm?+Z(yi90mtNJ(%)X{IM=Y-BbqXo?YbK#g!YuO=79bK|buDB@;t~YXPoTbhH;M|5O>u z!z2^l)Qxy$VOk#P$l`y}{_&%n7$2WHJ&R%LDI^5b3T$XH2PXr>Wr>E1@_luYIZ#vr#cO{3&4J`*RvjaNun9)qBw@wcgW$Pk{{3OyTel$-?x%fR_K=k$V5# zd;VPq%RohIJlEo%qhMa?vr+{^y|P?`!55PTV%WQJp_qH|FCoNGyWyUa+r8~B8xr#= zejg5VFS4nSqq@UkHcSb}vryDjo+CRf2EGWq69=jG*^| zcK+gs{MjM-{c#2k_mENU<)JeV)-tFTpl+q`TYY2yh+97N3M@vcSFQsJA8XLL}-#^J8 zU)P&w1BX~93wSr5pY>Q1ZQd3j+l#-rbASBhP0;T&7ypi9VRtA<2uc4G(|Zi$QV4n< z1tMH8=1qZ|gM(kNH9=u8)nn`PO8ESl;k*~s_VfL+#Dd`SQV)4)=PxVP@k5+v2Wj~H zWt!W~RI_1gNzcfQYXD2u>42-LrlhXICn#x=HZe-TW>EV959_)i zp{=A;Dx2pFN+rYOo`6z!N@~-mzW#S^R>Q5F`kw4$pE210d(?w5lQo4(eMqtWPN(Am zE)dHJO82U`m%3YaxM_^Pb`yu%`jmcoQ5pX2hmzQ)&?|KjR-Ik8KNW5K#8&+C4gY@! z2L6A)_pby);zz={-qsuGg=$pnh`^(0N!};WQ5R6+?{qFNoGFLG{9aHcM8dUkdA z#ANuzbb^6Wd{6SLp;O(koYhp_^&`d3uY*Ry}5m;!~GahirRHKg0Jixg9 z^NZua>50>_C77@uKLXJ|vPSwCV z^|;rPg4=mmY!e<`;8~85Z$|@I-s*mQ)vcT|Vr(RpV$KU&X}08?x^B>Xy5mtCy-2cEBOBpeG3o+hhrCm8(mp_uW~vsKj$Bt-Uj_o>@LILDdU>?78 zVLnYE40!-`nH0_d1ybRslD!|*Dr>njZQcr2WLlc_is~oVkPc4OnU3y{^j9mmRq!cJ z7E>@ZwF*=iX!;7m-HL0BandVnqITsrG86~?~0?D^3BH9;$PlAKj)bEm^+NLyrMcv)Qr2t4=1 zhXK^3l}=N8oGX2pl{xhUUY$HgQlXl8_o)R# zPx(Hiei~mm1$)T=>;_b-MtL6NZD)#)D)-f=F;)a684 z$D`0L>E6{$oqodtZqqA#m;>>pDXrMgmLk?}j(1K+bR)1TgW4|L-WTwG7mJ^ZZ|9)` zpV&8JbTsr&ty&Pqy!|3cL4uBYH7WdVrYV%9w&^U6QdGZAKP%giIRm6w*GV-=9Ttbb zGOP8>P-USj*|B?^KcUmE0n}>MFW_p5(Q-o^4eJ`X<~ozH7H)V-^?LV1LDKWxY{y}^ zGLNJ)cTZM_>S!p<>=p*TB;v!^e4ZaToIRWlQ~s7~+nbEEF}Pe{Q9iN>>x zF8iawLBBV%glLY3&`*w-yIWPs9&tXhFz}!!$jX4)GCGI6T<@#d&n#Ys+WWq zx!gU8Hu!Qg#XmAk*D4Z8nbaMk2%@FUS|=tqn$n$}wo+PN8%$xZoN!N33o;Zj;k%K# zdi<^B+sfZ}AuS@S?zL;BFHC1XvrJO@b`y(R^VG@2Srwl8qWI_p-|dBO1n)?>K4?F^ zg_BAOrf>3~;~<;k5ZW@dhCYm3St0>sHrkC6EVa2^M-j>ZeNnuG18tFGJdklsNJHo_t6r#G zew!lqtDM;^b&Lq-3T)IeOO#h@FHVmEdb@JL8{yX^^7WGJB1I~GwONAiPHd&>q57$u zfT+GaVjRwa={)@!Y^CclO`hxcP#&N#oT0ru^ZqX#JdWyhYQe1zUx||Ms7^1qf5D-~ex_ZQykp;_(d*FvLOGWf>N7No~ z@3op7x+oRe1{^tNeLXTivVQ$oB|plzv}KB}Aqdnw?K-kSrI}&Ihe!1qM$mBe21%d` z6))-GGw1Y8n#Em_j`q)T2(SSU%vnK!_N6CM_p^)Xa(DwexWRU4 zcS#|K$g`*4*%UbU!QSC?IipACY(yIY`D@xNbz>j@l-jr_PT7psPa|9q5MQnlK6}n7 zNeIV!{RZ}qr{Az4s$W~-G~i``5`kVnC#kLT`nFy7Q`d{@>iK1d*H%yM;WTQWC@Te| z8e$RY5R=_=?-tn1*(IMsi8}`O!t{1htT(`=yA zoX`FQW3xVwRtn8Vs&rlVP^!heG?t4Rs(hXHf7<)*xTdyrZLHWZA|QeY3W^jFr3fMr z6#)SOsi8+fK?p^f^h8ksX;Bam5F)*Xi1Zp25Ty4YAV_agLJc8_-->6>jLtph-g{=w z%y(wy{*hk_dvDg-d#$%V@ADe1o)@E>AUK|`O0&05Y9bi5R+sb_v7x*QSYN-5=vR-%9VPDDode&-uu1rie8H#tjgev+ zLT8JjN32Zb7c(8xr=3mv5XkIifY0ubcM-tiwx>Jn|G1sO*Vq^OQF0ppXOXx_{2232 z{o?yISU>q{tiKsTv0Lw)?Q8-0#3#337o@{~7QLQd8K!0N6JHr4#(HW+E^^+lOy09Xp*BL5$p8Pr9NKwX<^85z3H=54d z;p_a8{Wz@dJbAx(?n@GXSseS@9SOI(ZuWojekKda$f<0u?`=LE`AT^|dM-z}v3f!` zVtmD$tgFx~#ojofu$#SGNgdYu8K-DFoR>w?HSk7Uhj%vH<+ zPARdxWWpd??qsEMyY^?wR(5=oC}y48iM9>DMcC1{w&NH|wXtHM5^J+tGUKVTd+2-z z*0YIK;e55?Mz=ZkEomptv0fB3XNVpO%?{S<%-Z`hm{WCC#8@%6i;s7jZLnAI;0+St zg?msHK}I$f6cwJ>Ex?nojB{X&(XOt9C6}R|E^-tx4Lgm^vg7y8q-CA~F4ZzRZkNz% zRLX#j)0ezQn8U`2xK4sYGKs&N0hfNo{upg|A&DF_Qav&`lpjlY-M{ksOE~-U87297 zH&DItU{RL5<9M;plgo=ziGuuvcHV+n{S3&au`SP^;k(sS=Hh$uyW^VW zZ-PA%;;gsKig;IkZmKM4=*)AW9-*8FcObKjLHfez#oOQ8l>hE}{|wH5ZdanzkVeqR zZL`lpJu?j6MUDdNEjNzPj?Q;iY7&bcNaD8JD0LVW2qa^KT*uETXJV$hD_63qUWy$B z)U(D+X6?dRhTC^t5l7$g;&C)(&E4;Z*0siG=HjR;xbJ zh?Soy2ARCpJC6jmK4JJEyGg~=;hw~OH+Mo_j`_Widuu3KfyHASmzE1^CHrczp z(w<)Ux2~4854}%C21Ld9ZZDBdwyOhH3(nMs6;^G2hNW7)ISG36&2@p#0+|weQNxrT z@GtIv;|TeAO7!aKfjm2zDSv||Ia>4zThZT|93=F#XvQfdjo_4USO7^mKN8kjUg%UNh2JV{KM`CS)H>lxy z;xj>Y<*6?d8bNXZ?^S+@R*D`ojqujDqOCw@5;h=vQFX7;N#Dly#_NW9IMvvPNhbCL zxsc3xVAzYBi>}lKjpuo=$B_ZZ9pCJ|Qeb{y7TLgC4s_#m&ZWQl&ecHeM*8Pf4K9i$ z&5*_0ZYKtk!dJgpE;ueC55*s-^qD-A%&H8rg&B;=6R5&6sU@7-QQAo=ByhxG%?3V9 z-7_taSownTxo-hZ3GRmKgB7Um+2kSR3kn`AbICIFM(3`)e; z9|ZxYK%aXcT)L^sJ^XS>x3|vgqp#T>OVxXd!W#*C$<}8}^@;<#PeX|3Hkwud8lp(&?sX?_mHsMgXe{hH|^*;NBYhJW_$h zLA@#=i|tEivrT>ALzgHRP5HyMAufqCPFNiS`y?7tEpej`Yd2Kz$h{@p&(x-xBgjHuOBr#&d(O*eO zIf1o)B&4NEL2G$x;=71ADFVckz;g{ z=&H;mYt?0(i+hF?@IKlt%eiP~w7pZpWK<()sao!^mya@!>aw6Y>$0bKSPJ`L{o3bk zK)RTJzwLjI&uqQkV!($gn+F|=VuokF*9~uDP^Fq<>q-4X_;rr<6;!y~W>7a22)+Kz zI4AlbXbS9uD+8L;9fW*X&U?teji>OJZiZA{nm_&KL5MThQbYsaHGWF z!D;=RZB-(Trd7dGpB3H6XR&r!M(f?!x}hg4I`by(*_DOthFLk2D~pXAJEP@QHQK+d zt-DEpr8y)=hc#|KAD}(F^Js*E(zM?FIpJE#SjG-l!C5%z(3pHuF3u{4Z&=;Wq6mMn zo;KA|q(w#ae8`n=F3R&#Tawoaa#{&^=VLBB;6J(s{6SyfM$AAOd@H=(6`D6(%ts%z zG@V_MB|NPb=Y?ns_w!wQnz=WS0k=gyz4PeaHIy6W+!`0lMOCa(i%ZxBAanKyx?)Lr z@;|W0WFS?MhH&TixgTP}kx$R3J;=HFa9o&F<49RxMpiPxdP4 zI^zI}phd}gog+1bp*)y}&ZN`yKEi4&QS&;qQl3`}6OU0Nms->9;zYVCy zCW{pZ>Q+KFA>3V*ri9?hLQV2lSXRR1XtF*~ePr}o)SS*&hC`G@u-DwvdVt~wc@}+S zR@O&r>(aUF*pG}SQM=MF>3HYDEL{;gNsKaKDyOnr6N`nvG90cm2->Zu_oe*>rVbTy zt0=g@_v8NRq>aQgfy+bT=9W3_X4&h#{l-z+Q9|}+L6k3^A#87~?9dpV915(<}89vjL=An%`I`ziEN& zCb&s%dlXd&K41~h5ea?d#7>hbaZ(H^cNDb@ABPT^KKHJyJZi?=Q6?LF*TeTsrBU8m znFyQ$iJsJjg%<^`_?_D@8{jXCa3XFsG}Mv0p!`c#_g^xjA~(YM%2K|o)= zT7P69fdcBHBKf-aaQy3=WrCyz$ zF5IwxVt%NY6ehkhCx>S1Qrkb0TzGE!nx+Bhm1EjcjrNNfyq^M)59Y4}N5BgIYtgEH0gu=RnndS_AYhO?NCY zbK&dA+iTJeG$oe`_-M-oupx)hJwScF+*}Sf0w#6M8r3bF?gGvJ=v8?gB~47C14dAv zfx>;N0R%uQR7Otc4QBCV9|{EI$jmvuC0`kS(4(ZQ4jVO^uystq7|$Flw2J2g*u*Ba ziEM`(+xm<@NHi5ncjob25K?UPHy{!~YYF_@~5vzCuxD1TCllIwvfsWrZP#1oI_ zB$aPp7h#IO!|IB1rm;1*Df*0Yo=)IawW*`1u?J&=)29g$eYAWakqXZF=1a(Fmz^@q$nLl$epYs$`myO^TFdfpW2NW7Ixn31t`igl8L^VI^pPg5SvH zMT0SHe>$04c_k;l62hX`nkD3r1hso(9x2YWYi3rMLZ$&TT>pfDzSUz9#c_X+y<&F{ z@MzW1^wO?eIP#!Q(q(+AN!>*Zrvgh6((YBkl`qr_A$rP#KF~vx_6_Tte)tO-(~}f_ zEywVnPZ0mdI@BSv&&guGGTewN;g1r#6TFg)H1?_~Z6b$4riI_MyzIXLh0iR=PxuO@ z$aI$GV?Lpl=z4uUZ}|w=i(AA^+pq<(Hx9oayLC?4EEDmlQ z-R9nnOTKD|+&hyeYg>Et?YP)PCic{fGVLnyeAb~Xr9NkelgiW--Yi1N>nwVUoLshO zw;}4Od3o1ohH@Qa&=k+kyh+fgsLrRh!CaajVIrqX)x)!)>WF0F)_L#Ux7W``?XUJ? zJoLz!?Ehdz%_RRyl)(A-9+VJIi9A_JQ`T(Cvg{t%Udw}aw=`x-2{g!O=Vv9uMHfD=+5M7Z3WO79n7rRbT-ofx*%7GvLLNc}*=R{A|Ej<5BX8h{$9>TlG}sK66> ze7`vtIryP8$|HOH44T#`{N#sk{=n#T#Uy-LSfd1|lWD=rM!-ZoM*T@m*2*daH8}%T z`+8i}|}H6&XBeORJNaaaTvCEGCOI+zQClbIOtSg9?>BG1G` z%ukFv5Rz8m(ck_-7JIC@g^w%JA`Fp4@&^}(P;=okCQUx6hL>0;m9&fsGS(r?7Q*Id z3IsbNMh>)_g8Ruf$-M)1V_{zVHg4c;-K3N*rTC1c3#49WMcKV@ahf-LOR z*|4ljjy`C$6V(k&At~CTnVM~rE!TmR7}D(ZrAvttQLdRH8HWgj)kBYKx#7he@3*-Y zOrZ9Ihjjwj5GJTq3o}2MhUA$u!Cl>H52}j;fyQ%A5td^k#Gd0goP5{gU|#MAz~XMl z3hU*0QsiSmf7%tFn-sGpi(zhaY4XOQ{male&jNR!vUTV8Bbl!p>IlWKMyvcz8zn?0 z1wYi%MiOQgY|T)t;O&5)2QmxHZ-~c+e$yOLER5vCPeE072qmj!rWMJLs*Q-Ex087qqv3qx)p&jmMsF#Os zRxPieK4b3oN*7Yb!r`Q)0_fI^n`0P_6UR^lF%X$3Mahtr0^8$Mblfg$>_X@)!W*ALj?2T;I;yl4WbUZJCgAZZJv(K4UuHgA3@ z>Tp^XrKNWfy|#;I2t5k6Mvg_>uJ81ik!$p@!#ev4ggiW0UjVYt2jsJs&N&XwbwS^O zLDr@x25u}Zv?l&!O>HS4-8#xH)pl0^yS^Wabz7N0F>RuCmYy5=Fa?w<+hhdsd~KYVw# zI#F9N$4hC96>gm{{X>V0_fTuo?ERyk-#TdW?kx7O7nWy`X65(cPz~R)z`E%>dVV`n z2^plUs$82_60HJiT|B}lR1Zi3c(??#)qUWNaFsr?vc0lwTZrE&b|cABs7SMzXNjqq z$_#tnJjhMi0eFi4lmBpkiraz+p(*ifnB%)kVx}sO0e#d{9*mEjZ(m9W+i0cqK<46}pOVlbrjj(M>8UhWkiwxW0# zq6i8=?q~hy$$k3H5(+Iy?m>9E6v`AN!|sL!OE~2y6M8nfrUiN+5%Pi*rpi{ztlGOo zHLAp|_`d8`w8K1vuugdVMEA)XUqE9qS>d5vBl^HdGw5l6J?N2w*`-U z7Sdg2ZSHw>JVPN{6P=DlvyvS27Yfr?IX}hrnTYbqW})?Nyk$e1W~61FGkWzcGWK`W zm%+Zv{siT83`J3V22sWMM#6S$+7vWu9PJG7#iLdrhIe%t=BS2c|wr^q);gulPGMRbuuxKY_XKoCA;&7U>|1T1SRO~;5;DyN4&&6 z1Z-X9x+|#Y4Kpp;kOJilg>G#$dLA{5=LhK{6o>}qh)}XL^2(L6$xP&zC*L1)hvns2;@lfr!jEsOaIADgh0LG(L0$ogKohp zXT(k~w`g{-mBE>xcj-BbhxIv>DXg0K?^*chBjhwd*jS-n0;-0OuuC9SowtUijv^#z zvCMkh@oZR`tXH*|N9ov7=+bFQsIWXn*3U5cY2{KbX!&vr`9MSViPjhNJ4EMd!7ZIK zhk0<*vg?G#<1z-U+wJp;-Kn}f&!~zziO-TaatwtNr#j;V1v~5{yAHpP9Vt9$dZ|Z> zDiJWUEZN@Uj#jI96FAGcBnZE@D$ zvq^i0T#bl3HZ{S+@?$|sjOZ;V#xdhaq(tZEH&OL*s6G>(Mp!hA56d(30s4k4E5$Kn zmEtaquRx*~`Pwn@#6*>C!GZ00!~5@#S@f$k6PSE{o&^8PmI@npTm}#7{<<_>`s_`tXfHqM$~G6V1mY5uvUQsO;2kIhcxy$d)z4s5 z6PlHD%U`63?cPMVvgKQwFXjw?2HO^(%wW()0#b5@6|vHd3O%Tt}^-itE7X z{sy7}(uOW^e(%>mD{++g%VE4N>@~=P?cRE+a(+d(qxQw?1ysHF(JOrb7oupB&S$yH zHu$7Dbh>D@#3ZdfL9f1|))47oJCW}mCHG~^;Rqc_tJX8nIW$UgG#e`ZJ z`$NybN@%_JD+6WtpML*dX|S87Rf&=|Ko-am6`ixEgW8S~ZQ`C_0!B$QfCNO-d8E9x zHojGM{8hpT5SsrG7Ms5b<)ShZxg02*z|RnDQ)FANolwqn{{cJz{oZ|m?*!Q<_l#-a zwXBvWQQ!HKEWRzGp9IuiX!QWITA^2*#7XCPqf>zseXWE|&JL2EV6TtQt1<`tEh zHk_`h3ET~J`?Y$W0-WRO9mDF$lwFmJ6wRd!ZgR3~9XsvqYR1u+p*qk^3*ma?BEPQ{qg!s6 zs(o_!oH;50;4NSZsU*&U6A;vSP4wrB1)78k*H#5C=9AYFlWk73DKX^iv3J}H+r?Sb zxw>f2k~Pa_Ym*QA_z4f^y}-b*iLQ>F zY6IPdB#$iS*u6Z8p(gd{yJo%*KCJR3G?@QD>Z$zO+D5>hggd6Ck)%J}=C!go`~@Sk zrNiMmvf#r&ujZ{gt)@zCbrlzJc3gY*MrdjS9Vg&jR;PclXN^YzF6rN##!;kaCb|H< zRJ-tqGvEAGOQfSd!P;{bX%*CFTA23PP&cx}YHvjtsPZ%E8h@N&7(Vf_o?$Ml7-?QX zmhNS%+$q{g3N}*@d5a(DxI0$>_Y_Zm>AM%QJ#bp~O&VXmJ;%YUEl)1KW$o>0qnt-& z)Ri5*ogjDX$#sjq?z}8*){!QykB5d9R&rUcB^?+W_2hZzklHCBZCQLiWov@NyhVj= zm#J92h|K##2J)j89C9jiHeKGwJ(OIJ3kdTsLv7#cWT-(j*y2XR%9el`#`d(GEn*n6 z&W;1Xgr``ex5V;c$X=7B#@K7f6BKddA@eb_oo?{xnrL+eMt#loTkG69z*-r=1aLoC zX!7sD!SK%^Q>Jmi^{qNbbo1B*_1JQMS(;Wb$wrLM_Hfi*DjG$WRk5)y*593SCA|XD zu~}{bY|brB-d@QSIf)Jy+IACT0W|jcb%L5b(=h@?hkY7=trfreql#uDj$;NWk3VH>(epSK3vAMX)ks zp}7zlWvL*cem->Z1-c>Su5R<4Ruk+gjmr!HQEz5R$*YuzG#EbE^ zh!yREzON9jBC{2S>}0+*;piVxin%cr!Wk$CP{4xN8HaZ>Pp;Osbzql0Qa@Sg0~ykgHOW@xiHDR=YjjQq&_& zVY=rSjc<5`_nsc@kk->?JaQ`q~jN+7p)ry$tN+?tu*e4 zPip()!_V1VLl|JrX=#7zQ@3_LUeT$oAZx2P@>KQp)6RW1LNn*y{VlP&KYo1f7uQc} zS@!MbFKoIu!|pJ=JXDySl{HwKYQ6YLy^V3pM(rycS83dw{jqqR=ws%MK23HcyopS~ zMARnUcD{!^1JHpSWM!4mTUkkYYl=2S7F{nLD`h~YHHWVqEs6y?zk#*`2;pfS2^ELk z{hC~-GprB$rXT)*gSWbLw&(X?!?)dQC|Y~IlC$Ma_{WRcmJ72_qijziCoQOmPT}#q z#>@Q?hYTG+%3%-k@!Mif)n~bG8k(AlpagvpY>DzMbDP1V+L2o1>#)*Vdtzgn2_~Zv ze}%t(5KXoenq^Z>9?27n+vV)`OlO^K=yk!p@k8zw@|pKIa}`-%yePQ!p4m5F&liBM zuF-8~*d`*+HHhp1<`?T0ji`)CsOW^LJw8hz*&x6D$xT_MI1pfa1#I)IlV2Anm>5U29HVWD4WF8VbaF7#DegGMI)mg`jtu1s(S6RgzYA5JBe- zS2&fQ>!x>bNU7@ z91na0c}y1c>Sz3#AP6?nwCE2(=L`KIK+b&c2e}OH0~fmSm7!3WiipOQyg`*IwDAN@ zpcYSKY5c(6bd5LANBg5j&x2!3qt?4!Ziz1jzO0zsjL%o8Ypi9q9 zov>2aqfp=$Rk}gHGDM-Ets8TU;KNsj)p=S!xVrb$+FLqGfP*Mblg-A@B+&FGx+bA4 z(2@EEfGim^^MPADLzUJ}tYgB60J%6ZiKhdhJgu64j%@q{()c^m*WVS?{^JWu|M#Vd zcToG02Pfmt6`oY%i|w;$BqncMj&@~?%}K`IemuO)p`ASGIwq6 zsx0ACmnN+Ji))Cy@nP+5;Vcy;Zb9}Vpq|5MAi_hZZ8|tpTZfRbKB4G^se#^ii&2iv zaKEhLbzQ8*YAq!Il}8;SQxR2$s%P}K8F1}W+JRGI3GNzptUqN?pg#e@vQ|#;mW>KGS*|zA{WKBbQwwet>OX{>xc}7dAQ~?~`05JU$-Fp21vF z!6Epox>C-`JVj`v)j8fh&lH%VaxFc%!|VLAFn!-LzKj1GGd3;6t+?%vy-r@$zWg}a zz;%1Ruz2<0qUbcwN)0{&anUSWXD-whfnd_YjiH_f4dK}p-1iS^2{YARJURrYFuA@Q zz3C+UZpKZKC`XGfl%x582zR6tH%6hi@&QL!hP=0YJbcvqlvd4LjBai(Zrqqcfa_&U zFT>8Iy5p~wGh7|-R_N8h#5q(+=m^}tslvLZzZKTfRn?U^t1ZIg>{ok(=s2DrDXL7i z2Sn%k%Gb26^_v>^2_ha3KUQ|TE*#tCZEa!8Vf(f;Dwj)ug7B2KDfpC8lNZGPeCUlBc_@y&Z3pgXM2Ls?KUl z$0SjAOI$lQrDzV?9ADF5lwi=(vv)mo{4~e%1#Wv*6{vJBs*dCu(6Zq_KPX zRY4|&*><>k+`vSr!sfB}%0`ilXEmW**(y0f`yC>_O>k;uIp>5v5t-o4ag?{4uNPX5 z;+!yuG&cy-JQlP1s)P@J_9!K49dk6A7;HQBSs&jB`Z3Z_jt+XAIg)CH#ic$?ZJ=#J ztDcMiE+JTVf3RMd{H_$0uGl!`yK5l7r?-zS<)uyg6#I;r+30i zEuSGY9x3X_=0~QlV$YHXwF=aNV0(gP+;k>wy4;(?Gfk~w+;+*2oY$T$VprG(rntp& zOZC(mdmH-Wb!mV#ju@?OBvKzUjvhv0o)MJ}v9s+_7{$c5w9$g6Z>PU3_8Z!A@uZXB zq}F{l5f2f`B>6-!ZJ5T8A{$dpSHz#cGk6s807gpMmC}%xj9MSFAz^IcfeK+$_T`fAG9Eo&w z89m?h6_h#g{8^l>lh>mbfvZloaz=W`rWLV}u;G5> zA+}2|ZtH4WFS||Z06S??^TA(XMa~bE)n!YTA!2IT67}CV--rpXDj;Mf2${ zn{JI4#`*w3!nkw%pvkHIcW_#YOHx@ymtCI0r2cYj(qBfU-028inx)ouf)<>=S!6rm zzc*^IICh(}_a~#=58(gd=rxyVHP;q)#%rJB;h z-^YDra;#ZWaxxIICCs%)EZ?*+bi0PTj7C{HJPZqQS2gCxi}woTIQ)6w$`pQ2<+X9p zk<_yC#80YZWL0!F8&5+i&nv6^qJom}QDyej55=uJ3=pv}7Vu;z~p7)N7el@;z9ZBt}sog|S>K-%;`@87iFJf?tyPomoUsGc-vT2V+o!uJdq@fNPl>K6jioD>> zfN{aYkjxi)HQkcO4;pX0luL0SX0qzmzqmydj=5B!irN~pd~2d>g=0H6dCI>W3`PA@ z|JcXpaXdT|6bC^+;97sG4*(a9Q**ezzoUOXJe( z*rp>zA3P3jRNk*$z_gdImW>=g?&1V`pn&{^AYDZM<~-j&Z+Cv@F8}6R{V#s*A1ORQ zcZa@zDa)QQxwO#2slwu$6DoF)`(@zz8pwva#5Q+Q_HR~c_>h+8jRk<&7Kb&>p((h6 z7Hn78gHrxGI-pk1ZY+Bhrrl?zaEzV#3@lVps9xDzh;ze#7+Ph=QhPRepk~!a5yotG z`Si?}!2#~t1zfoN5%(=1K)e=_`L?pFnYS!}g$skW_=0EyNKA=oN8RM1Njl%&Z|`a7 znf4?2(V4atWZVBzH@)zDa4j$%w**DhL;D3#e$X&r76HuP_l~r6_jfvn0HGL=agAfO zs4`v9s+of32C8trrRvh(5_A4HIp?28cKJ6l?`CC8t;VDT7kY-2!_K}AmeoErc0)h? zB_xU_NZDD^A^S<~ZRVQ3!nhZ&n%Y57pZk?z-|EZ?Zl}xbPft6uGTv&RzKCnHSrQH8 z*^!UpNREn3kf0(s*mvMhrnM13m!$48L36w0W}9cC*_t5&*!hF*ng$D@soh4OlHRSa zAKP-K9vEMoZ*xXFV9LV!ZNDYnc&HQ>D7xHmtf0tX&ux`Mo#7u^u*SghAJn6b;iwC! z_a&afkp^6{ea~Lzw5Y~Aho)@Xp=T{}AWaK6P?cHK2KdHj5bEVwh@Hmw7XS(=2#Fx7 zsJV(fw9bs`D?w9eQ3I~u|Fu)9@>HjT2!Bz89i>D!%kI)%$cV`-YxfoiyX)4gdD-5> zBSP)gKM|xo|6XMLr+xlM6QX@@VE!GSH$@C%AZvqH|A&_OM+J|U$-)y8nQc@_X!#f2q~>C$|!0{8)y%{YPTuN&#|dagU zp}?|S#)Z+2Qe?X+Oddtr{!Op%R!cqI^PX1Qy1}^-pEZ5pY-z}g!l(shM?0Ze_fgaO zswv}koRFuBScZzGS=xjT+x#FaZL6lmgbi{x#L-r)-QY~)W3cv)e(@j^?$fI|l!e4( zUWmPV%h(#Yd{kQ;_PAC+y6jB8mZIU&e~{C=>EGzOe@F-Zf8$kp;6du}VXst3QrPry zouI7Bht!=7&yqU#yp&^tm_B#tz>&oAh!= z{_OR!=nK_4nALk5Y5hpa#fgCY!l7x;U^; zh1|mSo&>4ulVe;e%q#lS&noX)ReQ3R+p&oyes`0`c8fKfmw2W+*~sb!p3Vh-DJM)z<5L1FQwD(zQg9iq4LI_<^^ zO9N)zQ%tkdru4Gr&;d}03zW<7;EMECx4<)OqZfAS^vTuc;=+-<6e*yZ?;=03W76`> zg?s$#25L9bdH_0C8^;;rDm|@zhmcU+rpQ(TlJ@^K>i!@8+rOfz_KU{LHMn6PyjKn3 zwH0u!eiQ^Q(I@o(wX-XK6@)NUoI%HDmV#s#J_ zg4#ACzo#=KwZK-(03+n`C&tN=(phy$7pMGgI$^>$9)th1pvJGw_g^H~@xOI}Heqrm z$;gV-h8tPEwXW6zRmUDc1C~Kd}M?uok{_VWa0c?^{VGhbgXy87+u$nOx zr>zb($`9)uliAKCy`s~Kb~Fo-X-m(SPE)UP<_8P|SLnI;|9d6g`y*i5|CW5&e|L^B zq-KDvjbfZC8Cr5XirH0Zl8Di>f7w08+JVqzf-8AD5EX{D=YA%HE!N2=C0X&h6D9&Y zWkNApwe97Vc*ReEa`|4p(hj~OfeZLR)Ml(C{Ji#pdua2v z@yL|Dx$-87*y&Qcxu+P*=Bu)lV@v$1m={*gEDd4vZ})qiJ#1RB`k3JwVwUE%g0sI&n2iL)D*mkLC8?G(*# zi}bQ+{F(J3L2Z8u03#g-V5Djyr{EQ#D@OnaL_197-hZf5@NeS`{14Gh#%G|CTPnr9 zSPhl(1=w3H&y$!NQGHwR8wwb#ch)e5$)@r^7mp@Dz4f||veXv?b1RU_knO#`mB`)8DW??zAT`*QdjdFJQp$p}{=)sp4fVnF$s~-V^88T% zfa~jbT*e9Kkebv@xtx9qf}~&k9|ro1p3cAUIsfVrwBG_Q+Hv9fMTQ4&x0fpbYRv`_ z1Rwz5=gp-P0HF8P)A3uHX+~?igoe&t95_}F_#Y|D-7ZYyeP|E(rc>`;>+dTyRqC*PBi=N(GZ zb6F4;4CtvtQ`nh2xtLhBjH7?c;Q!T+{Uf8Ie$6}ndwyqQYbux0U%cqNUu{pI-iT}c zE4hG-+bE&NL3Pc^Vh6`%4~-NcMPRUKn_Ut@NTHB0dDMaC55 zW7tmOiJ{2HBe9lV&udcClb8pp@uydBP!wiJFw>K2Mpq{?F3OpED=NAoGXesNBZyRasF?t*|aR{NTZsEg+?ja zF#Rw9iOTtmYQcK9fINfgc=&{k8S{R#p5VM*?puGV&O2G0AnO2 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058726_N4JQ1CR3DD_medium.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058726_N4JQ1CR3DD_medium.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..86c36082c9a38d8633d3c6bd49d9dc81873df427 GIT binary patch literal 47416 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

&Q4^+n=WlZ+B5cJ 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058726_N4JQ1CR3DD_thumbnail.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058726_N4JQ1CR3DD_thumbnail.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..c0cd734e588d951acfe6176b638d67315038134a GIT binary patch literal 13272 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058727_PC7D0HUQU7.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058727_PC7D0HUQU7.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..9243190e1916cf45fd931f046a75ab536a810834 GIT binary patch literal 63842 zcmeEv2Ut^E)^-pOMG+AqMI|aAB_b*!NQ;O80RgFz8Wj}*l`btIDxwmafPm5k5vh@0 zgn)Dq=@5FCUJ_~uDgWWlojWu4`=0NgJKx+H=l?H08#u?qVVAYnde^(w+Aw+;BcT17 zY8q-FCMG7(4d5S$K?fcdqGb@k(7KB}RqepK@s zT>a>y^Jm3R9(8oRdEdsxHT@*F%Ua6|+ma7gIz!GlLn9u+zvDlQ?;50N}ADRx>!Ok8aHB}{B=Z0x(( zx!Kvd#RLuti2dddMg@p#7mFO*V`iq~pdDOH%v?;2Y7hhjVqyh`x;@&TKbUqf1N~*& zwVQnp@Id~4&<-YM<{d1|tgI|7z|&~p|3NHVtOt&rQQ66@ca!b7BhT3=Vj$C$g5t4tEpen&@?bKGDciCF}1pV=dSfV z8(U`=*GF#d9-cnWe4oE~>4yysdmSDT`Q~j@V$%EM4=Ep0({gh2@(T)!ic2c1s%vWN z>Khu{J370%2|c}iqhsR}-zTT0XJ$#uE30ek8{|#O_PCfp%zqlzFC+WaxVV6E?O^VK6(fe> zZ^!)bEu*oP_s75HzGZ-fPekqXM(e@B^Km!y;5?u|+CMZ0Oq4Tl9t76&CYUIP7aMdSuWL0wMlpC;2L-2%OWM#|Cdx_X}PC2ne zEa;E+kIey%FA)o^Q97N76$AZ<4JSo=pQRZ5jDnroY4a`sGhBgrB{K^iV}SDS)I}00 zjUbQ6YpQEZ5GvdTZg~$I)N0+Zst5P1i{sIQgZ^m$*c^BV0IGH@?#+*?W)Usr`8Cbw zS~yL8zG|O*h6X{p@lSDYFm`sCbm7)aXL?rKgb;12j{enbv};yKqGiDPX&W3o6LaiG zRC@m*ePWxg;nA}cpYi3)X7S+gCuGGQ+Z7?>YQk?Hp_%Z)s+ehJWlea=6mr2mM! z@4x92BndFU?kOLBR5R=t&o70^rSp-o=zfiYGd_zGVsuwx7ust}7m1D>_3!ujHaMUc zGRq`I?L^cYP2U8+a}M55n*(IRLhXmd&wrSbe~M{>DAEbMTlc9-DL!9Ib3;}^7}L#y zvgq~HPUQ{~dv}}J_q=SlP}|$L!PCa2cgr-{JKMgP4BZEuke;SLJduCQ6WN}(AKM!V z8oOOs7-6$dC{QbVo%zILWg#-*BgO`GjqsffdYFEZ0eTE7ZxR@toy>l;K`C3Ccp>Ge z&{Q}0CX6~8>vhIhL-2m=U?o=U!_gnn)A|qT6Z7gVtQd}G6`02#kIX7YVxuD0b*pgu zEg$wZ+?!6Jeb`i=tt?4f=Inc+za?5_pZ&ZFX`#+9n3m!}GqRn@vUz`XFtK-820+%u zQ-5HM|D9*?BjXM53Sz-Nb+OZhL)?sJu5Vt%0J$$9X{_lprsS{dFFP5aJiNHA<{|NW zsrQvW6~j_x+I$hQO?ICzu4sxSmI=N5!>;_}cI5}RElfG}*_W5EIK13*W_3q7Oi}OS z`OGnG#5GKrtLw>O?Is6Z0aK|0N!IEO6b$nWk-Ql)oL{VAVt&*nbwK62=SqttBx zWIsCm{mT;}RX<7vq5@E-wS%n0}@tU+>@}6rt1=$2J30v z-u>eW9Fm95%&c#{Y*Byp6azQy=M=Pk*_fNhKV}HV0I5k01_*CAX}XH(W3!<5lcQ62^}Ch@^gnEYu!`+%DH%a%ZJRC zthIg*HuVg-QIRE;g>jjs6u#XYhI_w6M{#bB^3sT6TjwAvl~P+HdTT9godGj$5JA3*^n)82m zUw$++1U*zMKVW&7h)+(}4lMX2{Sj8(v#CDjXeKifpKtG{{4LRDNbm_sWFY=t4Cm%# zsTkEPp3Gq(=iG2y^wjkNREwLV0Q9XK4^+u7n+q#iBBv9vYr2X6IQt`=^5;4D(E^gV=Xfb{B>eymGV4adZ{5b^ z_ES7;D(X5X=rU`_S{yf~Qi`~tSAx}!bYq?`AS*6;pH=n|CE{M%74GJka2gZIyMz_X z>Ui^;7WoLAXJ6uD*kwuBcsFE&chfK*{1=HURqgyqIS_Gu9dx(_H@)190lFx&m_Lfl zPoZKb}L$h7NcS-HqZ+f}C zP_CJcnWT6vykawY{XGe`9*oU&fx3Oe#|mybxSuVw|KhkAMH2X&X)7y$$9a4ly5QxX zYKl;vm`p|BbPk8;Db9R-TL1uMlViVS%`eW1CAb2d>oFS!NbnN_R8-!W{4w(v`5O~V z*yf@TzTVyO%M#P^Ym{LM6V4UKPRoQ%7`4IX*2-6z&92dpOJ@?Lzf*t!nnBBdF=0@H zi^O%@*r9j!i8BP*oDHrM7b$T*B%CFYFu#;(DWQS0HKsTTUUig;747)QyduM$%7?o8 zu~NTR@DLpBoJ8G0^nGREwLYj)hdgibn7#))n?dI#pJ==2U*+j#MfZFc$pBqC@yWeN zGSFDiLidFju5_Fz>nn6Ab+qh=;o6R7WsGJftSWwIQxRtG%u!7C{g0T765hpvRg~OC z6cmn*OhX+YDlHdAe$cpAN6wsbq#S@dSRSQCk4v<>W6bY7Y#oA!FhFP=**K)ez-wBo z>KjC^%_834o1dIs+5f)NrP#6y^L=u7BZMyOLUjaw;{E%qfG9gPU_s9Oh37?NlC?%2 z=8xI$yFPcYAODSKy-o7@bL zUlN_asv5G&-(J2f$^dceEzE34^|WM<<{^qPRdVh=bRT&H9k4WG_ZXn(4kR|(P8S^2 z56JIt1D@oxQm%X6W{DezJvD9dRh0F}7_#+5wVJV>46z?@s&EVmTKf)mjJbjW%Z$9zwkHs@+BVZ1eIg3DNYlmup=j`~H4u!h@@4G@bOWq+WAG+~3Rr zG-}w-G{G>j&?lb(;%OFYU2RxYb7+53AVbL>xz=a;Nuw-)9Xc(=L2s5lo7ONUdFSZb z({`mP`j#SRJt5Hs#$3vMBYo<+Hfp@AdZA^cD9htkDo*;&rTgSb>{i#@gX>ciPa95AMKylh2rf?8JJ1)$w1GoomXyo6$(ga7pc z^O$~Wjuown`b6ftoAHXN4$%TGEPbp>7;E%;%g2H%#+sPR53h59!+DH${bEkCB1ajZ zXAessjDg!vo;q6irn-G8TEU@c{4v_D12Y<b5P6D|%qlRDGTP*h+LE|?LkP6%1Dw6ov}4J|bZNF1B5 z#EPZu*Mr-WkzXhb5Z0P&+Vf6LeY3#D&sNBEK~|3Bbe8CzibVVqV$1w@SBVU~Q1^iW z*VCpa)&Ul;g?U+KnxbC80I~Flt>kUuVld$91?uZ3=F2q(mUkPU^^(fK8|JWmrAOEo zbobh@Uw~;5mrBjFj9bXvbJn2+*fq8l zI!6K&eVg3V-%3V2>C^*^GV|)C4!Y7!2FMW;uBB&+smp*ZN-d|_RV!WGUYEZ$z?O4= z>w6Tuqh7W)8?pYn3Ph7q{;EO;k*6)OVczv1Z{gI=xaH9THUEJv3a96xPv8Z~l+qGx zUpxPaqVR(Ej#-md)2wJVV*kj&RV)KkoAov3>9?@Mvne`OuRT-gv3Ak8Rv6bkPq&wO#V!lCx)q{L*d)eZ zp;hM5H7GR6el&CAiHsy^skklWzKWCjQDAyWtqhR-s@NDI}6tb*|y1)QgPBB2Lwqz`RkbDd%lT4|KUt7eip3k~!T^ST0TirYcB+DtzZ$lr& zt??;pXrLRdhXGo>MW|_26{&96%T*d$%>L0w#BZJz{e^_NQbxc-WqEa4TlbrnqStF!IBo zMf(Uq!Sj9YyXf%s4nJ+2bG?N~lczza(ab9CRM@SED4D>r81^$SU+oy0SQ8!Sjs^Q_ z=A;&9h}RwhFo{V7ctw4|=HpWLIY~LeneXWX(z*w)0vS+9Cn$OCHm|p)PQqz=~FBIt1g!a`)nu&~RPO(p! z`J}kvw(o#kr;{!j(Nn(Lo8N}Mv!mN4{FH04p$9?O+B!`%VPna}I0G=gnl1yql_+WQ z9{c+twu0xXS7Ofg9NXlktwY?m_9^Etoi~o?KHt{gs{$@7wc3673fRz#$w1HY_N?Z^ zWrZYG*6pMm6_r2ZUi0`MZ^*QubpGwG`)v=ipM8GG8V(z=sLLKJX%zo3w)m{wQ+f>P zSI!}W*)g~nb0|}?pUYCM$Y8#Tth))WL`>gJN$InV;5M;6Pa5cJ`g|J;Z~TM{qD!M> z<16}#qFNP8L^>eH4H^Q=r=q74Ow2QY9S{PSPb*pZC8!0W*EZto?Mo7T-@6F8EB`5vc97Qu95+I z!YhDPO?9tsu(Ne&xK(;(oR&)!>c_R0CDx5DMnZyRq`q9XS2u*O@St^5H)OoA&`4*8KX1pQ`&Iba z^4(#zl*?~zLMRCCU$DfkDH&Y`h0YLXpmlp9>W$HI=EeKX18sk&fPG2c|+y! zelMd1PW4WiK}{&f@ouy9Vx5C>b+fuR7wx7wu}IdewRl0ZL9&zF$fwTTb_H^~ACB|i zcN-)y{JmwUtHqLt8 zb!WNi+!U_Qx#Hz|!`Qd;M{|yG4NC-??06-dDtT_KoUB=je$=Xg zudR;OLb)8%7^s@BV;LmRB!c`3w_=XDs1cGVYSDEeABRkaZG%cYgyJaXL=W8({KZuM(_%IMJR$`6!P_i663&&(KC67bkzJrkGrkaZUNJbJ9< z$!M6B`_r!Aw4FCMpP2T^1}UD&yplZ=_5Im$Arni&8Rd#)+Zf@(%c-KB)6RJ!3gRqJQC>}cV^t7wOe(QppU7wV1Q`Ki#QRXTvPfx4cuH7?Q4{%n@5iAO39jQf`RMKmg~*0Y?gAz#G~L%Rxz}4NvXt>()^36c<{n@y4vp zSro~9{we2@DeOr%c8#-l>q}CNvb0uo(S}Xm-F`on>81D(%$fLvtcHe5r>;&0n8$|9 z1I368P*onW_GL+P%6@t;{$j=H$z3hS)ZZ!Tjz9??6HEQReOr-k3otZ#1tjc=u<{t8 zFHs2Exe<%>F{szQ=Ti}C>}kq*Ot*lvfCKbaJmk~ccWoJ2`R5IS&y9(-bSTExBcve< zqRvy^D^;&HP3VWh;%Pezu6qWX@yoYb=)S*)6CwuJ1@E16=No!WKy%^PY8@gZ!)Xa(%KYU_37gGbk~1}L!QO!s@X%md*-kxMYjN;CyQ2w#T$ z?e!!;a=&~uN(ams^v%F8UT0cVP_oqr)yY}ei1lm+$kS#AXLs=miTBHxy%;%Idqqic zlu9wsv%h_!ePDJvtek0o@00}jnQ@a6-;M$M;jf}W#&>gZGxEY-YiP~( zZ&&J9lG$}zUC)W2eXMk_qmZ$wCvA=K`^^q0_}+pkBH#(`Nvb6Fu*BJj$Y*=Qu@|p% zI(Da4Y8gL!AJ^2Ih?Ur;TR>o4(~g9$RsV1D3#w*=ms^XJpWVvRy9kMwUynW&oH;yG z$jl9R*|J^NgJ%q(ofsRAlQzTLi{IgumQ_dUI>&8j#-$}?Z?vMs<8DVn?*W5<2h7=D z_4%Lo{g0AJ53WGjlWq{>ALCB=t`83e=bCv*yV`$o@-E7v;}vL$D04-P@fSJyVXn`^ zO9oH7+eohzPJ*aVTb|9yJI1JN#f(r5;fW}K{!@MSi?IBb@W$`Nd~0GYx(x5U8R0b0 zu!#S%z-L!};GBX6uZy@`oXTwKiWR8>5-6^0*wg+wd#${3i6g}q#gRPo?qZCZNDQTZ zpzwQ)H@K?}?*46XFfP>C7*K=y06pfRzQ8$dqbM~B|J3olH`Uf8@VcPYOZ2)R^|3O( z>zaSe>ubp(;73m7e9E#)zD*P>LvnN=S3MgYETD;-|(dVVegVC&T4#F-R@V*wzl z%$dEOWe#T*`F7;nbeGL?S@0)dy;g!+xA7m&Us=KZw!7Wfk>!ig_m|GNf{uXfOJO@( zMzLEurK0P&Hs87zF0nUxHzIQ106PEExX?0`LQoSx%kV@@)HUXQ2yE@9gwy z8XCV3LjckXTKn+FafRNyFb(3wDVkS2jUzpbpO}=yL6R;4)Ju!Pj-Om)XtS~i%Cwbw z5N8`mdzU`$fBpI`Aah0g5ZOD{Hi=jCwzvk|BFacWavTD^l zNpgG^0j&$a%^a|meGFrOJWC!QgT16$L9N0lRnp z>9U>kGB7{OQ(fog{14F{j(KT4>lMDwyH`mb0fB=Pu|f6E`v;)&!;b|I2?~;3Z!Mqb zFH@zRvNx9HcL zIBTpasv{s@d;l;|6|UPVlWWi%Zuda4rpf>C8F9Bqa_kAW!;pL^OLCycc)^BZna#~tFRy(E zBpPAU1YU*Aw~=<-DHG)Gx+}F8Yg+SW`PuC^^k+^=r_pv(jpYfp`qPx}&y$$J5#26t zfn>AWK6=LNjyB=DoiwMWWxyOUbFMb5)fgv36W(bxTU%1gm2EZ9Oga(1uQ*{=z@okh z;gF}^FXTfOv>SoWncTO3D0zzae(ROCqSYG?cB}IEljO~04$-lQtdE~>6^pyX6J=N! zpz;3nTsBE_o%>f^uCDZnC4O#J{mnkrPF=rJcrw{q3$1GUIjC>Y{ZMezRGYd!I(>3j zkc#S-(GV^m=3#mxEELz7QXHv~#Hpd;r9RPXpG(L|X=di;8X{%5h*!0`U-{0Y$_Y+{ zSz25A?p4&+k4QOU+FmYka#nW3GrYrmmRH)mdGL1l*E3XY<3#M-g>Og>@(V-a(EG?f z$%%qOc8@;yo6}YAAf{W;QQ4pb((rTN$u2B?;APgkBjD_f7h9r*V+x3B@H`}kp0$qL z->*CTGJlV?K0d?8oyLq%J`wfWyd=q1+A+KYV%i1=PagTQ?eSh$K25qCJj!u;XwFTG z{qUw^(Cybjjb#i_xILmCIKG8B7j>201e^KVOhs{@S+bp4R4Poe$Gst+sa3I0U|0C6n|C&?Hu;x3$>s(0&xE zavv0cW&VgS^j&3T>N_YmCrksDZ{H*k`C+~&C84}f>H3;jKjFCdgmiiv{xlFD2(Eph zlOOgdKKY@qgS(B6>`z9!A-$ujW5Rc6el+t>4%pj&2;TgDP8hTwc1M8=7Pxk(7UXYt zoN4v$4cIGNl01cX9e&MTQKx6~*`pCiY8VxIST+B=jrzEH`?kNKp70a;Beg8>XB4E9 z&aWToeO`GkUnTAt$2Uqg)fXLz+(}i786a;}pACx{zAC-y1WR@u1yA(0_<7H&UjS5L zg5$QTWtEq`H-S)-m{nnMWihyyz==ogNyj`SE2wogur5BZa;w`QJ*TN;Hm+cLC!JxH zv7(~NW*U%=eRl28+NiuJB{78>Koli3*+=m5J^l=kujlN@!RN>1rwY1R=x?*ui_86N zA)^E=^WNb3&taRFYt#0%UwsR$iDv`U{e0X#M1MI8Y~nT9nO@dHf8Ip`qdyZ>=^DB( zU~>Zu(DVcIH!Z$L`0V{+Yk+cAkDl#eJ0kh>6}}0LX61W)7*Hx}S4+!D88ZLGCK$v0 z4W)WA_++4PM)SN=Qyox*g0w-Q06$JIivju!p$C|fv&!}gZ1C6lk+=P!Zs(uo0GJ0s z|1H?{%%vI&A%}XOrg)N}94-$!5#Q^|jM8vYhu#r~L}4j?r2}Rg%>(iqmjK$!$^qDY z%x_8dPf!=eW)>z%2V>V8Bs<@$w}dJs#Kdtu&VZ2h%Q=BDfMCS4;|x#`B2mvcO#^yD z>zQSypc@0kjsW6XG!Pqor#Xhy2piR;!Wf_gfQ+yB8ce@n=?53=M)!Ehtt&3kd2m$> z5Cz0Z-3!!sj=#oKhLbTsMg$YUmPJCSPGtwMN*P<9)*a_nt?W1gUmuJ}%^BL&Ek+O? zHN3GmPM5h%8xCguN%T>lx32f@q9Wc~vNAvx_pC%|-7u;V8je)-CU9{IRXMv!%6q@W zWZFM}^f4eE=NK;ssNdPcY#H<>%(9^n0JtucC!;-($Nu)(@}U&@#Y4ylpwEf}D*=$h z>8}iorooI*J_JYwT*PC+lNWHqHldyyTWy%>_c$fot;mWp&hshNz~kw{&{jO?4AMUi zBH`e66((m{x?aXM!b=Tr0cNmVitgu$EoWYpfE-Zn1hikB(otaYLAVeM5X#>apQ4w( zV}N?o=h8NL*W-cZj>F6~LTFJX4)N8et5IyBz*5f~2JHW}ox@Mwqy)O`NMTzb0$%)s zgLn$=i6S=xBoz0A0lK*dL*vWe7<90bRku8J7mzi_*kjvN8* zyf-cZV;8@<+ue>{1e^%;P6S1>%z}{bt<0f*-xC{tgyf+tp>baS$S*^?N=pMD<73cq zoxlW2@}ZY1Zqvd2gjyi?mN%e$RGjoaq9D}1<6P61eVqG%z{>EOx>n;(@s+Rj%^91w zJOYB+ue1mqd?cL9-5^?a9t-W1+wofiNPm9*9}Og#FhIX#vHdrZq;0SmcpQ*l?#tdQ z@P}2?d*jETQ;qAoy>{0QmhXS~z~qzE$wAQCojW%v%-6@jGr5|lI@G(*{Y66)<7*Dx z%_IAbiCw?p-+O4~0zH4@4k~<0XIl3IdDB(+lH{5gc;nFu@AsRVtgt}Wn<6$nQ`OMg zsbFB+O~>5aMm=z_Ol)3Zq(i^iJC6^J0%D8RBkZ6p=@Hh?W>oRE!o40(HP~8*9hSf3$k|a0 z_aP?g+y|UIEkTCqCJ~1?RFGe<<+?FY1xt`I?3xIXe&lamv#JO85{&}?ngT;gz20zLzdO1nC%@}$rqaXLYEVW*0V=TH!ROSheBbu%zzpfL&0)8UOXp1irL6Fi zQa0FD%Amwj-%r-%;DhhMtM1RBcM7yMM*cC2CXk2^>iPnV=~K${C4g) zzsocQB%7XjWZjd8s6!IV6df5LSobh_Zyu&{l~e~fnP!-gvt2a(+q7Gv|Kl@lYZ@=Q zkE8wJC|3q(Pe&bOh4uglseVT`zzT`jTLQF~6pbf*Dt{UYVSpww z=Z2Ty3{YYypq#PD9jl6c@!Y@&oc6LxW*TF|(? z0pYw+bn!|-soMQVd)cQ}j+X^xQ?1^qjM?wCl~*Eg4OU;1-7rfl9JvVdwYcEaaMoJz zULJ}SZcC0<=~R@-7_f=TA#+6-(=~BIAbiq6=XHWpo*u#_Q}kL_ z%Kn)Mgsf9o;HO&TrZFM=t!7!>x*hRwNkkrAwDaci%7&K(`{W|bt%Q2fXvY}?>IE_z z#zPT-KLfMMWjUQ2_#{I2u8=PT*NzYGn9t0c<2yNj^NQrViy^f>Ocxi-T}kW%+4A?= z+$KYA0T7uvV_Tg=r^@@jn(eVpoOYCLS|m6<_F~G%=nsdZcP?fpDZENc5{|3HA0p?D z*^{*t-BNAE`ZHBG&yRl!bY~+0DaakZFU~FXRF-BQa+9O{bZW^zB~`(7J*BQ?Q$%2C z3n2HWYjD`Pwl7r9&arALuwDqg$3Mq&n4Hz{VOP6P+Kv1qC z#dN6uRqMdzoOgl&XQ3<2_+naAsg4Y8N8nlOZ$qz#^w?5gPQ0UA5wotEOMy?-sR7R4sw!?!1cP21EGBqDA`2}x8ww0-4aF(SqcOZ|<4zJBz@Tvz`$!v_ZbIM?Y9h#+J zgDXbSDb?6ukpqFdIKHjh*`ZqSR#%IMHxGuSSXe4MZoT=+w5G)BiSxx+FLBwzMF^rk znuUt5TcS@X`#jm~)O>%FbT(->kd{^hUXOg{%}Lg(a(sFclXoiHM`=+Q_H_InHGQH* za9V0Ko_97%%co%ImXK(PpdtOTp#e@H@V()T95i>wpEU%h3GEGFNIt3qStiaHP0BJ} z^;b7a2au$F1F#AJd^;c7_Rb&&QGlxxT8P7x@zSxHD}Ad{!3@x{@!HZxA_LT>Vd-UA zBlW~n@PV!$@VN06L;`gO@O&}gTWtX8e{E8hXiFdB9=hh#3kGNy$ez7i21h=jfI0mj zwt!zS9tSvrK-O=4IZ(hreZ2y7>>2|!-?i3-^54=0?4~Yk?K>y^n}wY27~d^r<_18d zSA(|Q4`#bBu;mNWkoEnAz_0N~q6rmoyMZEzmWu#g>{|{vn_G)TXQGpE9J#>E7yXn5 z#5`HvH$}f>DZl__Ndt1HfrKGiVgJRsKyXsed92~j1Cwxn+YR|$qU`_f2s%Y>zn7?O}#kipDt*~+<`*MUd#3NHMJJk6SA$QrEjjJ_vKgd}LXHnIQO}HX7+kq=slVy+?G4q& z_WSJqb6x(qK6}V3bw^~Z>uTLRiF83qGJ#l7 zrhF=q>EvB!dNyY2Eo|sWnL4-*n}k6hIVWK@XpIM;=AX!WR<^i2y^G*G9T|6=mV5gZ z;xgLAVT~DZ9!Ih3A$sZ;lXk@ZwLF_o1*0rd$Wm1+ww_^6)xYjNO6^dV)1&F$)IKrz zO7_kp2TY!I5O38Gzr18XIZ3*+GwH_4VI@AAt_qpZAd@+k>Ah7=SsThs)4)%T=3Jak z$_c&!JyB+Qjuvy@qf4{}3M_)@tAC~M0?jAG$JvO;fCc-Q_)krjd2JPHb&($x>EIeY zO+9^MEA12)Z$GndN2CP^uYj>stArlfq`fZ@f^F1;+ihTbsQk%i z^9!EbZI(Y*(TE|<6RhE~#`cfcxy+6$xF0L!DQkRy zUs-`CjmnE(Ir>U%*DQy~&(+w!Jy1{11$Z@Tc$pV%x_nh15l49UD_gNHj@r|@VvE{7 z9U$k@uY8h~*;UA6GSGM$lz~152r`s9<52vL7_9`WwvJ6;{ zySS|M31lCJuIT~rU}Bhc+nGwt{wgk_pa!t9-aE1a+x?K`uWbOHg{Vsn@?@hull&=5 za@h4tz~B-02f*P;;98e$6#%il%5Sr<&kQbTOmBl=w>gqjdSeh1z6{ zqn8&{+gahQ@T1u#9gVcEIfJLqJamad!YZZwHt;NEr*&|yiA}u` z1A+j$Kr7Kc6V4wG&O?|*>!sJvuPZkv0x3abO3>;)TjagM5c7(v*px}t>8g(~({7wh z`n~3f#XrLUa6J`4T%w8y?&xxv&?X2XV*y|NgI|3efsbmJB@EIy$}~V=8;4=>U}8?g zk#7`BODSqeAD7TnYe*&iL>jyK*GQ*3c3ySW)%zXX&l$gXY5!pDh!cIixu>sx;Og>w zk;3~dQ~Vi}b-hVnWpP1%{VTDdmydi2u|&!W>S=|}0Ur!VK2kk2xoEK?qoA4SkQmAq4y(|s@K zj$Uqbd_I+qQXx$Sj|83`uytB0cHD{vvT%f9I#ok>dD2pVtFFMD{YTrJ8=Mh*yo;Cc zk1^6i*=uN>jqBwR8-^tjv$LuZ5ylNLn!Rm`WuO3iT$zqUxcTZdurd#~vvNWIN+YxF zS-CuHD=EEi)H!il?kJ%H$%>bTe^j@BM=o&1u3fw$W&SwVlT9glxSW7%axr!lm%ug^ zX@41nBqls%VmrE#h*u@skkS#voEoe27hUJVE7Nkl*N@S^3MTc?Z{fp{asSPyvh zff{c#zn%4sP+XTT?B!T#-|IWN=%5~>fOUCKvBaEnKlv5gzJ*xEptEIr-XY_nZr43# zVlO=2v+I63qOs1io#UkWiE1{M390n7vz&ADW5HCs{aTL(dq$DskI-8qNGBukocYpP z9Sf1rXOP7d1M>I`QhBa#GI@+cWhp;$Ge3=7laA5 z!-AzQsGDO2g*lP(YH?Su-5?2BPll1Em0py2!4~+(hso+F7|kz5yU?i9V6Wi{kJ4^R z+(oM0MA~9y`_}#MI9Lt<5VER%Tha6XkS5mN>N;ppwUM|4$Hz5~V0g3xwzyh@tt@Ls zaeboKb1(Z_c1W&)5A)x`h|u5P@Y!T1mPm-|NRvbKVv=%|KYgOTLy5~XyV<3h7CKlV z=xR7jm(%>>47yJ#`T&RQ98)&LgpR|4 z>;S9U3s=8_@Sx-M&#@bN%qseY>*es*qsn&C#_h3;8yuBnj_jt-c{E z;?zg%o+Z9cOiohOx3ij7I?JRd8PbM9lVVyvk14TU^L=Dkb?W zK3jSQZLl8F(aM!P&1)cgwu4RKgW zZuMAq^sFV|gsPhq%L-(!eVeEwPrWLp*#`0$cmM67mk66gsvarGFS&oF&oYl_VwTmD zwEuFynsaBW(wu){7&c-`Ml4350prIEoLzMeMukIub?vbRR&GA>YV-TRDuir~ zR|Z0gKs=Ht0HGfb{?&CU1*;UJaC9otIfvkVf?RIdp3>p^;LhU76p#kjQ;|WRkos$$ z*ztRbsc`k%S`W5y!+$;X1I4XjFY%Qn_;8w2*0ZOa**uM6%20BaqURfX=l;>**0Fp3*@-ZS{6@&)&=MtdU&4l~(;-PwEt9sH;JllB^8qn2g@6P8mm1P8BH^Ws?%r9e zYpsicnIo!vua1{DdIp?6UefYkr#EufSL5gJyPN)=rLg?XtEOt%Ewx57Mr$F8WO;K( z8pMq(dO-Vlm|FLB#lyii*MCB(>6C-n=!DsBN?e%3gQW!FIx(XM#Vmrmgx)Eo`SQp( zD6!FbrGm?+Z(yi90mtNJ(%)X{IM=Y-BbqXo?YbK#g!YuO=79bK|buDB@;t~YXPoTbhH;M|5O>u z!z2^l)Qxy$VOk#P$l`y}{_&%n7$2WHJ&R%LDI^5b3T$XH2PXr>Wr>E1@_luYIZ#vr#cO{3&4J`*RvjaNun9)qBw@wcgW$Pk{{3OyTel$-?x%fR_K=k$V5# zd;VPq%RohIJlEo%qhMa?vr+{^y|P?`!55PTV%WQJp_qH|FCoNGyWyUa+r8~B8xr#= zejg5VFS4nSqq@UkHcSb}vryDjo+CRf2EGWq69=jG*^| zcK+gs{MjM-{c#2k_mENU<)JeV)-tFTpl+q`TYY2yh+97N3M@vcSFQsJA8XLL}-#^J8 zU)P&w1BX~93wSr5pY>Q1ZQd3j+l#-rbASBhP0;T&7ypi9VRtA<2uc4G(|Zi$QV4n< z1tMH8=1qZ|gM(kNH9=u8)nn`PO8ESl;k*~s_VfL+#Dd`SQV)4)=PxVP@k5+v2Wj~H zWt!W~RI_1gNzcfQYXD2u>42-LrlhXICn#x=HZe-TW>EV959_)i zp{=A;Dx2pFN+rYOo`6z!N@~-mzW#S^R>Q5F`kw4$pE210d(?w5lQo4(eMqtWPN(Am zE)dHJO82U`m%3YaxM_^Pb`yu%`jmcoQ5pX2hmzQ)&?|KjR-Ik8KNW5K#8&+C4gY@! z2L6A)_pby);zz={-qsuGg=$pnh`^(0N!};WQ5R6+?{qFNoGFLG{9aHcM8dUkdA z#ANuzbb^6Wd{6SLp;O(koYhp_^&`d3uY*Ry}5m;!~GahirRHKg0Jixg9 z^NZua>50>_C77@uKLXJ|vPSwCV z^|;rPg4=mmY!e<`;8~85Z$|@I-s*mQ)vcT|Vr(RpV$KU&X}08?x^B>Xy5mtCy-2cEBOBpeG3o+hhrCm8(mp_uW~vsKj$Bt-Uj_o>@LILDdU>?78 zVLnYE40!-`nH0_d1ybRslD!|*Dr>njZQcr2WLlc_is~oVkPc4OnU3y{^j9mmRq!cJ z7E>@ZwF*=iX!;7m-HL0BandVnqITsrG86~?~0?D^3BH9;$PlAKj)bEm^+NLyrMcv)Qr2t4=1 zhXK^3l}=N8oGX2pl{xhUUY$HgQlXl8_o)R# zPx(Hiei~mm1$)T=>;_b-MtL6NZD)#)D)-f=F;)a684 z$D`0L>E6{$oqodtZqqA#m;>>pDXrMgmLk?}j(1K+bR)1TgW4|L-WTwG7mJ^ZZ|9)` zpV&8JbTsr&ty&Pqy!|3cL4uBYH7WdVrYV%9w&^U6QdGZAKP%giIRm6w*GV-=9Ttbb zGOP8>P-USj*|B?^KcUmE0n}>MFW_p5(Q-o^4eJ`X<~ozH7H)V-^?LV1LDKWxY{y}^ zGLNJ)cTZM_>S!p<>=p*TB;v!^e4ZaToIRWlQ~s7~+nbEEF}Pe{Q9iN>>x zF8iawLBBV%glLY3&`*w-yIWPs9&tXhFz}!!$jX4)GCGI6T<@#d&n#Ys+WWq zx!gU8Hu!Qg#XmAk*D4Z8nbaMk2%@FUS|=tqn$n$}wo+PN8%$xZoN!N33o;Zj;k%K# zdi<^B+sfZ}AuS@S?zL;BFHC1XvrJO@b`y(R^VG@2Srwl8qWI_p-|dBO1n)?>K4?F^ zg_BAOrf>3~;~<;k5ZW@dhCYm3St0>sHrkC6EVa2^M-j>ZeNnuG18tFGJdklsNJHo_t6r#G zew!lqtDM;^b&Lq-3T)IeOO#h@FHVmEdb@JL8{yX^^7WGJB1I~GwONAiPHd&>q57$u zfT+GaVjRwa={)@!Y^CclO`hxcP#&N#oT0ru^ZqX#JdWyhYQe1zUx||Ms7^1qf5D-~ex_ZQykp;_(d*FvLOGWf>N7No~ z@3op7x+oRe1{^tNeLXTivVQ$oB|plzv}KB}Aqdnw?K-kSrI}&Ihe!1qM$mBe21%d` z6))-GGw1Y8n#Em_j`q)T2(SSU%vnK!_N6CM_p^)Xa(DwexWRU4 zcS#|K$g`*4*%UbU!QSC?IipACY(yIY`D@xNbz>j@l-jr_PT7psPa|9q5MQnlK6}n7 zNeIV!{RZ}qr{Az4s$W~-G~i``5`kVnC#kLT`nFy7Q`d{@>iK1d*H%yM;WTQWC@Te| z8e$RY5R=_=?-tn1*(IMsi8}`O!t{1htT(`=yA zoX`FQW3xVwRtn8Vs&rlVP^!heG?t4Rs(hXHf7<)*xTdyrZLHWZA|QeY3W^jFr3fMr z6#)SOsi8+fK?p^f^h8ksX;Bam5F)*Xi1Zp25Ty4YAV_agLJc8_-->6>jLtph-g{=w z%y(wy{*hk_dvDg-d#$%V@ADe1o)@E>AUK|`O0&05Y9bi5R+sb_v7x*QSYN-5=vR-%9VPDDode&-uu1rie8H#tjgev+ zLT8JjN32Zb7c(8xr=3mv5XkIifY0ubcM-tiwx>Jn|G1sO*Vq^OQF0ppXOXx_{2232 z{o?yISU>q{tiKsTv0Lw)?Q8-0#3#337o@{~7QLQd8K!0N6JHr4#(HW+E^^+lOy09Xp*BL5$p8Pr9NKwX<^85z3H=54d z;p_a8{Wz@dJbAx(?n@GXSseS@9SOI(ZuWojekKda$f<0u?`=LE`AT^|dM-z}v3f!` zVtmD$tgFx~#ojofu$#SGNgdYu8K-DFoR>w?HSk7Uhj%vH<+ zPARdxWWpd??qsEMyY^?wR(5=oC}y48iM9>DMcC1{w&NH|wXtHM5^J+tGUKVTd+2-z z*0YIK;e55?Mz=ZkEomptv0fB3XNVpO%?{S<%-Z`hm{WCC#8@%6i;s7jZLnAI;0+St zg?msHK}I$f6cwJ>Ex?nojB{X&(XOt9C6}R|E^-tx4Lgm^vg7y8q-CA~F4ZzRZkNz% zRLX#j)0ezQn8U`2xK4sYGKs&N0hfNo{upg|A&DF_Qav&`lpjlY-M{ksOE~-U87297 zH&DItU{RL5<9M;plgo=ziGuuvcHV+n{S3&au`SP^;k(sS=Hh$uyW^VW zZ-PA%;;gsKig;IkZmKM4=*)AW9-*8FcObKjLHfez#oOQ8l>hE}{|wH5ZdanzkVeqR zZL`lpJu?j6MUDdNEjNzPj?Q;iY7&bcNaD8JD0LVW2qa^KT*uETXJV$hD_63qUWy$B z)U(D+X6?dRhTC^t5l7$g;&C)(&E4;Z*0siG=HjR;xbJ zh?Soy2ARCpJC6jmK4JJEyGg~=;hw~OH+Mo_j`_Widuu3KfyHASmzE1^CHrczp z(w<)Ux2~4854}%C21Ld9ZZDBdwyOhH3(nMs6;^G2hNW7)ISG36&2@p#0+|weQNxrT z@GtIv;|TeAO7!aKfjm2zDSv||Ia>4zThZT|93=F#XvQfdjo_4USO7^mKN8kjUg%UNh2JV{KM`CS)H>lxy z;xj>Y<*6?d8bNXZ?^S+@R*D`ojqujDqOCw@5;h=vQFX7;N#Dly#_NW9IMvvPNhbCL zxsc3xVAzYBi>}lKjpuo=$B_ZZ9pCJ|Qeb{y7TLgC4s_#m&ZWQl&ecHeM*8Pf4K9i$ z&5*_0ZYKtk!dJgpE;ueC55*s-^qD-A%&H8rg&B;=6R5&6sU@7-QQAo=ByhxG%?3V9 z-7_taSownTxo-hZ3GRmKgB7Um+2kSR3kn`AbICIFM(3`)e; z9|ZxYK%aXcT)L^sJ^XS>x3|vgqp#T>OVxXd!W#*C$<}8}^@;<#PeX|3Hkwud8lp(&?sX?_mHsMgXe{hH|^*;NBYhJW_$h zLA@#=i|tEivrT>ALzgHRP5HyMAufqCPFNiS`y?7tEpej`Yd2Kz$h{@p&(x-xBgjHuOBr#&d(O*eO zIf1o)B&4NEL2G$x;=71ADFVckz;g{ z=&H;mYt?0(i+hF?@IKlt%eiP~w7pZpWK<()sao!^mya@!>aw6Y>$0bKSPJ`L{o3bk zK)RTJzwLjI&uqQkV!($gn+F|=VuokF*9~uDP^Fq<>q-4X_;rr<6;!y~W>7a22)+Kz zI4AlbXbS9uD+8L;9fW*X&U?teji>OJZiZA{nm_&KL5MThQbYsaHGWF z!D;=RZB-(Trd7dGpB3H6XR&r!M(f?!x}hg4I`by(*_DOthFLk2D~pXAJEP@QHQK+d zt-DEpr8y)=hc#|KAD}(F^Js*E(zM?FIpJE#SjG-l!C5%z(3pHuF3u{4Z&=;Wq6mMn zo;KA|q(w#ae8`n=F3R&#Tawoaa#{&^=VLBB;6J(s{6SyfM$AAOd@H=(6`D6(%ts%z zG@V_MB|NPb=Y?ns_w!wQnz=WS0k=gyz4PeaHIy6W+!`0lMOCa(i%ZxBAanKyx?)Lr z@;|W0WFS?MhH&TixgTP}kx$R3J;=HFa9o&F<49RxMpiPxdP4 zI^zI}phd}gog+1bp*)y}&ZN`yKEi4&QS&;qQl3`}6OU0Nms->9;zYVCy zCW{pZ>Q+KFA>3V*ri9?hLQV2lSXRR1XtF*~ePr}o)SS*&hC`G@u-DwvdVt~wc@}+S zR@O&r>(aUF*pG}SQM=MF>3HYDEL{;gNsKaKDyOnr6N`nvG90cm2->Zu_oe*>rVbTy zt0=g@_v8NRq>aQgfy+bT=9W3_X4&h#{l-z+Q9|}+L6k3^A#87~?9dpV915(<}89vjL=An%`I`ziEN& zCb&s%dlXd&K41~h5ea?d#7>hbaZ(H^cNDb@ABPT^KKHJyJZi?=Q6?LF*TeTsrBU8m znFyQ$iJsJjg%<^`_?_D@8{jXCa3XFsG}Mv0p!`c#_g^xjA~(YM%2K|o)= zT7P69fdcBHBKf-aaQy3=WrCyz$ zF5IwxVt%NY6ehkhCx>S1Qrkb0TzGE!nx+Bhm1EjcjrNNfyq^M)59Y4}N5BgIYtgEH0gu=RnndS_AYhO?NCY zbK&dA+iTJeG$oe`_-M-oupx)hJwScF+*}Sf0w#6M8r3bF?gGvJ=v8?gB~47C14dAv zfx>;N0R%uQR7Otc4QBCV9|{EI$jmvuC0`kS(4(ZQ4jVO^uystq7|$Flw2J2g*u*Ba ziEM`(+xm<@NHi5ncjob25K?UPHy{!~YYF_@~5vzCuxD1TCllIwvfsWrZP#1oI_ zB$aPp7h#IO!|IB1rm;1*Df*0Yo=)IawW*`1u?J&=)29g$eYAWakqXZF=1a(Fmz^@q$nLl$epYs$`myO^TFdfpW2NW7Ixn31t`igl8L^VI^pPg5SvH zMT0SHe>$04c_k;l62hX`nkD3r1hso(9x2YWYi3rMLZ$&TT>pfDzSUz9#c_X+y<&F{ z@MzW1^wO?eIP#!Q(q(+AN!>*Zrvgh6((YBkl`qr_A$rP#KF~vx_6_Tte)tO-(~}f_ zEywVnPZ0mdI@BSv&&guGGTewN;g1r#6TFg)H1?_~Z6b$4riI_MyzIXLh0iR=PxuO@ z$aI$GV?Lpl=z4uUZ}|w=i(AA^+pq<(Hx9oayLC?4EEDmlQ z-R9nnOTKD|+&hyeYg>Et?YP)PCic{fGVLnyeAb~Xr9NkelgiW--Yi1N>nwVUoLshO zw;}4Od3o1ohH@Qa&=k+kyh+fgsLrRh!CaajVIrqX)x)!)>WF0F)_L#Ux7W``?XUJ? zJoLz!?Ehdz%_RRyl)(A-9+VJIi9A_JQ`T(Cvg{t%Udw}aw=`x-2{g!O=Vv9uMHfD=+5M7Z3WO79n7rRbT-ofx*%7GvLLNc}*=R{A|Ej<5BX8h{$9>TlG}sK66> ze7`vtIryP8$|HOH44T#`{N#sk{=n#T#Uy-LSfd1|lWD=rM!-ZoM*T@m*2*daH8}%T z`+8i}|}H6&XBeORJNaaaTvCEGCOI+zQClbIOtSg9?>BG1G` z%ukFv5Rz8m(ck_-7JIC@g^w%JA`Fp4@&^}(P;=okCQUx6hL>0;m9&fsGS(r?7Q*Id z3IsbNMh>)_g8Ruf$-M)1V_{zVHg4c;-K3N*rTC1c3#49WMcKV@ahf-LOR z*|4ljjy`C$6V(k&At~CTnVM~rE!TmR7}D(ZrAvttQLdRH8HWgj)kBYKx#7he@3*-Y zOrZ9Ihjjwj5GJTq3o}2MhUA$u!Cl>H52}j;fyQ%A5td^k#Gd0goP5{gU|#MAz~XMl z3hU*0QsiSmf7%tFn-sGpi(zhaY4XOQ{male&jNR!vUTV8Bbl!p>IlWKMyvcz8zn?0 z1wYi%MiOQgY|T)t;O&5)2QmxHZ-~c+e$yOLER5vCPeE072qmj!rWMJLs*Q-Ex087qqv3qx)p&jmMsF#Os zRxPieK4b3oN*7Yb!r`Q)0_fI^n`0P_6UR^lF%X$3Mahtr0^8$Mblfg$>_X@)!W*ALj?2T;I;yl4WbUZJCgAZZJv(K4UuHgA3@ z>Tp^XrKNWfy|#;I2t5k6Mvg_>uJ81ik!$p@!#ev4ggiW0UjVYt2jsJs&N&XwbwS^O zLDr@x25u}Zv?l&!O>HS4-8#xH)pl0^yS^Wabz7N0F>RuCmYy5=Fa?w<+hhdsd~KYVw# zI#F9N$4hC96>gm{{X>V0_fTuo?ERyk-#TdW?kx7O7nWy`X65(cPz~R)z`E%>dVV`n z2^plUs$82_60HJiT|B}lR1Zi3c(??#)qUWNaFsr?vc0lwTZrE&b|cABs7SMzXNjqq z$_#tnJjhMi0eFi4lmBpkiraz+p(*ifnB%)kVx}sO0e#d{9*mEjZ(m9W+i0cqK<46}pOVlbrjj(M>8UhWkiwxW0# zq6i8=?q~hy$$k3H5(+Iy?m>9E6v`AN!|sL!OE~2y6M8nfrUiN+5%Pi*rpi{ztlGOo zHLAp|_`d8`w8K1vuugdVMEA)XUqE9qS>d5vBl^HdGw5l6J?N2w*`-U z7Sdg2ZSHw>JVPN{6P=DlvyvS27Yfr?IX}hrnTYbqW})?Nyk$e1W~61FGkWzcGWK`W zm%+Zv{siT83`J3V22sWMM#6S$+7vWu9PJG7#iLdrhIe%t=BS2c|wr^q);gulPGMRbuuxKY_XKoCA;&7U>|1T1SRO~;5;DyN4&&6 z1Z-X9x+|#Y4Kpp;kOJilg>G#$dLA{5=LhK{6o>}qh)}XL^2(L6$xP&zC*L1)hvns2;@lfr!jEsOaIADgh0LG(L0$ogKohp zXT(k~w`g{-mBE>xcj-BbhxIv>DXg0K?^*chBjhwd*jS-n0;-0OuuC9SowtUijv^#z zvCMkh@oZR`tXH*|N9ov7=+bFQsIWXn*3U5cY2{KbX!&vr`9MSViPjhNJ4EMd!7ZIK zhk0<*vg?G#<1z-U+wJp;-Kn}f&!~zziO-TaatwtNr#j;V1v~5{yAHpP9Vt9$dZ|Z> zDiJWUEZN@Uj#jI96FAGcBnZE@D$ zvq^i0T#bl3HZ{S+@?$|sjOZ;V#xdhaq(tZEH&OL*s6G>(Mp!hA56d(30s4k4E5$Kn zmEtaquRx*~`Pwn@#6*>C!GZ00!~5@#S@f$k6PSE{o&^8PmI@npTm}#7{<<_>`s_`tXfHqM$~G6V1mY5uvUQsO;2kIhcxy$d)z4s5 z6PlHD%U`63?cPMVvgKQwFXjw?2HO^(%wW()0#b5@6|vHd3O%Tt}^-itE7X z{sy7}(uOW^e(%>mD{++g%VE4N>@~=P?cRE+a(+d(qxQw?1ysHF(JOrb7oupB&S$yH zHu$7Dbh>D@#3ZdfL9f1|))47oJCW}mCHG~^;Rqc_tJX8nIW$UgG#e`ZJ z`$NybN@%_JD+6WtpML*dX|S87Rf&=|Ko-am6`ixEgW8S~ZQ`C_0!B$QfCNO-d8E9x zHojGM{8hpT5SsrG7Ms5b<)ShZxg02*z|RnDQ)FANolwqn{{cJz{oZ|m?*!Q<_l#-a zwXBvWQQ!HKEWRzGp9IuiX!QWITA^2*#7XCPqf>zseXWE|&JL2EV6TtQt1<`tEh zHk_`h3ET~J`?Y$W0-WRO9mDF$lwFmJ6wRd!ZgR3~9XsvqYR1u+p*qk^3*ma?BEPQ{qg!s6 zs(o_!oH;50;4NSZsU*&U6A;vSP4wrB1)78k*H#5C=9AYFlWk73DKX^iv3J}H+r?Sb zxw>f2k~Pa_Ym*QA_z4f^y}-b*iLQ>F zY6IPdB#$iS*u6Z8p(gd{yJo%*KCJR3G?@QD>Z$zO+D5>hggd6Ck)%J}=C!go`~@Sk zrNiMmvf#r&ujZ{gt)@zCbrlzJc3gY*MrdjS9Vg&jR;PclXN^YzF6rN##!;kaCb|H< zRJ-tqGvEAGOQfSd!P;{bX%*CFTA23PP&cx}YHvjtsPZ%E8h@N&7(Vf_o?$Ml7-?QX zmhNS%+$q{g3N}*@d5a(DxI0$>_Y_Zm>AM%QJ#bp~O&VXmJ;%YUEl)1KW$o>0qnt-& z)Ri5*ogjDX$#sjq?z}8*){!QykB5d9R&rUcB^?+W_2hZzklHCBZCQLiWov@NyhVj= zm#J92h|K##2J)j89C9jiHeKGwJ(OIJ3kdTsLv7#cWT-(j*y2XR%9el`#`d(GEn*n6 z&W;1Xgr``ex5V;c$X=7B#@K7f6BKddA@eb_oo?{xnrL+eMt#loTkG69z*-r=1aLoC zX!7sD!SK%^Q>Jmi^{qNbbo1B*_1JQMS(;Wb$wrLM_Hfi*DjG$WRk5)y*593SCA|XD zu~}{bY|brB-d@QSIf)Jy+IACT0W|jcb%L5b(=h@?hkY7=trfreql#uDj$;NWk3VH>(epSK3vAMX)ks zp}7zlWvL*cem->Z1-c>Su5R<4Ruk+gjmr!HQEz5R$*YuzG#EbE^ zh!yREzON9jBC{2S>}0+*;piVxin%cr!Wk$CP{4xN8HaZ>Pp;Osbzql0Qa@Sg0~ykgHOW@xiHDR=YjjQq&_& zVY=rSjc<5`_nsc@kk->?JaQ`q~jN+7p)ry$tN+?tu*e4 zPip()!_V1VLl|JrX=#7zQ@3_LUeT$oAZx2P@>KQp)6RW1LNn*y{VlP&KYo1f7uQc} zS@!MbFKoIu!|pJ=JXDySl{HwKYQ6YLy^V3pM(rycS83dw{jqqR=ws%MK23HcyopS~ zMARnUcD{!^1JHpSWM!4mTUkkYYl=2S7F{nLD`h~YHHWVqEs6y?zk#*`2;pfS2^ELk z{hC~-GprB$rXT)*gSWbLw&(X?!?)dQC|Y~IlC$Ma_{WRcmJ72_qijziCoQOmPT}#q z#>@Q?hYTG+%3%-k@!Mif)n~bG8k(AlpagvpY>DzMbDP1V+L2o1>#)*Vdtzgn2_~Zv ze}%t(5KXoenq^Z>9?27n+vV)`OlO^K=yk!p@k8zw@|pKIa}`-%yePQ!p4m5F&liBM zuF-8~*d`*+HHhp1<`?T0ji`)CsOW^LJw8hz*&x6D$xT_MI1pfa1#I)IlV2Anm>5U29HVWD4WF8VbaF7#DegGMI)mg`jtu1s(S6RgzYA5JBe- zS2&fQ>!x>bNU7@ z91na0c}y1c>Sz3#AP6?nwCE2(=L`KIK+b&c2e}OH0~fmSm7!3WiipOQyg`*IwDAN@ zpcYSKY5c(6bd5LANBg5j&x2!3qt?4!Ziz1jzO0zsjL%o8Ypi9q9 zov>2aqfp=$Rk}gHGDM-Ets8TU;KNsj)p=S!xVrb$+FLqGfP*Mblg-A@B+&FGx+bA4 z(2@EEfGim^^MPADLzUJ}tYgB60J%6ZiKhdhJgu64j%@q{()c^m*WVS?{^JWu|M#Vd zcToG02Pfmt6`oY%i|w;$BqncMj&@~?%}K`IemuO)p`ASGIwq6 zsx0ACmnN+Ji))Cy@nP+5;Vcy;Zb9}Vpq|5MAi_hZZ8|tpTZfRbKB4G^se#^ii&2iv zaKEhLbzQ8*YAq!Il}8;SQxR2$s%P}K8F1}W+JRGI3GNzptUqN?pg#e@vQ|#;mW>KGS*|zA{WKBbQwwet>OX{>xc}7dAQ~?~`05JU$-Fp21vF z!6Epox>C-`JVj`v)j8fh&lH%VaxFc%!|VLAFn!-LzKj1GGd3;6t+?%vy-r@$zWg}a zz;%1Ruz2<0qUbcwN)0{&anUSWXD-whfnd_YjiH_f4dK}p-1iS^2{YARJURrYFuA@Q zz3C+UZpKZKC`XGfl%x582zR6tH%6hi@&QL!hP=0YJbcvqlvd4LjBai(Zrqqcfa_&U zFT>8Iy5p~wGh7|-R_N8h#5q(+=m^}tslvLZzZKTfRn?U^t1ZIg>{ok(=s2DrDXL7i z2Sn%k%Gb26^_v>^2_ha3KUQ|TE*#tCZEa!8Vf(f;Dwj)ug7B2KDfpC8lNZGPeCUlBc_@y&Z3pgXM2Ls?KUl z$0SjAOI$lQrDzV?9ADF5lwi=(vv)mo{4~e%1#Wv*6{vJBs*dCu(6Zq_KPX zRY4|&*><>k+`vSr!sfB}%0`ilXEmW**(y0f`yC>_O>k;uIp>5v5t-o4ag?{4uNPX5 z;+!yuG&cy-JQlP1s)P@J_9!K49dk6A7;HQBSs&jB`Z3Z_jt+XAIg)CH#ic$?ZJ=#J ztDcMiE+JTVf3RMd{H_$0uGl!`yK5l7r?-zS<)uyg6#I;r+30i zEuSGY9x3X_=0~QlV$YHXwF=aNV0(gP+;k>wy4;(?Gfk~w+;+*2oY$T$VprG(rntp& zOZC(mdmH-Wb!mV#ju@?OBvKzUjvhv0o)MJ}v9s+_7{$c5w9$g6Z>PU3_8Z!A@uZXB zq}F{l5f2f`B>6-!ZJ5T8A{$dpSHz#cGk6s807gpMmC}%xj9MSFAz^IcfeK+$_T`fAG9Eo&w z89m?h6_h#g{8^l>lh>mbfvZloaz=W`rWLV}u;G5> zA+}2|ZtH4WFS||Z06S??^TA(XMa~bE)n!YTA!2IT67}CV--rpXDj;Mf2${ zn{JI4#`*w3!nkw%pvkHIcW_#YOHx@ymtCI0r2cYj(qBfU-028inx)ouf)<>=S!6rm zzc*^IICh(}_a~#=58(gd=rxyVHP;q)#%rJB;h z-^YDra;#ZWaxxIICCs%)EZ?*+bi0PTj7C{HJPZqQS2gCxi}woTIQ)6w$`pQ2<+X9p zk<_yC#80YZWL0!F8&5+i&nv6^qJom}QDyej55=uJ3=pv}7Vu;z~p7)N7el@;z9ZBt}sog|S>K-%;`@87iFJf?tyPomoUsGc-vT2V+o!uJdq@fNPl>K6jioD>> zfN{aYkjxi)HQkcO4;pX0luL0SX0qzmzqmydj=5B!irN~pd~2d>g=0H6dCI>W3`PA@ z|JcXpaXdT|6bC^+;97sG4*(a9Q**ezzoUOXJe( z*rp>zA3P3jRNk*$z_gdImW>=g?&1V`pn&{^AYDZM<~-j&Z+Cv@F8}6R{V#s*A1ORQ zcZa@zDa)QQxwO#2slwu$6DoF)`(@zz8pwva#5Q+Q_HR~c_>h+8jRk<&7Kb&>p((h6 z7Hn78gHrxGI-pk1ZY+Bhrrl?zaEzV#3@lVps9xDzh;ze#7+Ph=QhPRepk~!a5yotG z`Si?}!2#~t1zfoN5%(=1K)e=_`L?pFnYS!}g$skW_=0EyNKA=oN8RM1Njl%&Z|`a7 znf4?2(V4atWZVBzH@)zDa4j$%w**DhL;D3#e$X&r76HuP_l~r6_jfvn0HGL=agAfO zs4`v9s+of32C8trrRvh(5_A4HIp?28cKJ6l?`CC8t;VDT7kY-2!_K}AmeoErc0)h? zB_xU_NZDD^A^S<~ZRVQ3!nhZ&n%Y57pZk?z-|EZ?Zl}xbPft6uGTv&RzKCnHSrQH8 z*^!UpNREn3kf0(s*mvMhrnM13m!$48L36w0W}9cC*_t5&*!hF*ng$D@soh4OlHRSa zAKP-K9vEMoZ*xXFV9LV!ZNDYnc&HQ>D7xHmtf0tX&ux`Mo#7u^u*SghAJn6b;iwC! z_a&afkp^6{ea~Lzw5Y~Aho)@Xp=T{}AWaK6P?cHK2KdHj5bEVwh@Hmw7XS(=2#Fx7 zsJV(fw9bs`D?w9eQ3I~u|Fu)9@>HjT2!Bz89i>D!%kI)%$cV`-YxfoiyX)4gdD-5> zBSP)gKM|xo|6XMLr+xlM6QX@@VE!GSH$@C%AZvqH|A&_OM+J|U$-)y8nQc@_X!#f2q~>C$|!0{8)y%{YPTuN&#|dagU zp}?|S#)Z+2Qe?X+Oddtr{!Op%R!cqI^PX1Qy1}^-pEZ5pY-z}g!l(shM?0Ze_fgaO zswv}koRFuBScZzGS=xjT+x#FaZL6lmgbi{x#L-r)-QY~)W3cv)e(@j^?$fI|l!e4( zUWmPV%h(#Yd{kQ;_PAC+y6jB8mZIU&e~{C=>EGzOe@F-Zf8$kp;6du}VXst3QrPry zouI7Bht!=7&yqU#yp&^tm_B#tz>&oAh!= z{_OR!=nK_4nALk5Y5hpa#fgCY!l7x;U^; zh1|mSo&>4ulVe;e%q#lS&noX)ReQ3R+p&oyes`0`c8fKfmw2W+*~sb!p3Vh-DJM)z<5L1FQwD(zQg9iq4LI_<^^ zO9N)zQ%tkdru4Gr&;d}03zW<7;EMECx4<)OqZfAS^vTuc;=+-<6e*yZ?;=03W76`> zg?s$#25L9bdH_0C8^;;rDm|@zhmcU+rpQ(TlJ@^K>i!@8+rOfz_KU{LHMn6PyjKn3 zwH0u!eiQ^Q(I@o(wX-XK6@)NUoI%HDmV#s#J_ zg4#ACzo#=KwZK-(03+n`C&tN=(phy$7pMGgI$^>$9)th1pvJGw_g^H~@xOI}Heqrm z$;gV-h8tPEwXW6zRmUDc1C~Kd}M?uok{_VWa0c?^{VGhbgXy87+u$nOx zr>zb($`9)uliAKCy`s~Kb~Fo-X-m(SPE)UP<_8P|SLnI;|9d6g`y*i5|CW5&e|L^B zq-KDvjbfZC8Cr5XirH0Zl8Di>f7w08+JVqzf-8AD5EX{D=YA%HE!N2=C0X&h6D9&Y zWkNApwe97Vc*ReEa`|4p(hj~OfeZLR)Ml(C{Ji#pdua2v z@yL|Dx$-87*y&Qcxu+P*=Bu)lV@v$1m={*gEDd4vZ})qiJ#1RB`k3JwVwUE%g0sI&n2iL)D*mkLC8?G(*# zi}bQ+{F(J3L2Z8u03#g-V5Djyr{EQ#D@OnaL_197-hZf5@NeS`{14Gh#%G|CTPnr9 zSPhl(1=w3H&y$!NQGHwR8wwb#ch)e5$)@r^7mp@Dz4f||veXv?b1RU_knO#`mB`)8DW??zAT`*QdjdFJQp$p}{=)sp4fVnF$s~-V^88T% zfa~jbT*e9Kkebv@xtx9qf}~&k9|ro1p3cAUIsfVrwBG_Q+Hv9fMTQ4&x0fpbYRv`_ z1Rwz5=gp-P0HF8P)A3uHX+~?igoe&t95_}F_#Y|D-7ZYyeP|E(rc>`;>+dTyRqC*PBi=N(GZ zb6F4;4CtvtQ`nh2xtLhBjH7?c;Q!T+{Uf8Ie$6}ndwyqQYbux0U%cqNUu{pI-iT}c zE4hG-+bE&NL3Pc^Vh6`%4~-NcMPRUKn_Ut@NTHB0dDMaC55 zW7tmOiJ{2HBe9lV&udcClb8pp@uydBP!wiJFw>K2Mpq{?F3OpED=NAoGXesNBZyRasF?t*|aR{NTZsEg+?ja zF#Rw9iOTtmYQcK9fINfgc=&{k8S{R#p5VM*?puGV&O2G0AnO2 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058727_PC7D0HUQU7_medium.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058727_PC7D0HUQU7_medium.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..86c36082c9a38d8633d3c6bd49d9dc81873df427 GIT binary patch literal 47416 zcmeEu2|Sel+V=66Rk*NJ31OOeKVD*|(WemJlHnG1+&MJz2)Sgk;~B zvF~QA!)))}d7k%q&pH3+InQ~Yb55W4Iq!qp$NbE4U-$3&UEk~by{_L)9i~o$PHJmt zX@F>GXh08uZxEFNx~gWNe(kQFfznx<#~1CKt!&PENL{*oR>a-a*3ssymbT_u?K|q4 zXFcSlMJ3L*jq{lr69@tV(a-~?{o`*yQKfQM!Z5loC2ua5!=#Cf( zf*zxS#vG|2B6RN_A!w}pgBukjh^$M}+(kDr9o{NHZ0L33r8cK@+C)-A_w;0e-=Kfy zgAxu{M-W)O9xC$b$S*p;AE=<|)Y14oc=PBV$5KL2MvplN`0wZ{&B z{L0lc?L-iFM9+JH>wF$K+Vp3A5no|D`-uvgUBF0-Z&E=7agVYELTjDUou>y}3fzJ| zPEjx_=rQ`a`08h-VJ|A^zFtyu)!;*Q4w!cvhrT-KpZP#)0M_}j?xv`7+@bkJ2g4ka z#K~<==_RM1FJg(xvgN`@E!``jf-Z34$-ay06KE=^L5*lkIJ`5KR$dv}q-4Hch`Ecj)CLi0gLrw>hn z=_2LaYCG16#w+sPvm?W{L)^-T=}+qr?|Q2~CzfYnxm(^KDQuBp$x;IqPTW3aou87Y z+lI;F;aNK3-Qw*6T%JN7A`SEmbrF2Xx ze+3sUlyKSk-zvB{QuaySyD4UQ*6rMgBpDw=c4>Jjoj#%coiX!=1xna|~O_@6z zsw-{V4b#&A<0K3_A~j;9d_g{Z)~j^B{u?qJN5qp=OXryj9pjFRM4xmy^Nt0&8ed?_6}K@;OjXxR1kjAZf9$08-{u8 zJZoiVyy4dfocaJiNMD`fpZ%aApVC+7#Ayx&a_Ij?{|k;|ovb=iG{3bAmmz3KPw+1I z#bz}3DlsgBlH(%#mP1k&$_8H`gclu+%(Je~E%eTK->p`FxILb75)uc#v!O1K}j zM&H);Jup`lB}EEAB!kWaHf-|W{Ga*y`_Can2wAUN{X|)gDA%1Ku#URlf3}o)JhdWW z>JUU)-D0byf`IT>N|;(#mBhhbrNxNb+FVGJe%X`m{~FaGPgJB0pZprXeB?FT^w~?HK4GEeS^e3*roeTC5CnX_{=$A;^WbuVEr2FOTQViV6sTCTa2 z_on0H%Bc5qaUK$S3wDA2U5Z=RG>f)M;$=Tp0X7-`&r17yM@qj|0K7Qy`=oQwk_(mg zyor}~+f(}lKwCzMZYaV^6k(_@_87bvu^``LxhGS zr;Wy5X03}i`%z{S*;Ta-Z0Z2)^?$Jq0P!#59sgb`@THc{YsSy9Eh3D zgR)_Y5IkP4^>JG>vnJIFFedQLhrBi`YtYRYcs%D$1&Kp^#)%~1G^%GFFzF<7`$v@Q z7tob6Z>NtXvkT}a7YS>uI)`uyR^L)L?MR}(F&fM|p#}_S{KDya?H9;|sZF5WLG$?kG z3gU+@X+sFoK&+U3hit5&C?kKogW|00si0PIaK1jpeLqdl5_SL|_ghr=KTQRl*eBqr zpzC)4!(suUhPO~=CTpz#uP}`cP90Kyi0#R+jlNyJsxWpgUi#$CQ<}n`RB~f|3_J{A zyX+bI;$JXdw(LYw%VmC-}GhHTGdp>&Dcomihtl6 zo-1LG)|<81Fwx#Hf~U+|=05oplARiQNZa}sr{oYrVT`4Mgs<6BK@So!b7fnB{%|UY z?i&>p^Qn{y`m8&(Wjf!CJc0-!AZJ?p$$o%rHKUiqL>QwmB?98!K_+t85klq`@{fU^ zLDm~0=cZ)4i7bs&Q0iP^r!XG7;`#{rQc`Wp1JK4xRUiC!4^Y1*2#M9EWt%N4`FHJ( z=dl!&>dF=?sH>kAq#St;y$T=+h8y8@oOGoarOXGrqpqTqFNVbLk@B`vYruOi2q`Z3 z$XRfxGE7D)bPrGI3`fi{c{E}6+aMA4-Qw$)`)l5a)E$HlsAOmYx}A7>(_fks@fAQ@ zzW^g8(PxnZfErcw&^OLqB#B*|ayy`pQm=q)w~yx}F&>`5QbA!5!`&@lnqRA?i_ba{ z#GFWp3qV=r;TPnNdF-C9p97Fh>H?1C*9Tow08_%Et1hEjgLArGQ;yew+ed+-!jFUJ z|IL-eUnA@u=uw!i;NSYm5SU$3P4(vOD59fgNuGCaG>5&FO8X7x(Hok_awzty1?sDDYXzn(JmAYyelDQ z<$A6B&q^8<)Lv_g+B*|W1*x~g9}l%m;qFcyD0InC8l9=2rAqMb3b&`b>z&w1?)W53u(H(SA$Cl+N=LxC@<>JLn-kBw`*%1pggCo|KlMKI z(ByuYHDOord1S;)$J0}WF5=k9sw)2HH0^S`t@V+F3-L{QA$P`BR7H}(^tJ||Nu%Bm zlBf03)dH!Y7bpGmHlZU`d#X6t+Gd8@#e#gF)=(2E*Mf-)=E*a1S6I<~G2;pQp$BUk z)?@Q&`^jCrOr`_G$aj7WK=AA*s9+V-{0;i<;B}|#ZRMC#qZUUM_rm?P(2sgB*2q95 zx`z_A5HEkwrcl65r2Nqu>Ci19i+qZx%#Sx>E~6qda|ChR;gdK895mQe4tsam`GLQx z>AashQGmpczu{Pc0qD;bWMpuQKKQdD_@9zc(_QW)1${B+7{@RE&0 zFIfm@wyo9YaEEe?;&Mb(vPYaoB2Z&&<^>FM>X>*zBt1CGh< zc=_FuVy*UE^NP*B9ab6&P)GMu^!|OsOUQa?^+o*b^g{eBpI+0~!X$4`iJr2We3y53 zBJMtjN*=(KA=&_EyMmb;@Vi0Bjc_2saR8M@Ne)Ucv|KpC z`jvbnR>wEnc+(_78b;Q(I?wCm^us1$vwrr%qOhr{;Il{0J^I@3+$(Y2LCT`}KtB0W z>xWxM0o>r@dc^2=0fCC?tG+{{NCwPwT(TIR!x>H>n!Ic@M!zJZY>32sWGP~X;sEz2 zag$MgoH*{^bQK!D-tgEejx2?DY>$sCJlpUl9R=;6Z|0ys+%`4nK?oVtwJ7xupcK8% zP#AY{uzD%YszyEZINI!uH8xWMIdKLc7<}kE6$EBn_pNeq$n$CiUJ+?e+qWNSHjJwd zXDG6S4}NEGku6GN^4*`8VKPU73!~w3I8)tiZ=9Y#H!`udTyM*PI7KoXxaf|4oh)8k zR1wtMGJCf&dUEnugP_Z4vq!lRAm$g%&-#}iOSf&bN;DN6COG(iHP(Vi05zm?KcVRf z*H)j2+DPoW*y_FhMa78G-t!y&ge8@3&Lva{a&2e^KC0Ftp0Tv#Ae;BBAlt6@oRrx) zC}BIqQ~QOs=i{4PL}K2`6g{O5O0RsHjRp%} zuk3eKEpqNF@rMPH;%Y-vYdtchYhd{WOTL#I4T9RH*Hn*?LJhw2b~A(9i&k+;!M zw#v*E2nfMrS5$o8i@C8l&z%Z-{a8$IhFfk*-+Z#ycOr31vuHc`4Q3hzr&qS&Nc59F zWNAEcN!B;$RSp&8Hjl)Q9*EB>kl3c0RZmxZ>Gz|}SD&%JS?{#AS~NE?OODy+1)N*% z@U^CzgCmI4DD9AaXGVj@!$^-Zx#;8I-sr+odrQs%!6lI^u@vE1qujIAm0F z&j=T}eg->ouD_s956>YLWmV9CstGKr^Rx8v$stL>BjDV%Y+k|Bs0b&L$Aqocp?*Gya~eq{Wf}}pK^|eIwwq8|uwxA!b zR@LO>o|W=TiwFqBAf|Mpr`e2=DHPv>dd_LD(~EYmB0P0l)!IbcJcu$IYk3^i{Yk$a zody)f9&Cp2pjwm6u2AByCQxp_Q?chqey0p3D%(n3 zihLA5x_y>gX<~FE*)YOm?~Q*vzRTAL-=E$5Y-MD@py77f&<#IUoF#=ZYIcgZ`DofK zbDQ#+%r7i^whW0rjpX{RtQpsJIGn&tVeG=$*GJtLN>Cqqy~!PiGOiTT+Iu6P4Y^~# z!X&=9GHX9TG`7$z(qq;}=^Q5eW>UJL$KBr3$=8B#Yc4OIpV58Yz3@%L3RBmw=4@2E=?wNF3m++&Y;7nAe_!74Iy{X7fm~9PS62nAi(7K z-c$c$SKJQ+5I-j%Y&tlsmfkC62g+sp#zty^`Tf=pbM$_i-!rKGIBe71d$Sc9V8Kt= zoj$Y4V!!uo@)1{QHSK;}%!BnPy`mK*)_SxK%lo^T>-rN2VV0-77tk!H2^ZDf;T*NR zc2kUg{`mwwvyfI~=J!X|EnMm=#gasc%bJ6gribtPV|%7c`$~Wm>-~S4_x&e%A$X|) zAOHcXrGJT2{H_1PcaILu@*?R}=?P&LCfDs^t`90b5sq;^AA21-F^`#+BRIGy#~ZfG zet#UNacQGf==BxBp%GvX3^8w}aEX3KWjJ}1Dp|5oobr}~g!Y)`I$SrG>@V{(fy17s zt>w6?jBFFWe%zHP|5-XpclnRXE&n$Dd&+!p{m&ERXET@=P&Gd&-f{&|3sevB!u^52 zXGFE_XaEXwFk4OuHsl~6p!7V0S+f&xyH|>!_3znfyYJ`y z0I%r!y#WG434!}ebDp1m|9YcRmF+0(J;w9J!Z+yU=TuM)Jhg25N`%mN#J8&#$$GBz z>Kdv&8$TzQWA{ktcRVS>A7g%ykMrN>VO$9h77Dj02=ezLy|lk`!>mF`doyTkGd{G=NM|%`jMz z^8&5ws`x>TQ`@l}W!`f$pHEGc{$`s%%|&Ovhj3oR)Ksx9rWwgNPMB>p)=s|M`%qH( zPUeXe&`Q=o|KpeAM^wbue6G{bvA*@?8I3j3PxyE%*YM4~FKra}586d^44&<9Dw;2DhCaxTDy@9j4vWalL1atjO7=Imzu zEVLSf!~A@)7@84g46A({&&A5`{Cn6(6FcpTxJ*F((;YK*f_0&AcQ!eZ4N^o2uVgv@ z7`ta<%gf&xC&Ws?#Ce{ARZImHR5x+d`gOH+MXzW*m%QsO=;o&{WcxYD*z8^vPO~)< zMDV?ayT3az6oVLo*}FXbF)em7$!Du|3$4ij0avPH|*eNbh=>SrS=! zL}BwDvU=7LIV`vHK4G#U24xW(d_BHA;q~rRJ@};(xDFLO>KRxna#!=n(aNu4bn%9e z8iC|FRNDiXKd^?L#l$>|TkK0u%?}6_Z=r&0BS$?OktMKKF?R-NFoTi`7f=~DhP{Wu z)vbg(p3vu6$F$SmJt`d5)@DvH<8N0-$daxB==_TG7~hg2TmJRX%epOQe$ zRM|od@zxUTvfk4K%`mrJ7VoS)wuoTbu8?Gp#Q?AX9-XqC|^s_49o4Yn|&8s9)GQRdtS0! z&u~pNeKN$%@u@!iBni-b0ovdQB7w>HB_a&XwtfWW6tZVl*a;8dkKAxoD8+yWI*)t@a`k{6Ljut z|BxJ$&8udd{_gCBeK)%a?yd3pvdDP-wzRR$-N%_|?oU9dr9Z}TR02e|tGKG8?u_E3 zC=kS_lf&gCPuahKiqSz2ycAP071Y;+ zEUpOeIBP4xc4mnjs$B`)Z?_|vqVqef7q54x6>Sd>KO6YE0)U23@ZZ{A^p9`f0TP|f zFnKr~g5B$7^H6w3{yk zsWGwrTdGFT4N=nYA{{iTc%sNU|=Fb zi_~S18cMtvneZD+eqek9WMc_XXx(I*we2bA6%S+dtw>kLmWb#Ye5sS-5nT$ z2y|V>8*etFKK0Tqu^i6mg|MWFkUwxNeD`)sA=_{Z{UdpIOHRk#MQFaW?_2YHY)2J; zdQ7yP7%}1QtOgV7l3#|_*XUU&&fbScodxmH zBhJTf?2Fd(w~p3spc|;5k*h7PUf{jgxJ!8)0I2jtjG9AgcrjHE{67y6Q{ckyd+z2= zUX@vLcIVUf@IL}1ClZ^gKZE*5zpHwji0;*yW2r2@*wv-QaR${}!5uCK@1TJQE!YMX zv@Vk({2FRQ1%+^(sCxNiNcx1#w{y-&IdlWlVR)fu`s_&Z303**j0yUGM{b&R|NUTJ zC-Av7$j*Cz4B}s$!r(mB5S(UXWPw)&EKKhu!?))r`^FXaEhFO^I$(wBzMKEcznKT(CuMo)DnshGo5K6 zhi|Ij@qBR(=re?>3ub>ktFo52V)hoLzBpN{IwVKdYZ`s`H3*G#&6(1Ew$E))8Zl*X z+b)oE_g!{hY5~3FqMcEaruSzcq+3ei8ur-*^>e)W0&=t41#$EUB8eX-2t8iQ?Pb}3 z%gyeSIR9dAN7|tS)%;qH5R7BKuE$1tij)6velueBUb!(`f8hu+7O^+fy--3px2+Oh z3*`U^1sbLDqWET!#`iT&4lcnR5DXi+u7{6Uodt09q*s8}hn=1Gqqg{%CXb$Ic8JA2 zVi{t={2E_Bo0_yl)XfaOQ;(&i0$9AANYbeK?!ya+Q-JjM6ppnB0<#{M{g__(U0L_E zv9^>4lqnlM0Vh~NU!rzyk)(jBX!|Mo{psTx__`_bTQ`)bitLPR(;G3IxkUvr)v9`_ znMW9MR#xliq$Km4zTL{O-90V7uTfnrIbu0YVoQ%A$>F};d?j7HToiC@$p@C>h88~^ z5_CaZ-&p0i-03@E;YrQFMeM!`4na=mP9D8)hGhsQ>GOJ{ZuU1Z(wYET=Bs=eco0P9DP|^Wphul^JGfLDwl(XqTFMR& zoGOL4Msky!@CzyKGxyaq%1bNb#IGK!HS>LZ{5PiBFT6!_LmPpuB;O9oGR26r=aom% zcwyhC5Pal2ORd=F>@MjTA>9`jz>qxO@$<%uNv~x z%<0avtGH#V6Bg8dNb`ec_yj#-)6WvP99pw_MB4kn<%mPqnI1~yi0keuKdrB71}K^Z zehzY-QjcMEq(CUWwOltdtcnM%C>>Sd{Ctm3nUf*5=utb;Y4XS6tk$VuD=h{hq9Y2} zFA|6G&RcN3nPDLLRi%^Gk;OX@crVcvel_C#=SXYlYwtyhU_9iI1#ud-;k51v)I!_w zl9alC9qO}!H^%2+7a+WUh)GYj0NV67ZD#t1GwW?CC=$xGrh3dzY^WNJxJwS@HEU=k zyRC1vyj*EQoJKc*SqTBeW*`?X&Xr5CNe)_~;a&yp7=Nbf)|ECCsT+QN|YdjakJ(mMt|#_f zgBL?_x1A2;;3tqnT|<;Na)gQB4Q1MpI9Dl;A3#IN;LPmQ&;!dUCemGU4Rmf*8DP!# zQvens3NLd9IwpLO?So#clqxl#$-toIBbw0Nx2yRflESmAiUiJgx%=2pg=sf({SRZ~_U~uZSvO2Xcx=DkwDV&pvML8|B*W ziiOQG!}^mHj5Q|Y({s%|^(@+#wSE+}K~FS$Jhf-{`ILnQPf<@eHlYXa%=LDH4~!yhANDJt_og#%D{ zD89-@RFHrMAXLL+kW1E>l&g`1{djU8Deqtn4J`s@pt&}D7Vq-mg>&g@8E)~Q;2{)y@1TLQ;gqT|E zD?bT={@8m)&e(zga@upBMLv8aMN9Y%bd3cF5=jc;3Fk@5h#l(b$fD+%`Bu^#yR6 zrQSg9{5N&g{zJP}Ip?Ozz%8qK&yfseXtuT$_wzwxy3doXDxI&pEMdHeOuNy)(ZxK! zIy4JN6yke9R8U|$-eHID(qb1-2Q^uLAcD|ZRNS`OpL5{vNZuWLm9oB)zGwFFOG528 z?v?KNG6#cI^8rb@Gs_7xdmA=il5MWocmO@Dzkyrfl2tOZ zkcq=cd%M`1eb=QEDtVB46i$0Hm`A9$3vKVy8uEkp;7r^Ic*v&ffE9kHM2xq>6JuHL zC00>%V5sf`Zru!$Ch-v(XCfJuq?@xQK37~sB^;%hxuSy5Fx_6+NfQ!GFSlX7eK8X; zf@nf=3J?m2N`y!28Eh*&cLlVn>E>U(&CM2GNd(VYY{TCI!nq!J^a2!!^c?WUgs!%i zjTVC9% zO80B)ysY3iXrx{c<%H%Mzs>$4q@+&nX@Ym9}H64Jn8Wh63%v9J77Zd{_*~ ztW&iwyPKTh&XasYYTghE69g1o@$E}B!h5d?t={;tnZe4Lj)Hdjrevdc$6e>f(5&eB z_WcXhqqTj^n!^Tne0NnXSq(?~pPBu&M0SY(4`^(2o0fq7acBoPe4Gjn-K;m(N4Z_n z3f~=tklA91FnB1cB8!w{6M}TZx>fUPH9Y}D`-H;pH6_6^1{KjXU7TbCY2|b zYR^A#;MZtq_EH3pj}{`?f6@CxAh+?G!BxX52G~e{$p|VBSoVHVJJ;av;W&Ndh?@Qn zWg~Aqv{7EqHDyImHII(Tq(Hb;`Yprw)8omls%3}1`r~HJo^)^Zcyy14>d!{0iPQF< z^a)*V9E#)j!(T4YGL967CWVUcG0dPz>|t2FCbNSw7I|j$v8hg-n{E}I60MkbBeY9j zIOhORqKoWP?TIBsdYq*!_YQe3E?DlM(<3aeYp)OEg#z-ELl+k&loXR~7WF`(;=zR?e}p=^>|>U(ZnG7AL@gdKT!Bzd;zOpt(4&tCYGy z%ev2-GYp6QgmZx;N%yVpgTMyY4rHipO0jI!vsqjHg>8i9{yykJ@Ww9 z{F^pM{sVV{84T_wN}gES|C*WR99PceT+_7O=A0cW-9$O^rVc1&Y>`u8(>4vK*Gk8t zAfbN0p{*XBB;is{MLRisnuZvth~rZx(mkw&K165<3nuJhM#E;yHrE;Ou10vl{N;=^ z|HNBmmQBX14fvHQFJi)$67e`6_~Gqt&&uuh3AKZsW@h871Hl`hA*yhv-RiNGPo33bDht2|;DVR`lJL?zAGgv`+3e4p#5m?ggrc`-T1uc3?v#}oxpI1i(UM-ijtlPbMW zfv&?z@eF^&Hn-Vv}k=+}Y0!H&03}Yn7tvZOAkG65qAy}32XD6rry7Lk0C&Gn_#V^ZG2*8lltbr~MQ6 zVm+54x!J=O&EM_)BmpHIOynqlFu5Yza|y1LT1U)|ExKjp_hSMw6efU&$n?}9IS(Pa zKy7EMwU+bGsVv5B6>qq=1CWD!{1Dj#Aa^%1e6PUqsH~Mp83;*k1&Ct6CF)B)`E$R> z-MAs?k#TipHd&b_>}=GD)Ax7-z>ioo)j^1AL?f!44+RuNkB~G zE<*+tb#pDpOyVg7AlfIqBQRswWzrI!!M0?)gl=THgy*;5Nmf=bdk$xIl<=n z^dJHNlLA#Y@!yp?;oaol;afu&;*?*vnIs|I-3`b#unSo6put0WH;n_$=}ZpwV?l2R3~XLH>7SQRO@=stxZ74m}5W(N}NbRK$18 zmIVdqz8gjWcc;?KKx_|9x2~;HK|-6@DRS@{X2%D%sp2b!4Y3J=O!QeU34K5XcCT4vRNbJ&_;OOZNc~_wYq^H zp2{^hI)^fRBDN0mkmT?-q3)7ryy6Xp%EIES`*#^9XJ%}aON!b_DtW^lIMUW$@d&~u zF}$DyVqIaa3vAcc_5$er{~fWnh{7TFg7N!xRST#Fo}IzVH^(xH-!eL6P(eQOv#PTU zlhu=Dqk|Xf&dQh*69-840!#5rSxcDL$mxQx`C)Nck#;*gw$G@Y8S2sRCFG?uJ?sqyN@@c!F*D(JlULa6w?2TO(nn!D%kC6r96 zC+Qeg4N7L#V7?~oZCFnT?w`0nei)~mP@v8^?D_{a*o~i=@M1PxTyrt2PDMI+KkV#3((jJrFX`$?TkYCG+iK@* z*K+|NB6g<%6b*~vM=rVPkvK<@_*^^$LLizGP%}U%U?W81;HwIqJTucbqBhT6kBt3| z_PG+%Vs>7jnA-%FqxOb&`a;2lVF1W-X#U8189Z?vv#1EliHXzI=Vr_%cx4>D1pI$g z0=K@C|AzWjn~l>XaoV&h-O}QIT`iMgU*~L7Ze8}4Li>yoP|KTIx$w)u)`yxOun zo3n$gS99^~kIx6)GXv9ijd2Zkvhp8UNvNA_p4-+HYC=mC_THQ;x%x3qrCs)$NMXsx zpMgw#NmpQxK)|Nh8k;)AhqIlzs%S8IiV7-mFta-!s2{_B8^mY7_*G2!Hm9oW2bYg2 zxGIPy71Y>>PzLI2ODKXkz;|cwBL8f_XgiQIejpoIIm9T$D0&-OeRu^kor2`&(?;=} zqk90PQ-yt0&>PtIAx{JNeO*@>pjXhAuPD6eitx4^Cuh271FD`QfvmCFPfzIFC*_CJBa-N8hPeFF1bV1R5f|2!9??Khksmm9KLIXc4U z!GXhvPzwJU{30dz2CzczXHh{sfSCNJ1L%(;`IG|91Mcy#y#eG0^5PHE?)6nXrin*= z;{$SLC*j)`cSSOEZOmvqJmcPD&8W9}+6Q|;L@*45z7Vt#{f1-*GCt17394uCfZM;S zzLsV0V?JfwUrK!SwLI~OL$ttA!+>LoXPwo3_eshMv)$wqKeg~GJ&GJ$QQzcytWv^v z(RiMi;D$AS2q2EL5P(9KS}Jn-4;M-QcO$Ux9L7lFG*Z}xI?)c(N!gwtJtR8&S>YE0 zccEZ=gt$(ZfWeD`!cRQz#okdBbdLAxfu`UFk<_w4MML9&qK{dktHMB8M6CUteSCCQQv(5DIoaMvHpA48RXR`KAS_1p78z(2w6#S(kqiY?#M?lG30Vr?8-4oV zZ3!=SQDC5`#RsIay2vAfF3!3wo0`HGwJnqQ@nbp=#h(2CaKDcKsFms;&?05*8B(p5 z#2aVLBH30A z9zOBgZ4=7FE1LVd7e^%yn;FaK!S*ds&NReP+q=RW%2f(^lb8W~h7a%bEWfSn1NZJlS&WY%iv-PlRW zxPeX$g&v9WWW^BLgV3cX=w`bG#czFkF?korCgc}ESW*_F)}6G#fJlNNaSVo(Y4-CZ zs*ZLzT$~C%osx9T(nf#{y_UWO9<1h?nKaXy3wkmtYWb)z;_}^yPebFe+e7Qz*n6d9 z<8IT6-vYL!Izh8uOGs8y-p&c0?mXlC&-Zd%;aDesCDlc2@_FamV8&N*PH~Mfv4vg* z`4fflI`V~aaYLE4R{(yzytwW4m+$BC|NK4v{(cB%vwBEL!a8v6!v+dxKKDJHu`Rr0 z{Z!(~Q7t(S@7b~dt<0!Lp>mft7MVUh(^3gU=prWJ<>shUyC3}6vErF}^usztVn{oK z{s*no`{5pZ-Kwzz!HpfPszw!t$z@g=3cep1)?`z;s zOY>rV7pa?CD~L{|(D_{tHtao*BhRHs7HFIuM-H|wsQ?=%Ud)jAv!mC4ut*%dr3!l#9fMMx5s{Tyb@$E1|}KS}+jhj-mpm?b?&f5!Up z-O_l-ljHk&023EN%4-TA4Wd1t7Z^jHx7QFaC`?4WMqK2*2&fQWAT8Jao5T3u2Zq@| zf*)zdd*FP?dQH{K1R{2^aacBz$(FnP&^Y%Ef5yzsqw^JNt0*PDP-j{mG2{g-tzQJkJoe!GOUtU=0nVK5!!j&1C? zmkgF4J^MzejQRAs<*S>PO`X@;T0g0lb-Zr+(a^W|{!J|qAkZG!1MR%sG!nsry^rgKF5?F5P;YZP=oIl0zlJI z9+*g&BI!c@^r4@wsh^Cg-ZBJQVpy}yFAr02--py0(XOVchdIF{)9&vmSeyheSJxSlZ^hRv` zazCtho={y5UmHHHsrwWov)xIQm^r2J-sbLNUWIJcj)g^-8UTHnIOuaZFvNV~+CvFe zm9GqzS5F=j=6%4kYY)o`&RiLYO|7u$oW^;D`+H$Oe|G&?uH)Ii@C0oP=$_B;5EA(w zatct?X8;lKPafO1Q6@T|izK}qD#+kV80C4^KC)gpGj!L9U?a&&1$7SulSxjL3zcwQ zl0tv=k@Da>ag*@UIL0|C#TJ>5FILTdbbVG#2#w{*HSj+n;l#XX;?DTU#mnS6>qJ>< zRmH+HH=_ato$A3$(WBo+&2q-4HQD0iRuE^%NpcR?*zTKdsmOs<4ZzZRi!V%PWq2>p zz}fXRJJ920SGEcvT{4x==686BHPO2zw2#tZaak)_pfjIPvP`_2w>qSwUxU^vc?kO^=5e0hd}SseR4vbhQEN3 zQ1DI17&Pe^GSFM$b@iEiFO9|a{RFlqzp|Bd;zjs)p}!-n!F$q$Q< zbg@$<7RAU^n~oh=?-0DCBFGx@HExS`MUaKm?D{opt-Db^slavQ(nDZ>!V5}lk51^b zuMyuJRdXouiY}@=E*JPLWYeIyc0{@IrO@?iHJocRKc8{b8p|B>pqbjjaepOBY_pQ= zQ=@lzCyoiw!FumB-)YuNe$^izQ+cp)^?&sS7Oll6NEAw4UL-0&)yWoKTwkJfI+ z0`x)ergy%MuDX8vD;8xU(Nh8lNO%?Su6PJ$i3tgGeSLdU*b1y*<}y7RQ?qe+m&5tn z^BmWkKE6nPM(;APg2i(b-5oGxTL@c;{LuP9%EZAlsKwAZp{#G}{vBZNl<^xJx5eO2 zqRMsVM4_M+&JVOzkB^={Jv&*_Z0Sr8$$4}k73G?C>6_F+^}HU9R=*3()=e{^#{vNN zi)696@q>38kw_hk%iqHX*8%-k1@uC*6UpmfpsFgpF{swckdn7)R(P|V?|LxL_ld# zu_FR1y+lwzKtw=5YQBJU5kX3TkSHifjdVpM0wN+pl-?sn0TJm$x*_zM1On;4o4I%H z?^|Z>o!>ii-#c^v;6u(1hn$?V_gc?-p0(Dtig9)c`zW&(6(v))ykzy3!SN*n>!CpDLP;gpNgIBcZs^zd&qC`&-0f_U(|IIi9G~;E{k`{i z&hA`Tk6v}3pRBY3LHZSX=RZx5o1Zu9AJmUgkIVGfvzXifWS@$QlnmvcD^ot-XVZEelbC;O)>n%ebaA71K)wReV4;siVSdPsINW9vvFf2zv>t6Uvf+v zB=D%+Rte=hg3Hn;VFxd*K}kl72Vd8euMw}1&$va0+-c`Z;!i&l#<7x7=hJv-Q5ybL zOntDbaQYc+d(N_H7v^!2MLr^!omS()RGlAlAWMhO@3e_FG#{EOtCVOTzzrzLPs*># zB{^4SEulq>z5O@HE3N-i5`lgY3;)9iGP(&xIbAS=yMWyG5kfKjg$;7A zUR_~q2h-;xdg%~Wjg~`NnjYVtnbnXG9jvrFR;@W<)6jixpFr%TtZLb(Z!1oJsTFpU z8k%|+X!+VQ;+t$yy+hyZ7reDyyDDN48y`vV+CHnn`@z~%q0d2yJ|%hXN{D6tohT`u zQWvW`IeH3X=Kcgy@7<*AOMKf7=w41J0zrkj?nN_0Q@!H;g;ynY<#BVPn&ENUE&0A? zIwoNz(Z!c<R^3$N7g_-+!E2ko*Mj1(D@D*R=dM@Z=F+#miwsFdwc;;F#yjlL{(iSI|bN~CG@9@o3C))%II5$?$Y3HdVeV& z9tAn4L(lr)O?k5b>t!PST3I_tw~$B4=f_5Lju!WP40)&I#y7$}8;as#+`-9@$53*1 zhUo5I;PlL6wHcVsQBl$zh^P_MMK0okm#g-|Voln7VWekvNcg4Q{^T*q)-Zz|*L z>pV+UO;BjS9SI2F2trJ4pXxu2^2?^0_YM_(n3yV&l_&WLIUV?tVA%G|HDQDvRA7`? z<3-rX;>Q}iwlNg1zb<|&hHBa)`C;uHPj$h(unH&N?~QF`!Z@Mr11Ide?^`prXoQ)g z{^{bHg+e?sm$Y5wrrY>i18!jv!@3(IgR!gCtLjo~Az*SxK-fWTY^m8fdc<#!Spm@? zOYj!10jGengqwnbav-&06IfZ`D}|UC13#P>Qhui=V9pCUY@VQr%?M2OO zu2aH(;7FiaA_&V2QrJ(fi-Hs0xy(`s8QQ|Y?SM|$NB!j5{FAFYE*xBXOUK-G?28h< zuvFxEvNJXWY1ei|xX1p(O8ZLr$l5wK%Z-aic02b=tmCFMi#A7N`_-gwi^bU~#OuID zxW=Kp2>7DCUU^%$;Sfq%Va~->L#zjJ-k~hb^OgSx-fHeMA$oI$ZpPYyrs;3>zTH`* zy7U-l8}S*MWD6cB*r(qb1d``Y@6oF45T%J;BY#d>E02GazGY(g!OG{6%2ldhSC_SM zfhnJ{*{j84#maJU{R4CR8XbPg*(r5l&E>NdCaly@v8l*aXJ8U%sc>57JgyV%pp3mu z3nA_6xq1bQ&=R67hYP)MFj~jepo|KF%GV4rYw^7r?T{e^PO4*Q#q*nYS~<-AsrNN0 zVVw(k!D+$mcgC#!VUctlZNh8o8|1Fog zQ)N__g;OWdvsk zO!d#)rZIUOM+%k?JEMjb7i-?RTWe%_qJNr%-gm3G<-Gp)ovLrDKdndiL92DAo&tw5 zhdH;ge*d1If8_2>+jXPWj%CxPFIIQSFRJXZ(681?w6=LYY0u`Xn6Q`%STq9AezdQK zf30I5>%3)Y?alxLg{XnxYJ2=TYNaoTydF`Dd}VBVLrze3i5%oFo3A67{0gOFs(GxW zV}DnPN#^O9AK@mzabLQM{k}XHvBpP2H$T1O_d?vB)Ev`tMkdo}ctp|WYFHk0OpNV8 z$sWhoqlFRs$QPCWd6yx?v7sR1g>d1>wsh4k87z})W8tuvL$MM@h8_4Y})URqsN!&LV&AjqeAUS#T

&Q4^+n=WlZ+B5cJ z!Qc4g>-e@Y2yPjzy31CT%E6d{(ioREuQ>RObpWaB_D2B4aOL2^K zH0Y)*PyE9OV}lWBd0U>v>Y@pAp;`^YsHvQI$r+K8S-8#6iqI&jXj&#&2pYk-YH|kZ zT331OmrLt9D#gLOltx2kzse5S3BkYGRJzsM;^y;%By;=x^2K@k z3jKCf6dZ2i`HsJkdJIN`kKy3URi&PFv_F7rWaDBax0TE zHbGCLwZ*yKRk2OC)zgB!jf78}^0$&%K|Nr#r^7#>vBr(3U*|PuCrl;MZ`aCGHKKZO zZsJ1YcTMzRFZa{d(%<0l@y7d(BMn}kjU<_LK-VNc)T*F3s6wzSBHuP3_ zxbQi-o?LtJua?BE?BzFazS0bMXR4lx=ABKgLKU=E-1{Wv^Qj^Ho||c6`8jiMoQ0>z zat?zIu{Ji0(@v51Jsf}kP~1b)e2OC@R&_44hDgLd;(n|qer&?+d951#%f*RYGP(`Z zP%DvDU1rc``7-{J?&*+vu1nRN&pD!$ybd zbwq=eaL+AJkAbZqLT!y5HWa6y`cJLZK*(XZKuLe7$Z_kpdH{vHs;_ zbHO#*;#o0dR<$_y8oIB**21hPl%cWAw=~9*YCS9MT=L`j29N_{xa9v6jM|oU)_rv_1(bN%=A&-#1^7(g5x(b$SwHGj`x8 z*8x<fSYknqTZn!uo_6j;&*ym$vHr@p+cc=z-O##~tKN3Zlm-)((A)n{yTa6JSI0>P3 z&9p!Oo*)sp`362VmqqjMaY{|xg`Zsh*_;z}hy{V06(a}x0b>|<0*gmK$kqoMfFjG3 z*8J8zD0wOVb@gojb^Xrj!%YdvO3LaH;@g6i?aW~E?`Dm=q=#nE3FFs})swynI>=ev zvC@$Ok(_h_y8AHr!UX5eJn0J4T8k|7 zV%++$-|La)06wkjl-$m^Y;wVAx&R>}dPz-kyjnRyM<}u7gN<&+LA}q&Y-Ma=eM`$M z3^V=g(if>W+N1mWpzY90>vp@x-;l5In7Qy;z4uSB_9(V9vQ@dz0G|7E=tZ8}MX%(F zTjoVhwmK_qA-^6rn6Pd;t9aYwJM|+rQ7wS1YDv!Nja2(Q_)%`B8-K$C%s7Y4z;{m2 zs_G-CJFlW)Dj3I3iFcCaeO~y7j6q@ z2C&Z<6MX$2JaMjLd)j67q`iJ=HscYX_5j2bfeC#KTZmC*QB&cW2-z`IU?8% zm^_WHR$7i8{H0Gre13yvMeEa>1222hKe_kxEVp`{)>Q`>b*NB4S5@&TmAyVX0Qa{V;Y)U3NHb!b5i&@Eye~XnyF#vrniY9K*(FCz!?SVYDY3_lGf0 z(0Di4(YPo<81jwgkBp@1NU`o0R@D%H>~thejRk0WpB;ORYL=g&vZm8|ZhW8;#FKL9 zn6Y7bJ9v5`w|Q{YT^wg~C@>Xg7?&nGV#>7I#&zDml`T?f13R=FIzessHk(07peA0~ z0n>jHx4OwL5Z3kVMmlN#(Vtu?&;;oHinMe$m#5!#olH;+Q}wYmFR~g^n-eDW0EDb1 z4Lj##w``(sq2nv?O+h*ZewlN!FiU;=%17mr=#}oe=d@sQgaJ85ZfLkAMWjr&v$CNj z(bUdDIBBEKlKUEt!@8M@&ZrbSz z6y_N=Y2%jUqOxzge|WllreNs_RX|6Sj^{5#@Q}*hAIvNZQ84W)u;xO2yrvx_E;tg= z?C?Hr^|TTL+T+;VJTc(!B4Kk!;yhLVCl_*lqT{)mDN}vkN~>njCNE!0`TO?PiwuD< znh1rNZ}~14e)b65`BJ!^@K#7pNV5$ZjTCi5subC)IqafBs;Vz4Zut%|UW5gtc!%6< zHEqtvPFZ8kis{x<$Wu*rf`mEKWJJQX^Du{Nv-IOsqrTD^M6|XW4c&;(5b8gBVQ7X` zNl+qY^EL&Ge8u)1R_cvP7w_piPfsZoc^Z_EZGQG#v|DbKhi{O@0$?DH@j@BYRE9-Q z-wNMvk7*sk0G_vkuEDoCRis>w-a6<=aE!iD{|9!#I{yV!NsZC@3vlyP=qfihZGP5h zP?2-l!;8T-VsDzCs$!Qv#7*49HFXd%`%x2FsWeFjNbH6=BXxrC3mf(tfC_dXC~PcU z^+OPuil`PM3a|)T7|LNnL4#D z9Go*ay*(u7cLT`8de~>|3}AUQ;PsqYz=7q1#Y)Ed$au$_EV=!vXYIo&s(?tAvvz zH3y~efjsg;Lsl$8Tahsf9hF~O%Kpi9h>2lLjJ4pKOi_i2Y$7c0u2Cb5Gr~5S!W}@W z@x1~Wa`j1ocTGnrrLr`p)B~tLyyT0M7CU_J=tY7SF;F zxtUV}taGf+4Y#MvV*EXPz{a`%NKRnWukt*&pCi237`21Ui*WA}te{)|nK!u$gAbJq zo;y=@SE<}#v#`XmlI8_VqS3%8#d<2Tba|)hxVS_2xyymygq%3r*Jnb4Ef&o*QTCp1 zE!*7!y~O+8wV2#=zSuGs6w0^N!XF~6rdrhJuk2w@u(o~1ng^)nCs!$yC15gU%yfyY z6Uef%SvfN*>aStFeti3hN$7%v`i@yx`ry=~H#8Ts${u+S(OA{%W%tDT%Z;B-#6~#q z9jVKRJMzL0e!d}J$M?mns0+s0z6ZcM``apJLu8n$sFr9Mb_k9lxB?`tUmcPWwg5tp zF$0mg^P@tjRxMn;Oeb|CI2V9^*j#5vV*~54kI1ReX64U`(1VHVtRw?8gCI=7R{Q`f z_5hBAYlpEPtMFd>e(9&z!wfQM`pqqF!jb{(dE zgW)TN$HhCi*`F6N%U9Kg!ESVs)jU$i?_G@$c+{Yq=6O6dSWnSQRdQ1Q^i)Ci=uAM7 z6>Lb)!~0<4(P*${@o)W@ET@AxC#zqhcHPKTG4-80{;I@n2Fzh!(4I@&n+W3{`rZ_~ zcK6b-LEJvsKvpo%w^CR8*)IjUt~vg`_Oi(zzCX6VOP)#})C(-0g@g?{y1{r$;OX`g zI-nxv`D1-DXigw4mjk+r> z(Nv`$_hw80Q*w=h+)B3WW1k4kBGIgFzy_jVKG2S<#Qh%z;v@ydbCvailodknUpW*O zsv0Ggi6coA=fZtDtW&z`>hGnWi%AlX=KZ)PU#s6ctMu7NuXO;Hzu?{3fN%PIFPr<= zwm?g}FKgA5-hNW^XPJF8dy}IzOwS_n#7HbrqWKMLzNbWHQ1@yDPg)^HJ$pT2^HHO= zP^cAsE{ZJoj29Mr$L!R=x)rq1sD`;aKXzpy^W^m=WzCRh!ENL|M4CoZSDhry%Wl`2 zq1|_2^iq7FDPxHaRq}p*+iuFL*S90&tQfk{r3Kp_yzgD|{Lp1`shpx_(dqy{D`O+? zm6SS%YhfG6kdf|PUuE;?^+RCRCWFer>_ri&dJPFgU~i$HhoPd)m8I;8cn9lO*W+HL zZ@%~g{iSxPZkJq}+(BD2gT3Vg1<#n_noT!(k2comY&RP5cgw%PVjy~4fk~zqy;chY zO&Mzfso3E- zMGoxU@f>RTmmPGc(F0GkebB!t3e@sX9lUMhUYo~;E}GT1l>dp1<_*k5 za_p+^yWhw6F^KK7;I}m79MQ7Zk$J?3J6XIs=8EQ_CCTz@$qQ7Pg@8pa>|9cw{_N3` zCG;YU_bxn!kFO^;GVI~=#Nw`jTJf5V2s8<5sOI$tx4wE}Q5rZpaY;@ckViDJ8T0EJ zg1~#*s5{XypE$=+tp?UCSzs@&TYLxgF3^cfq;)o26L={e+W7Bjf%wmurCt&eWT$LQ~!4ZU_s$xVO;Z4jEhS2cdHh0}FMWqf>X} zIolVrEW$PjMFL;FwmgTDr!|s)gq#4K26uxCStZh&YBK>2@-PBi&0{d_55qhynyMhI z@RFZg3>q3Rvw~}VNQ=*7;(U6@h}Dj;v5%h_jg$>NnEs5t&ym*5(N=fl)E)z1#}EO7 zxx#-p@Q!3bk_y7MaA4pTtnB0*L~g7JwiqYmugb-LqrCjzQWKQ*@5%94es(tW7J4hP zE6wzUd6#bozs^HzkCgZaDiQ!{O=mG7AEVahP7{<(Q!m&)I~SE!k`h7iTHL~vfe)?u z<3+}rg!X8}OUAps8#?6?_!&M;vt}oD^w)&rGCKs|)>p_Gk$&0pX?|t-uWgUF7Tn3^ z1&y!h_$ln7Ay63xdJ(7AaQj$iElWcVIqR(|i~6-!%w5&xSe@i7k4jKiZ{BW@2Z}!Q zBiZA}u=im z-D=jJ6LbE?!@nSk_#=$6{RAh|niu*!&%`jCu}67=bni*9kdlxuwM9`>Qec@PSLI!* zh;2FSo`$so0fA9P&=ypZaL;TFmd(AU;5sju7xbpXPz5pxd{SP*Us3zuSX)y0!wWcW zxAC5<<@~26C)8>p`p`jxS^1*rj>S7D4yWzZ%U};Oj2fOPXe(N5(bVx;RG8oxmU|^{ zM85|ZorSxIMo|`x$eoq35YN7K#)XR#cXhv_WTtI=>=Y+t>J7ltS9&*zMB}`}s^f)=gL zGqh}9L6pHlK^f(FvXnyZv+1Ho!tgGA;T6NmkoKVof82H||7(_Zw{2W+e3}4|!A-d5 zqZr@n#EYsE8YsJ-THk(O5zm*&eWb2~)L+dVSt2EUZ8%K4$^HYoXBY{FVKJm4GdG8X zu0o5sQdr2?FfyJX)Be&O{j#_LWdLKB*ys9>AIpG|rvqd?3L2fnq3Vn6e!;In*Wrh5@ z=5bO{Ww={+#Npv7XyggH#Y?y~AtIvYsJVfvV8R5d?NjqN@CoWrp1GOA;cNGvIuKi} z@;%DNlF^W}1hMGTZ{8AQKbY9sPT;c>{ps64GoMpb0tmRcV>#Vplug~xc>-=#guYbw z@VD6q$ms?gMJnPQ?mP|92lP`OsBj2FpN9z{OtT&%C4eW}0bSh$6Z?aQrzrKX2hlXl zLCa*Jn^VG<7nu_Av16LjM-HC;vkN_Yx?5j~ftA$Pms$sl~Un)Jq!y6$UUL{2-vx zsvE?9p$!o;XJtA$&q%?v17kOlCcsJH&O@`VqCiP@OYS=q{uPd~6B|9h$oIPeY6cHy zo0l0K(#DZR)!9QEW!k}WgJoknGJh2*|2k^^TVMZUDiZ#SbOJ;N^E#)|j-UPVm&(2c zAt*1r!|ha_7N)xL_TAz)^Fvb*+4Wgi66OGso7VUP7Wt|RVVR@nblfQ3_ho7A7xZ{6 zIUMq4X&aJ`mnK69=x_~bxI0mkY#zTI`}!+i>q&YxbW}$yKR9ZY^nJE#-Ra#m5!%hZ z_3UgY2Go-+52k|6v&Ox`+5^V^%#W0co4!IhF!48WY ztId;hRzys4)4gMYExwX@dFr6s7VeU33NvPrE;BXT(tn|-5gf=XlM!c<7e)*FyN-9f zam8*0e0o|H0LS~Gw7sZ}+~?PYxS4HyoUIVfaU6irW-Bl{Al(ynSsk-{v`De@0CV#J z{RK?7Bf%rT$=w6EW{x22VVe z6xe_61+E#gqN*P?Hzz6;kTHHEe7|U#o+wZDFh0>^ti(UwH73nAX}xs)KnmjrPEw#eO-}yz8m%kcrg^REUi2t6IMm4SZIeZzsxi#G%zpUR6n6Q2dtB zh%&2!Emhuwa>er6K3IDra|Tm4!OK0)w~Fl`izz-MjFBo+;q#z_E(vkt_?a*2Fl_dC`mPRxKfs|lY(`Re2E3qrH| zIEqG^ns%a|t>Y=w4?w>@8!)yD4knK^C`9So8TjbBX5K-mSsW`scE5jHuuEJAYTHsi5C~^Q@Z}l zVyD)_e{72YAa zXu5wodu-jvn3a!T^%@jt3<{MQ5+Ytpt?(VKT$5x;k>3UECXy16-_*!UII~Ljy#!ih zf^!MJ{LP!*`GYfcCIeV&sATWS!V3s{lD7@m8(2#{3JL@Q%e2&H?9T6}qc5Z&@LG1>$lSWuyR0 zv1WNh*qLByYqsz>(gH)v3vJZ8Q3PiKq4>O^HX;K{vU9<)uX- z1cMtm1^Wi%p~>%vs)29Em;t90ms)2=pG#R6Sxc;I!%wg-^x@fDfIavK2+@r~jo+Gg z!cVTZbS#Sj+HGZXTn50OTp?f>J5V&>ROkY*KQK3A2pt+$rb$ZF!0p0Bu@L3pmNSov z<70dUKjP<}@$M_*-ozu=_v-n|2p#fu*ei{2K7Fb1{I;|1o;u&!J@;qCef^_yV^{qW z*_kRL6p=fdpRIjv@Zyu5yC;MOU%DJv!>UFWEnIWdN|)`bzP5a$Dlh|d1GnVBo;XVN zeRcQIG1hY(EyrE%$eQ^toFI^fe&qy5apOntcCfcXS({O;$YnpWS2WOC`%&|28x7C$ zK}Q=yTT~|xgtY_8GK5d%BmE3bD621HhOe&d=T;4nQ(}88!W~b2nk+rtCa~+&F;7$0 zGury&VdG@)?T|f2XY;=iHXg&BRNtr)fIIIag*5(xIP{pmOr_jL3}0f+=}wi^6oY;D z7LO9v2%O$B3)|B~x05<|16d$S8D-?uR%1%w-c5lnd~N$zMAFk)DP7e=Y0MKN07d`T zkC=p-tMmBDrGcZk0wcxrVqK0q2*3iER`-p7yHW4>3;dLG1v}@gOXwBc`*G3ULd42v z-R>IOq2S`6bo}Vu&z4bbW@eGGl?9%rgKTtDjqbQw@#ABU0{yNbuLaNaVcR^r7n%TZ zwPf_EZSZ$26++d?=Qj3G#jc7^N9xAF*DwCd;+r9&I9W?I2o7*rk`VJ& zuxMQUU<@9)a>dX{Vr?b1D5N7et44q$;y%y0e;st$)%^ED#$N*=8k!1|2Q^bL`UTWi z(9QIS?ruPU0i^vASgDqbW+H^XpNeLz0_316PYwy@#WJ;R<_671;=3GTY%X^R3nZ3H z9-OsPhrJ|ywvKGeGmAT};F<4^F_I)<_$wmLj_7H+HJ79)VQcB^NHs^M+HiZ`oUedn z7wHGG=qQjy@iBiG`Q{B_Mdb&{v_oFrGlps+I1E>;NfTNdg!L9~Ua}y~IgllkUaIFp6g4#`zUu5?Re?y8ZR|NQl8}dUS20mBSqox)9Di} zUO%7<8R|Q*?PGi4!KS1_?7bWhAz=~c?Aw#aORHkCGsPdvv-euWa5{f-6{UtkUE2>Q zS{DmhEx488txIi;gkRqIhFd5nM6KN&c$m3l))$~QNRx*KWyG-Lvuab<9*f^-tKqz*$=V*~9*WM*0_I)4F?r z-=8qE?n1h$Lysl@o{p7k5zq*mR@BlKbX(!4wM7`~-10m!>>5>@%SAu60?3$P1 zw84xUVHEl76(E~f(wiS+C|AdEi(~AwCmw>l1!g_#%HM03{wL1=?@AN+&+OuMug5it zIq+(os&4rLjjTTH)10hbkKhc9dp&YHo0A*c-AuI7_0Hnjq2REuagM6Xu4Ks@P)YBv-{pT`+{L1NGV{QNR`oBi_SXFUH`N4Lzok=rW zNHNjRw9Vc!wI$cs^2Uv8>8C$6WEpCpZ+Pm|+Q@60KODYQ=2n8wwt9hLNp~DTb@~%2 zFj6*0XgUTuqv}nUp;;W(_C~q*KVZ?mB4(zjUw7Om zdPKQ3mvRSu6$|@`zd0-7Z_S?gfADwzjlIkNI@h$}{45m7TL}0F1FPll9-pDbt1EM51}7pB z;?)y<(nR(I*Q&swqgZC+q{fKUntOI-SvY0%%Go1&KjQV(d`>$30&=E|Z7UGAS5?jTN{ zWk86vs2`jhMHkGj%%;7_emYfJsTKDyD2z2fJL7O_T<`}+_mO$bO!bwo0It1i`Zc!r z-|+gstwr>IhE;z5UQO6qC_5D)z|@`zAA=P!6|npG-QHQ4AQ*2!px?D`T7FXsA#1M#=B2n$832_8huS zoq@LNqeIEnYawLJM+kpe@_2#VkI3)r=hvgB%BqZ6R{&`E0k#~ODMU5fago-WaJ;sz zcE`I5s3B(w{xym_c=^_osOI92r)tkADEO)mcnCz*3Ca6eou@Bn)pfB!zoH*6tkxU1 z1dc&{v_dy4O8s^UOA+n+$P%pVx~=kAY^@ z&;Avm1)v;$lO*~-1~vZLTC!+`l*+IK)*B%rk4A9>m}H}b-0f5cUh#Ixo$`D*@kqW710y2KCsr0$|7a|aFg;|yNqb3y{` zd3|;Cu6cNCO5eSY&$x}&!FCS}8wC}?+JRZgChVhRnIM(yNe0*lyq7McM>H-^Fb*=YDgDn%i{|!t+o_C&P=W()!yK+ExB36UXJw(hH$5l{U=1?r=qLW+_dW{t?nQewmkgT zVyl1h@&6|x{{JmmVA{7D4B6|Js^XvHwuk-b%Ymt~ii@*HGIksyd8}a9azPg+w^q-! z4guDQOGjL!zparI%yvA!6IGm@qmgJ5N10qwAn7Sgn0w zFhI+lah{U@r-|a@f#R(j0NZXDKp!KNDpNdVWAXkTF$vJ=EVS3tAc<1)h4>Pab1F%G zN>28ne3{spikK+6!P+bK1H>uvr?aJFypL<-h(~rk8^0)x73DlXBqqQncNBUIH##e< zONIR8nhG={1+3?VG!rKUzava&dgItIyOYl^lAV*!K0`h`Y5jD{hKRVMjIRT6 z?01Tl+W-I>S2h31jl`y3UEe>o!T7bI@x}=v_rgtyy5Yw<5!N}{d6pXba&)IhIGkwQ z4Ym{|cP)z@im)b~HF)8rWr;BU(HRi(daJazYYnKwx~Rgp9H;IL`>=n&L5~QZ&`wa? zW_k10-k?yaV!o)nh#>cBvA*}DccsG|U)PP2`Hh}njaf9B9XydxRsRxxf6B5Xkuqpo z&V;PAqS=&?D*Tj13_`dN0VQwij~tgiJAI|k^Xa=Xym7MqlDclDmvt? z+%UTw?Hf)fHC(IDISC$flO#*6C9&fW9!wSYeKxii2l06d2ROBzi1YJ{Vo3qUPFHl{2#vz#pM71 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058727_PC7D0HUQU7_thumbnail.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058727_PC7D0HUQU7_thumbnail.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..c0cd734e588d951acfe6176b638d67315038134a GIT binary patch literal 13272 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058728_HPCDZ6OGKO.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058728_HPCDZ6OGKO.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..9243190e1916cf45fd931f046a75ab536a810834 GIT binary patch literal 63842 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058728_HPCDZ6OGKO_medium.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058728_HPCDZ6OGKO_medium.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..86c36082c9a38d8633d3c6bd49d9dc81873df427 GIT binary patch literal 47416 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

&Q4^+n=WlZ+B5cJ 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058728_HPCDZ6OGKO_thumbnail.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058728_HPCDZ6OGKO_thumbnail.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..c0cd734e588d951acfe6176b638d67315038134a GIT binary patch literal 13272 zcmbul2Ut_xwk{k5MG!%H5eNzhihzKKQW6^=A|N0{Y6PS=5$PdOQF;*&kRlO~CMD84 zp(7yD34|VcPpAP>{(SpC-?``PbIZB=u4F#*$&)qLnq$r}$2-P526c=&3%KxDLrVid zM@I*ELHhux6oASD1ND2)^bC|m>|aVdy|T3z@s^XhAtLGJ>F8!JqV-r)j2mcz9X4IKJ@{Q4_gygPA%3cmSY3b?UD#?P8#PPoF(~nt|aoGb7`fvnNWbF;AV^6_x-ad2^S{q+z!dfFI<(@dvNGjW|icb@D2 z`l2=g*w4~kr@Ky1cNuVsosOQJj@kwQ0swTUX`=om?Y~`gr)YCL!+4g7`5f(riVJ{K zboBJ680i0+HSO*|+W!Cs_R}1f3vrZi4~5n1c1|_e{(@ zynOru;#VXjuU@;UcuVQ_9pwiP)ipF9X+1W4W@HR`ZenWl($>!2!O_X{jhDBNub+R= zhmXM_p<&^1@t+eClfERUWM+NO&dJTo|4~|2UQt<9T~piA+ScCD`KzmYaAWS}yN?`p3Kxi{T=1O$T!=)>)RG%8`@S%6hLdu3D??RDW!#d6+NsP$ zi5+gz!TNFiv(Q=g@}ZBbPq(zVDnm*HgNG zp)u}RsdGV^fu>yg>VUtRul;7-{JmM|ctB@TghZ0sRMLTq`E26X51K$cj|~xmC(h0AGa`cC9c)l_N!_PrxI4W>@nQo+K(j2=)8%aayHdxnEsbb4O1~QQc`S zeanodzhCly+aok+BVg?N8UK)wI~f;q0qd*TYFb>A2Trod!S?v69E&-apzk_n2VQW2 ztJ5Z^MY+T#Kp7HEr|yc_FY zS!O+D`ULfE$6s`7TvLbRwjDkbL-jmJ!^be~P4 z`{a&eV%h5L>hRARifqT7UR&pOX&tS=B51pa0V@B!u2M&WN6!9JLKV zZ}w3E5*7qD3i>h?Fu;!a0Lj-i$44Tcz0y@J_1K`z(c!MXIv3!-(dfv0w8h73j?%RJ zt{|d=Ep!xmmcs8F-+s6V`+BW+`I$}3ttZyEDTRv=0;*{fVO~$TRhPFe5ZQsFP;lA> zVP*xtHcVo0TBrdw;67J%6_*Pxy^5-as(OG}VU+)$6RnlT!DjqdM^^)`#a{nA6 zV&`m}JoOUZ&`xoKP2b-Z08~WEAA72`+C$6y&)SWmi`xh`bk<6)XK|q%IhzHA}Z{36hZWi2o@3Rqt^y)9u}7 zCo#QRRKRQ;NYu=_pY&jGp}O<9YUsOB-$`>9S*istM`?lbfX+Yf%b!|h_YG42z!U#; zKun>~Nu~ZJqyC!rtUqRrl5nC)x{gsC87Ksa5WTkz?RN&J)if?!plfq)+iWnQG(^~g zekpdBv8XF;udfxl;osObv>uHVL79k7yB+YW10-Cqgo$1?qO zdFL$zQF3M@HrH2~o}k%$aMdAns z4ZODHd-=M_d;6`0^hXQP%8($IKR`RL zkcB^i^3G+9;RTjio`Y8f$I{f4Z#-71^iz5Zx=2jLAw#YDNmnzcoc+_%LZ#zXASZu1 znIkAe7o^rvXA#X;K_Y9TjvzEHkh68?c2{MX<<`Rk>s!U}NayXo@%aM;etUy3drpU5 z{he6j8_2Mi{9A25&>d_4O~y-*Z^B0sp17C}#Ay=Wp!G1!9(T@yW7U#Ytm8R0jmt%-P{ zyVy4ze#DEDXa#bSt`N9tfe~-azH%Dgt5$!7mO>rI1{-0fw75?Yc;rD45kY8{LC>)e z#4Ae_^UkYPV!|DU@q8V#wLR`y0lw);Y#!(O<;DC#mfY)nEnwBPTI`n3n)H{ll)jNvyRI9gq z=3fl)A0R4G0SrgP0)nSr(6hwunHK+>v#Jo&33;t+>8s4|iF?<%gxfG1!e!r9r!QA% zTNrelzdwE@+ADaD{#z`c{4F+q7CPZsh(r<0hv+fa`^o{E8(ptv^&@f2WlK@gK;2#A zbfe*ZyPvZhN)jt&*f5)~0daq@yGt{S@1GF6hjjejATO?96V{5=E85E|*E;(_(EU@5 zD#R#!XWI-@J;ZYk&F!e*s9etndh*5Ua(Z;6ypWzX6E_(kT9{WHDa7|uKJC4l`s-;4 zqRAXsz&G_i;Y>}2$y!QdutoVv>X$>lb2ehEA>TyrG%vgu%t{`PAaCV)-MA5VcoQr} zVWT+!VEA>CX-SZQ>n%3sRZhUb)OxhFAJ^gYx3V$!?=qI4oMI?Umrz^=Q~-PVBJ4Hq zlTUBqjW1AjszxXGU^np^C~^YIcm3YrzKC~4Q9H@{A$XI8gpQV~`H8L^x&E!bv^soi z@C}E$Dyxu~fjs~7SNDY4b)K|x)(-e1lAkvBPx!1^WQI%isJt&BS>YN~ni{!B>K_#R zkuL9*$7Ah4Jn;#~;Rf&_bMuX6^`A}fT*R0hU=JE*SUg}iRaaUC1hR711-pOyJ z*p-5H3$Clm`&{f%rs1CjlkpPmR6v+%Ob1&S{1PEJPY+vRmh(kJ_o4n?wA@A>H-dS7 zC9`oroGT7wTRAmGL)x~#U4uY(nrbvr0r6?T1BN;(pg#+Rc}$Ar-nod)BN=tLITsO>}$#~O_(*|VeSNCoiEpx5A(c8+@-ngSo907gc90M@sV9y6c_s2cQ&vt3HjqC{lA4_%UbAnu zuxVEYhNuQ+VYLH!A_NotKflVi3+K5sXwEigJjt|K?cEFOU`((U?ISBpw|fTgzeXnC z{dA9!!H4^VleT{A#EKs^XyB{`ac+vti_K!~t)OS0@MgQ* zP}r9=R2TR=AyeJ{b6yV6ipj~;UkT5ME)xwa|p(AW!KRXNAcg-I!V9G zrzf{GzPlO@Z`}cXS%*47mVhMgT*_nncZdykSxU6x@~o9W!eCsjirnj0-zQB7N@1!N z#lAfT@lP=o0h@Nm50a`ItNqd_*L4DEV9d~oFG7bR97DH@FMtnj#d`)^a2w?K6hve) zZPZ2;(5!v(v}_f=Z&ShD_BBp%bgw50B;QH@`^Z+Gc=Rw(Cs5|9;(aHz7}g;2p4k?> z5z>h)U*6y?qzr;Na|t?_Hm||LxU-vu7#6h)?VFu-PNzi#Z%AhIo_H;RbRjg8!L8*>PExlo-(x!&9bLt zT>m)vD|$BFKX-!)AlPUT5~|wX4BM2OT(16UA<`ma{pQ-!s`Rmif;XypsPcjxcZzTZ zA%OA@&P{W-`O(CEM>L76V7}1*EC$$e%&}5*qe>vc5psF5``#snbhp7VNSM+=kulplzG2&Dp&i%JNeZw${%V~+Ue#Ih}4NB5C_vrSC29anuo*lu&rtkgzzB*84PY~|Qr z26tnWrQWv2CWv{AVQ@WlDm>OEVO?Si@z7EzULSdMbA}s%X;1n~FQXC$60$eUcBueS zK?H#};{$t_*~emtEsPVaN)p_;%yM!q@0fW7;aueYl;%lb2=w}c z+Y&?Db3@PDJzW;XA`_oJ{<$E?`Kwro!Zb${IB9nKc-4>Q(HUU0Pn-~4g)mK8AY(-f zK*B7q#~n{Vr^$M{(qu~tG=un240SU~EPC)!TbY7jT%Z<3VoXfL=j7f3_s(-=zF`-aIb`213g6g>$Vdyqhj z!({?;FA^de&%B|g<1PVn=K5atQUekKa)Jy$NF1mA@-KR+999&j}9h% z$^gQNSlXB?+j=7D+2eC0%qmk=j=VPhU!&{*_YFsaao_CWCi|SLkKq@yRgStb%kIuo zw{r|q`$|iULEX*=J68=H1|LmXD$yl;Ubx_Y!TD$ul?6WspGI?0&N+-8OBZeChQ87E zVmh5zaZ!6g_Rv7x_7&p`E&87*QK6v3OZlyy11&fsu6ebXQN2*$DGHEe)L79#aQ{%= z>%TMNt?uD-R{q+uiVq7%O&?>l4qTryGjrM%be`m2BcS%d)F-5(?&a`x*Ufn*F3l}% zfp<-<`7W&Q+I>=}01`I@7oBa_zgi0n-t&8Iv&qCzrhM{t$CcEK`T~-GSrL*tENm6^ z_?-_;7l&UIUbbdUsWU-JoGa9)}DNCnVB!VcMBo~L71YS=>eR_;FE++WdYeyYMmXCk+6qaW6jwy>K*gZs3Q{hJu09L69;o3p3KX}U@O1o`*t6nTdzBNzW)5_(!1Q}+(bUo zt74KqG>v2!=^B@;Sj#pc;NE!0MSYiA}k=87#Qz;5h07!IT(0t!QERNcN=1Pp(IDZJ=b)46piLY7k~M zi(rG@8$fCiWFl;eSBrfvIum)GF`hKBHy;XOW|cm|%!+)0W>urA;2%>f*zB6$5O0Go z3s9Hn&gvECH6$aV`Dvq!@9RG*uErA&2)CMJ$}O?whQR!=1U>RDRK-j>&9+y4CEzAu zz@y?BJY)r03T{T9pN^;lhA3Z16KXD&uE(?e{PdGM?8M#|*he<4}qIPvl*-{kQ| zJO7OHer^+ulTEp4`Ie{oF5}_)T;Bymvk~a3lXovqYNoHdrZ*pFc0KK6ME&DrBsWUH zTnL>VI_mDKHaP7m$NqRO`NlC?`21SDMRxY;U=vPocum{=%d_?YeUA@5M}q3`d3XmE zu;YwAkKSMh?kW;Jq4$Wq2U|omez`>e68uKZt$XItkOb0v6GuvdhNj=-;);Hz(l*;Wlbl_sC#djzWH zXC_d%!!{J-S~?%5qChH2)G*>#O}Lse-*}S9><#niyQ`J~O-5eiq7f9LZS#4pQ__{H z3N-!l8rhQO2w5V!s-cLnW-l6B^8(c^Z~*-Pt>|n+zMJNWZ=L2Cadj!Lk+Iy&O6k6u z`rFn>!t<2>WrAg*d}P7kLi94LK%v9%QtU|*RE{!*EI3)-7WX3>scxQ!Vw@z`lGdwg8zBj-4)~qlic?(B7gR?sk@yl z=J?XyTV_LBL9R<||4OPl_rB)azhi>`mp)M9#05Tr0hilHH+E)gi-8yi#a<45y8|sp zYgBuz`AdSm0=rPDl9^?>x;Ha3gkn8Y)6%m4;#4#KnNwx7$M?-u^tDNA*m4FYwoIJy zJ6PBqc;=r5%=V8)o!nY=e=+}L*e$c++)ip-VPqgHKy=*%T$U8>;({)}>6~nEE;WqD z;ar+u+KaryS;0W($!%28-h`H$+md#GZ^v?+MoZo?W?_8GJ=w~W_wna6>!R)=^wBy+ zbDx(1pUb**hr#1^-nIB7B^B4)i%L1`iQRpDfiYnf7n$}7P9jvk{Dxe3WE0C~$9Se- zbU9kFx?U5v2{Cy0xY=LGy}yx z$-KL!9~ZY2QF~EW<~Qx>*8lua6=*Pm^&(DaWX~}#@GbTlXj=-t5x*rj*dVZe;5Y1U z{d`N+>?Q#=-;Y5a|XhVHu9ox1H4^0g+uG3;QgzAtH(mABio z`^(Dw%I=TX;V}*3FON`Vka0KI2oC&Kxi|@tYwB19#>RNn*L|t~{y}E?`NICxu}?AT z5jn*r?zD1Fk!l%xH&#-eajhx-i22~W_OtSV!iBoVn&?riTwRW(wo|$vUd@L0( zdWfo&F1Bu{3ilN=$w+wcJ@hr7@CSjNJ}iB6tkbj*W#at}Sr@sb8-n!@XM^@6z+e6O zfgIMBmv5UpFBy32*&(&ldHIO}Nu6}6JrXfu-)hnf-Q=S;3#NOe-wYHdf!t9#cxY3U zP1`|<<#czgPTE^}zpz?}aE5QwF|y@nBZtFN-;mB@i=yr@>5&wBjg<+mpNtbC5DfPQ z;Yxs+Oxmk4SFw?a=^k76>LXb;UnwT!VEz|7r8o03MqHcTajumWl|M!+8}u#*9XR(V z0Fn&Ru_vD;vv#mf!MP_lO>erq*|c+*DV=QN1Gd=!6WoYYz&y=LV_pts4K6gKh#QL5 zr)li*vEvY&Tv6C-$62z~s}VsWi+s}`7O!e)HT8bsJHLXRUU z5BxFYfLzK)s15XDbaq@1ukk>^%YzA{ff+M5xK>ctj*A{|t5mCH4u4mX6NDTj%kpjUdM{$p_;zweE5}_oy&kKAmice~jMAK5>dtV2&k5Xkxs(5|E zRz23Cj_2Y1!J}X`@>#wP%_4sYNkI&`cLPN^g*fo)ga4V?0n^T!Cyb-Q_s+&BXB00WDG7HV+X-G;8z0>LWL=j2!r7 zcyG-LC*NMT44DJIjlrQf%n*<4@tCK zN1S^G0z>0TGAjIQM(rb7Y`?(N`LVAT>q^%z?i)`YrL)idM%~1zdeif2rzC*jM!n+B z&-6`mQipU*<|(p7M({@x*fU@AN9xgK$pTlQe3r#DN%|#FV}gAPvaC2G|8A1N$k-5f zuzLMtVe^3Wh+|BDT&?Cs)yN;=N<21i<)*76sDP;S3qKo96rAkr7|)BbD}18@-qH2V zr5u^^Q@kQFSD)${G)#VW8{B+ZSi4?x?56OpTJw6lCTCP!uFm(*;>OyN>)ue}wtui=G=r&2BnLgM4+ms&7{&p1ywNfWA1e zV7)+UO!49)w<24U&84S=Um=&?t*BogxNmYLK~$R6+|OnY4e&Yr!UqQt zWdk##Q->-FG3QHF|9r4K9l-B!l;xeWp5^WDGE%V>lTMO8QC{=^d=*sYTWhTZdg#2Q zP27EVjIl(QxrekwVS_XEUJEf&dd#p@9otNfN_JBy?CS7b%BcJbc-De#E@gc$(5??U zWQG3u)y>t8UyFBNxDy-c=8>*3@?+O9Vq&V41X*g8oYQ4VP2^1~}>PxXKlCa&; z0_K1(fCP!|IApNf8#b<09_*ilS0D#BUU5A;jkQRX8zSMPT5>+kuz#h9615Kc*ol1R zzPT+@$zoOKY7g2GSba(fviE|BUc>r1rtN@cZ z`&yxVjToHlnLuRA^bJ^-m{Xk#DwSLTT$`i<0yWi|*P)4Mb`Xscp3@B5f`M@jm*+J{ zL{!_ANyRmD10k%rWmhjZBH6-nLoK(+G0a6w1O6MHH}%!M(&qj{f}F8}@*BOwfkCf- zhV~ZBI5(l~phry3GyC1LOC#GALiiBS^nzofx*dA6k9vS1>MKX@9#bl zgiGwA7*W|272k`71bA}pq@zbf*!3qLH=BMG!8d!QI{`umhhMj`M+ItQ|Onx(S5)*>lh#rz3! z%C3VK4}jk(k_|O2bx3C#E&lpG)@dbuqt%J>9kNL5(qvE7n%g%>Pu!<*Ovjp%xrX#D zdl@kUEiv`Thnaiu2n|>jez$FXj*WNq`1=pjvtJ4`{5M`ai{1N!U_Rc~8QxTkkSyLP z7Ok!PWGfcAAV7@1jWywB*Zw!Qw77Bi2?5bkR}qLYb@+6Kqp7Wp^hPaAI2f(nw_|GZ z_0eNRu4~vXaCy>v_OUs|dp26O{ne6ixkN~Uxsv>AkLmLT?v?p2*a^=yS7>XBC0e*w zQ?IfC@Pa>+MYDe&`0-WBHjTKOi_+m6FFZEy%=p-o6F5?K%x>wP1s?!iF2Ppyo31@l zPJ~ML;p*WD+rrF*$G$zSmDnF5jTDaDfe#S{vhje%L$IO9d z5}m8b$+T3lQV{}Ei&!yQgYsUtLKA(8u6MH~u%&%Cuoa0!$ad719oWB6oU--Yz4%fD z(S$zU-`iHmPkG6`Y!Wa~3ir?T+zrJq0fVdOPir#Il;!)0?K@A!IgyQ1yhf+(`@IN= zNc4g`jqX}Ma3`~1u-rSEZ78NSQ_Uzv^|(KF(YVE>VD-p^Sd$1AU?=wy-uBT0c4K*O zS1DNX9!SP&ZKTP{>ArMc@AQ;RIeOBa@}E(rnR3dP({yluTrqWBskH3YCI?do=JlgH z=P-g^ird_a9!5@HWjw20H{(owhL54=Z&cuu8Eis>B0S@@3RF}~)(4QHFmoDw#1zx= z<+L2_xA0?$IW|&!;5kY)l6<~k+5LY5HG#bBh+nDar~o%AV3ma^Lq~fMns)lCk;h)# zjko0Vg8iEfp5ZPvZ!TrSGN2F`?Y=nMLd1kDSs-C2${%)-G6yE0yCwek(m-6a7QDZT zSoH+u5xJ$b@DsX|?UnUE6vo^Y%Lq4PX>lpE6SxvXD&U+|BE>rckJn{sAnuOyA;oVZ zC=I|P$TBg33OM(gmM=k{c%0-|PRy>+_|<+eWgY}VWrFQb@*&Nfd7c&SF9H6rP(%@I z8+wmKn{%wpG6jvK0`5km^>823&9_OvC?0sM?wy^o6Am&P7Au)qzq@q58W@S7hY1m8 zT4ghR>$IYeMVPAUo{0Ar0CC~EKLTroXEA~5&tBrTdGk^XjP2(oHt(IBCw3kLq|Y0d znW$>dHjL{SPlKn;SX<^8r>Fp5Sne=q_|Xq^X4h=^8q=ken<@NUS)G@IjSZU_gfkmi z|6$xyus-4Nah&@{E!Nlq!5(nP*Gkh#;oL48N`B0tUe{`61L@X_>@DM&nt4of#kS!| z%iUub$aXiP1LcgIfxMu~pUJz6Lu;_1AsP#J@w(0O$sdHn=1m zK&eqDVH*w_uMAdfYTtLfsM}aw;s{`MU66dvn%wR{1++91l*o+xKoS#zfa>#v^A~-Y zmk9g#FupRH%%Qn0D20>Z4B1IB@iOK3oYnio3ES1!_}#yX6n%(3U`LPzoHx3O;eR02 zBi|A=4y0GW>9(kz-~h1sWwSQ_(5c%y)%XRI-!`U~;$W168961~w~H z?si+(RepZS9-;~Sg=rnh&+^TM;CJnHhKpUA??`cdxOyBNT)W!{Omx(Tw`&A%;t5op1_;q0pC-;+|;qancwCWE3zD{UZqE5E<+|(_vnsmEU zYmfdID>AAmOvarFl5?jD$d|Rfs=EBFH==`Uc?GLSfRp2{}&jX04d6+i!sCFYXr9qS}!>&NsfeDJCc0;*&a6XmXqYz%*$G z$ypBe7YJJ(&umq#!56jLCoB353W8Je$6r^^kRZA<2dn$czEw|B+iV^LFWL_?xJq>b zFW2z^>kipJ0itQ9qT z)*dJt6%}EiNe}+C_%>)K>hXSr_13+vuoaH5nDNQYK;V{1p96g;bunZH$N=~(Vj*<} zy5B~l<6NOvrl%*Jlnl(Lo*eRW2-cUD*x799IF9@FSq>H1R91|-7j4e-J<9x=@GJn} zyju1oF$YYq$;AJC&NX2CIcYuQHkvKY-nV`*TS9VlsZ__pQ%4(GeGHkWSygEp$~aniMq+j-Z{<53U{V@3N9yL^82tHN6cqbOFR##l{nt*-;%&#L`Qld=rqjdVAxRx+ zTAE?IXKf^hUg^J&`dWKOWpyo32Y->GRS`q>q6z&#v5KHV(KDOXzy_yHM?HFKo(^7iB0+2+u4- zDi)qhk{Qq8bXkKn1+(WC7V-mBrc6ooReN&IE8w@_9dViqi`k_u6+5OcAv@PM-e^$d z#@L#Y@u$RT1OCVF17CsOH&!V%Y0I?+oPGRfb$tPD(K0@z8YK4|`}4K2Q%_)^-ikl1 zXg2l2zZK8XEda}b8&$J0Sfz)zD3?ljYTF(e51ExetukKz*+V-d{3?NJG zqiCGVd6VEWXL8ouD%3Tn&hGm}&kAZM<@udlMl0*k-@e{qi)@v@ z2}iV05_lyc?{0LLhu0BpVf~||1wB}6d zTAJ-OY`ZItk)?u&z4$ceb}KTfeDTD*auI!K8!&kY#@aoMlVx+Yb>Tl2qyqYs@wH46 z`Mun0wqJl78)7%=Cnu{$N2jVP8~T!Qs>|dTzt@&@krH5;q=q7Hp^ZA|3)*~!|9b_> z|D_oDuY%-8UvduG8hM&BU>xrxY)TC1VEz(eS{@1Ci;htCSdvAvs0eyM9}Jk@|DNBI z!umU`MZvt(6!?DTBHDgi0NzItQ`U>PSy`niu~GZsPJmeju>ImZ((ASrjq(epe1Lh) z0@+~N_^Hu%xWFqDCWCGoVwE-Kkq5SFMj>_#>|>ZkTE(>@D5;mFlvcN=f+!lmc#} zP?rjJI&sjJ87`Oz;UNV`aEk@S=BxiQ(J(OTW4jnFq?e~iK80Z;c+OUR0No~9>YQso zerOt1(iLpA)8|4wAw0Tccq}{{Ej7|=v#dFlUUFM%PSkKZI+Dbg*_ckwLt6oPNpy+J zCW9XuCGk(igR>YAjpdd!f9M`%>_{)LrXAJ_)zbZbtuuvng&@IX2rIp(&Sm_8mSw&B z-)X=BTGc9)A<*G}Rp>fyt%dVlP>)kOZ6f&chT1)$^|^nXr?T(FzRh%TqWLEW)4AXA z4%hFSObO?9gLk_6awowX`>5KLqE+Ls{GRucpJfaD7}VVD!J(MeL1!_=dlm?tnn6V~ z&2h!-Nn0Vd{)0WyAZ)4gq0RjEMi0$QNZq>y7yrrbq<@LWzc=rkmrGzOF=^zY=!zez z3yFPjaG!wQn9HwE`C2nluOPZvi7D(ql3pogeWJJFs}_dJX|=I}PWfvekKxznvbBwV zsNSNPqr3C}OgR6~e*c%c_`mDp|6~6nr7^|^wVI^toojz4!|HYL)lD}W4$aHLKe~M{ z7Y*D6rcwca(FFIkp_RVKIf0u4p4TaS2|MePlf8hSoLA literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_8Z63Y323LE.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_8Z63Y323LE.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..9243190e1916cf45fd931f046a75ab536a810834 GIT binary patch literal 63842 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_8Z63Y323LE_medium.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_8Z63Y323LE_medium.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..86c36082c9a38d8633d3c6bd49d9dc81873df427 GIT binary patch literal 47416 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

&Q4^+n=WlZ+B5cJ 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_8Z63Y323LE_thumbnail.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_8Z63Y323LE_thumbnail.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..c0cd734e588d951acfe6176b638d67315038134a GIT binary patch literal 13272 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_U1CXBVYIWS.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_U1CXBVYIWS.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..9243190e1916cf45fd931f046a75ab536a810834 GIT binary patch literal 63842 zcmeEv2Ut^E)^-pOMG+AqMI|aAB_b*!NQ;O80RgFz8Wj}*l`btIDxwmafPm5k5vh@0 zgn)Dq=@5FCUJ_~uDgWWlojWu4`=0NgJKx+H=l?H08#u?qVVAYnde^(w+Aw+;BcT17 zY8q-FCMG7(4d5S$K?fcdqGb@k(7KB}RqepK@s zT>a>y^Jm3R9(8oRdEdsxHT@*F%Ua6|+ma7gIz!GlLn9u+zvDlQ?;50N}ADRx>!Ok8aHB}{B=Z0x(( zx!Kvd#RLuti2dddMg@p#7mFO*V`iq~pdDOH%v?;2Y7hhjVqyh`x;@&TKbUqf1N~*& zwVQnp@Id~4&<-YM<{d1|tgI|7z|&~p|3NHVtOt&rQQ66@ca!b7BhT3=Vj$C$g5t4tEpen&@?bKGDciCF}1pV=dSfV z8(U`=*GF#d9-cnWe4oE~>4yysdmSDT`Q~j@V$%EM4=Ep0({gh2@(T)!ic2c1s%vWN z>Khu{J370%2|c}iqhsR}-zTT0XJ$#uE30ek8{|#O_PCfp%zqlzFC+WaxVV6E?O^VK6(fe> zZ^!)bEu*oP_s75HzGZ-fPekqXM(e@B^Km!y;5?u|+CMZ0Oq4Tl9t76&CYUIP7aMdSuWL0wMlpC;2L-2%OWM#|Cdx_X}PC2ne zEa;E+kIey%FA)o^Q97N76$AZ<4JSo=pQRZ5jDnroY4a`sGhBgrB{K^iV}SDS)I}00 zjUbQ6YpQEZ5GvdTZg~$I)N0+Zst5P1i{sIQgZ^m$*c^BV0IGH@?#+*?W)Usr`8Cbw zS~yL8zG|O*h6X{p@lSDYFm`sCbm7)aXL?rKgb;12j{enbv};yKqGiDPX&W3o6LaiG zRC@m*ePWxg;nA}cpYi3)X7S+gCuGGQ+Z7?>YQk?Hp_%Z)s+ehJWlea=6mr2mM! z@4x92BndFU?kOLBR5R=t&o70^rSp-o=zfiYGd_zGVsuwx7ust}7m1D>_3!ujHaMUc zGRq`I?L^cYP2U8+a}M55n*(IRLhXmd&wrSbe~M{>DAEbMTlc9-DL!9Ib3;}^7}L#y zvgq~HPUQ{~dv}}J_q=SlP}|$L!PCa2cgr-{JKMgP4BZEuke;SLJduCQ6WN}(AKM!V z8oOOs7-6$dC{QbVo%zILWg#-*BgO`GjqsffdYFEZ0eTE7ZxR@toy>l;K`C3Ccp>Ge z&{Q}0CX6~8>vhIhL-2m=U?o=U!_gnn)A|qT6Z7gVtQd}G6`02#kIX7YVxuD0b*pgu zEg$wZ+?!6Jeb`i=tt?4f=Inc+za?5_pZ&ZFX`#+9n3m!}GqRn@vUz`XFtK-820+%u zQ-5HM|D9*?BjXM53Sz-Nb+OZhL)?sJu5Vt%0J$$9X{_lprsS{dFFP5aJiNHA<{|NW zsrQvW6~j_x+I$hQO?ICzu4sxSmI=N5!>;_}cI5}RElfG}*_W5EIK13*W_3q7Oi}OS z`OGnG#5GKrtLw>O?Is6Z0aK|0N!IEO6b$nWk-Ql)oL{VAVt&*nbwK62=SqttBx zWIsCm{mT;}RX<7vq5@E-wS%n0}@tU+>@}6rt1=$2J30v z-u>eW9Fm95%&c#{Y*Byp6azQy=M=Pk*_fNhKV}HV0I5k01_*CAX}XH(W3!<5lcQ62^}Ch@^gnEYu!`+%DH%a%ZJRC zthIg*HuVg-QIRE;g>jjs6u#XYhI_w6M{#bB^3sT6TjwAvl~P+HdTT9godGj$5JA3*^n)82m zUw$++1U*zMKVW&7h)+(}4lMX2{Sj8(v#CDjXeKifpKtG{{4LRDNbm_sWFY=t4Cm%# zsTkEPp3Gq(=iG2y^wjkNREwLV0Q9XK4^+u7n+q#iBBv9vYr2X6IQt`=^5;4D(E^gV=Xfb{B>eymGV4adZ{5b^ z_ES7;D(X5X=rU`_S{yf~Qi`~tSAx}!bYq?`AS*6;pH=n|CE{M%74GJka2gZIyMz_X z>Ui^;7WoLAXJ6uD*kwuBcsFE&chfK*{1=HURqgyqIS_Gu9dx(_H@)190lFx&m_Lfl zPoZKb}L$h7NcS-HqZ+f}C zP_CJcnWT6vykawY{XGe`9*oU&fx3Oe#|mybxSuVw|KhkAMH2X&X)7y$$9a4ly5QxX zYKl;vm`p|BbPk8;Db9R-TL1uMlViVS%`eW1CAb2d>oFS!NbnN_R8-!W{4w(v`5O~V z*yf@TzTVyO%M#P^Ym{LM6V4UKPRoQ%7`4IX*2-6z&92dpOJ@?Lzf*t!nnBBdF=0@H zi^O%@*r9j!i8BP*oDHrM7b$T*B%CFYFu#;(DWQS0HKsTTUUig;747)QyduM$%7?o8 zu~NTR@DLpBoJ8G0^nGREwLYj)hdgibn7#))n?dI#pJ==2U*+j#MfZFc$pBqC@yWeN zGSFDiLidFju5_Fz>nn6Ab+qh=;o6R7WsGJftSWwIQxRtG%u!7C{g0T765hpvRg~OC z6cmn*OhX+YDlHdAe$cpAN6wsbq#S@dSRSQCk4v<>W6bY7Y#oA!FhFP=**K)ez-wBo z>KjC^%_834o1dIs+5f)NrP#6y^L=u7BZMyOLUjaw;{E%qfG9gPU_s9Oh37?NlC?%2 z=8xI$yFPcYAODSKy-o7@bL zUlN_asv5G&-(J2f$^dceEzE34^|WM<<{^qPRdVh=bRT&H9k4WG_ZXn(4kR|(P8S^2 z56JIt1D@oxQm%X6W{DezJvD9dRh0F}7_#+5wVJV>46z?@s&EVmTKf)mjJbjW%Z$9zwkHs@+BVZ1eIg3DNYlmup=j`~H4u!h@@4G@bOWq+WAG+~3Rr zG-}w-G{G>j&?lb(;%OFYU2RxYb7+53AVbL>xz=a;Nuw-)9Xc(=L2s5lo7ONUdFSZb z({`mP`j#SRJt5Hs#$3vMBYo<+Hfp@AdZA^cD9htkDo*;&rTgSb>{i#@gX>ciPa95AMKylh2rf?8JJ1)$w1GoomXyo6$(ga7pc z^O$~Wjuown`b6ftoAHXN4$%TGEPbp>7;E%;%g2H%#+sPR53h59!+DH${bEkCB1ajZ zXAessjDg!vo;q6irn-G8TEU@c{4v_D12Y<b5P6D|%qlRDGTP*h+LE|?LkP6%1Dw6ov}4J|bZNF1B5 z#EPZu*Mr-WkzXhb5Z0P&+Vf6LeY3#D&sNBEK~|3Bbe8CzibVVqV$1w@SBVU~Q1^iW z*VCpa)&Ul;g?U+KnxbC80I~Flt>kUuVld$91?uZ3=F2q(mUkPU^^(fK8|JWmrAOEo zbobh@Uw~;5mrBjFj9bXvbJn2+*fq8l zI!6K&eVg3V-%3V2>C^*^GV|)C4!Y7!2FMW;uBB&+smp*ZN-d|_RV!WGUYEZ$z?O4= z>w6Tuqh7W)8?pYn3Ph7q{;EO;k*6)OVczv1Z{gI=xaH9THUEJv3a96xPv8Z~l+qGx zUpxPaqVR(Ej#-md)2wJVV*kj&RV)KkoAov3>9?@Mvne`OuRT-gv3Ak8Rv6bkPq&wO#V!lCx)q{L*d)eZ zp;hM5H7GR6el&CAiHsy^skklWzKWCjQDAyWtqhR-s@NDI}6tb*|y1)QgPBB2Lwqz`RkbDd%lT4|KUt7eip3k~!T^ST0TirYcB+DtzZ$lr& zt??;pXrLRdhXGo>MW|_26{&96%T*d$%>L0w#BZJz{e^_NQbxc-WqEa4TlbrnqStF!IBo zMf(Uq!Sj9YyXf%s4nJ+2bG?N~lczza(ab9CRM@SED4D>r81^$SU+oy0SQ8!Sjs^Q_ z=A;&9h}RwhFo{V7ctw4|=HpWLIY~LeneXWX(z*w)0vS+9Cn$OCHm|p)PQqz=~FBIt1g!a`)nu&~RPO(p! z`J}kvw(o#kr;{!j(Nn(Lo8N}Mv!mN4{FH04p$9?O+B!`%VPna}I0G=gnl1yql_+WQ z9{c+twu0xXS7Ofg9NXlktwY?m_9^Etoi~o?KHt{gs{$@7wc3673fRz#$w1HY_N?Z^ zWrZYG*6pMm6_r2ZUi0`MZ^*QubpGwG`)v=ipM8GG8V(z=sLLKJX%zo3w)m{wQ+f>P zSI!}W*)g~nb0|}?pUYCM$Y8#Tth))WL`>gJN$InV;5M;6Pa5cJ`g|J;Z~TM{qD!M> z<16}#qFNP8L^>eH4H^Q=r=q74Ow2QY9S{PSPb*pZC8!0W*EZto?Mo7T-@6F8EB`5vc97Qu95+I z!YhDPO?9tsu(Ne&xK(;(oR&)!>c_R0CDx5DMnZyRq`q9XS2u*O@St^5H)OoA&`4*8KX1pQ`&Iba z^4(#zl*?~zLMRCCU$DfkDH&Y`h0YLXpmlp9>W$HI=EeKX18sk&fPG2c|+y! zelMd1PW4WiK}{&f@ouy9Vx5C>b+fuR7wx7wu}IdewRl0ZL9&zF$fwTTb_H^~ACB|i zcN-)y{JmwUtHqLt8 zb!WNi+!U_Qx#Hz|!`Qd;M{|yG4NC-??06-dDtT_KoUB=je$=Xg zudR;OLb)8%7^s@BV;LmRB!c`3w_=XDs1cGVYSDEeABRkaZG%cYgyJaXL=W8({KZuM(_%IMJR$`6!P_i663&&(KC67bkzJrkGrkaZUNJbJ9< z$!M6B`_r!Aw4FCMpP2T^1}UD&yplZ=_5Im$Arni&8Rd#)+Zf@(%c-KB)6RJ!3gRqJQC>}cV^t7wOe(QppU7wV1Q`Ki#QRXTvPfx4cuH7?Q4{%n@5iAO39jQf`RMKmg~*0Y?gAz#G~L%Rxz}4NvXt>()^36c<{n@y4vp zSro~9{we2@DeOr%c8#-l>q}CNvb0uo(S}Xm-F`on>81D(%$fLvtcHe5r>;&0n8$|9 z1I368P*onW_GL+P%6@t;{$j=H$z3hS)ZZ!Tjz9??6HEQReOr-k3otZ#1tjc=u<{t8 zFHs2Exe<%>F{szQ=Ti}C>}kq*Ot*lvfCKbaJmk~ccWoJ2`R5IS&y9(-bSTExBcve< zqRvy^D^;&HP3VWh;%Pezu6qWX@yoYb=)S*)6CwuJ1@E16=No!WKy%^PY8@gZ!)Xa(%KYU_37gGbk~1}L!QO!s@X%md*-kxMYjN;CyQ2w#T$ z?e!!;a=&~uN(ams^v%F8UT0cVP_oqr)yY}ei1lm+$kS#AXLs=miTBHxy%;%Idqqic zlu9wsv%h_!ePDJvtek0o@00}jnQ@a6-;M$M;jf}W#&>gZGxEY-YiP~( zZ&&J9lG$}zUC)W2eXMk_qmZ$wCvA=K`^^q0_}+pkBH#(`Nvb6Fu*BJj$Y*=Qu@|p% zI(Da4Y8gL!AJ^2Ih?Ur;TR>o4(~g9$RsV1D3#w*=ms^XJpWVvRy9kMwUynW&oH;yG z$jl9R*|J^NgJ%q(ofsRAlQzTLi{IgumQ_dUI>&8j#-$}?Z?vMs<8DVn?*W5<2h7=D z_4%Lo{g0AJ53WGjlWq{>ALCB=t`83e=bCv*yV`$o@-E7v;}vL$D04-P@fSJyVXn`^ zO9oH7+eohzPJ*aVTb|9yJI1JN#f(r5;fW}K{!@MSi?IBb@W$`Nd~0GYx(x5U8R0b0 zu!#S%z-L!};GBX6uZy@`oXTwKiWR8>5-6^0*wg+wd#${3i6g}q#gRPo?qZCZNDQTZ zpzwQ)H@K?}?*46XFfP>C7*K=y06pfRzQ8$dqbM~B|J3olH`Uf8@VcPYOZ2)R^|3O( z>zaSe>ubp(;73m7e9E#)zD*P>LvnN=S3MgYETD;-|(dVVegVC&T4#F-R@V*wzl z%$dEOWe#T*`F7;nbeGL?S@0)dy;g!+xA7m&Us=KZw!7Wfk>!ig_m|GNf{uXfOJO@( zMzLEurK0P&Hs87zF0nUxHzIQ106PEExX?0`LQoSx%kV@@)HUXQ2yE@9gwy z8XCV3LjckXTKn+FafRNyFb(3wDVkS2jUzpbpO}=yL6R;4)Ju!Pj-Om)XtS~i%Cwbw z5N8`mdzU`$fBpI`Aah0g5ZOD{Hi=jCwzvk|BFacWavTD^l zNpgG^0j&$a%^a|meGFrOJWC!QgT16$L9N0lRnp z>9U>kGB7{OQ(fog{14F{j(KT4>lMDwyH`mb0fB=Pu|f6E`v;)&!;b|I2?~;3Z!Mqb zFH@zRvNx9HcL zIBTpasv{s@d;l;|6|UPVlWWi%Zuda4rpf>C8F9Bqa_kAW!;pL^OLCycc)^BZna#~tFRy(E zBpPAU1YU*Aw~=<-DHG)Gx+}F8Yg+SW`PuC^^k+^=r_pv(jpYfp`qPx}&y$$J5#26t zfn>AWK6=LNjyB=DoiwMWWxyOUbFMb5)fgv36W(bxTU%1gm2EZ9Oga(1uQ*{=z@okh z;gF}^FXTfOv>SoWncTO3D0zzae(ROCqSYG?cB}IEljO~04$-lQtdE~>6^pyX6J=N! zpz;3nTsBE_o%>f^uCDZnC4O#J{mnkrPF=rJcrw{q3$1GUIjC>Y{ZMezRGYd!I(>3j zkc#S-(GV^m=3#mxEELz7QXHv~#Hpd;r9RPXpG(L|X=di;8X{%5h*!0`U-{0Y$_Y+{ zSz25A?p4&+k4QOU+FmYka#nW3GrYrmmRH)mdGL1l*E3XY<3#M-g>Og>@(V-a(EG?f z$%%qOc8@;yo6}YAAf{W;QQ4pb((rTN$u2B?;APgkBjD_f7h9r*V+x3B@H`}kp0$qL z->*CTGJlV?K0d?8oyLq%J`wfWyd=q1+A+KYV%i1=PagTQ?eSh$K25qCJj!u;XwFTG z{qUw^(Cybjjb#i_xILmCIKG8B7j>201e^KVOhs{@S+bp4R4Poe$Gst+sa3I0U|0C6n|C&?Hu;x3$>s(0&xE zavv0cW&VgS^j&3T>N_YmCrksDZ{H*k`C+~&C84}f>H3;jKjFCdgmiiv{xlFD2(Eph zlOOgdKKY@qgS(B6>`z9!A-$ujW5Rc6el+t>4%pj&2;TgDP8hTwc1M8=7Pxk(7UXYt zoN4v$4cIGNl01cX9e&MTQKx6~*`pCiY8VxIST+B=jrzEH`?kNKp70a;Beg8>XB4E9 z&aWToeO`GkUnTAt$2Uqg)fXLz+(}i786a;}pACx{zAC-y1WR@u1yA(0_<7H&UjS5L zg5$QTWtEq`H-S)-m{nnMWihyyz==ogNyj`SE2wogur5BZa;w`QJ*TN;Hm+cLC!JxH zv7(~NW*U%=eRl28+NiuJB{78>Koli3*+=m5J^l=kujlN@!RN>1rwY1R=x?*ui_86N zA)^E=^WNb3&taRFYt#0%UwsR$iDv`U{e0X#M1MI8Y~nT9nO@dHf8Ip`qdyZ>=^DB( zU~>Zu(DVcIH!Z$L`0V{+Yk+cAkDl#eJ0kh>6}}0LX61W)7*Hx}S4+!D88ZLGCK$v0 z4W)WA_++4PM)SN=Qyox*g0w-Q06$JIivju!p$C|fv&!}gZ1C6lk+=P!Zs(uo0GJ0s z|1H?{%%vI&A%}XOrg)N}94-$!5#Q^|jM8vYhu#r~L}4j?r2}Rg%>(iqmjK$!$^qDY z%x_8dPf!=eW)>z%2V>V8Bs<@$w}dJs#Kdtu&VZ2h%Q=BDfMCS4;|x#`B2mvcO#^yD z>zQSypc@0kjsW6XG!Pqor#Xhy2piR;!Wf_gfQ+yB8ce@n=?53=M)!Ehtt&3kd2m$> z5Cz0Z-3!!sj=#oKhLbTsMg$YUmPJCSPGtwMN*P<9)*a_nt?W1gUmuJ}%^BL&Ek+O? zHN3GmPM5h%8xCguN%T>lx32f@q9Wc~vNAvx_pC%|-7u;V8je)-CU9{IRXMv!%6q@W zWZFM}^f4eE=NK;ssNdPcY#H<>%(9^n0JtucC!;-($Nu)(@}U&@#Y4ylpwEf}D*=$h z>8}iorooI*J_JYwT*PC+lNWHqHldyyTWy%>_c$fot;mWp&hshNz~kw{&{jO?4AMUi zBH`e66((m{x?aXM!b=Tr0cNmVitgu$EoWYpfE-Zn1hikB(otaYLAVeM5X#>apQ4w( zV}N?o=h8NL*W-cZj>F6~LTFJX4)N8et5IyBz*5f~2JHW}ox@Mwqy)O`NMTzb0$%)s zgLn$=i6S=xBoz0A0lK*dL*vWe7<90bRku8J7mzi_*kjvN8* zyf-cZV;8@<+ue>{1e^%;P6S1>%z}{bt<0f*-xC{tgyf+tp>baS$S*^?N=pMD<73cq zoxlW2@}ZY1Zqvd2gjyi?mN%e$RGjoaq9D}1<6P61eVqG%z{>EOx>n;(@s+Rj%^91w zJOYB+ue1mqd?cL9-5^?a9t-W1+wofiNPm9*9}Og#FhIX#vHdrZq;0SmcpQ*l?#tdQ z@P}2?d*jETQ;qAoy>{0QmhXS~z~qzE$wAQCojW%v%-6@jGr5|lI@G(*{Y66)<7*Dx z%_IAbiCw?p-+O4~0zH4@4k~<0XIl3IdDB(+lH{5gc;nFu@AsRVtgt}Wn<6$nQ`OMg zsbFB+O~>5aMm=z_Ol)3Zq(i^iJC6^J0%D8RBkZ6p=@Hh?W>oRE!o40(HP~8*9hSf3$k|a0 z_aP?g+y|UIEkTCqCJ~1?RFGe<<+?FY1xt`I?3xIXe&lamv#JO85{&}?ngT;gz20zLzdO1nC%@}$rqaXLYEVW*0V=TH!ROSheBbu%zzpfL&0)8UOXp1irL6Fi zQa0FD%Amwj-%r-%;DhhMtM1RBcM7yMM*cC2CXk2^>iPnV=~K${C4g) zzsocQB%7XjWZjd8s6!IV6df5LSobh_Zyu&{l~e~fnP!-gvt2a(+q7Gv|Kl@lYZ@=Q zkE8wJC|3q(Pe&bOh4uglseVT`zzT`jTLQF~6pbf*Dt{UYVSpww z=Z2Ty3{YYypq#PD9jl6c@!Y@&oc6LxW*TF|(? z0pYw+bn!|-soMQVd)cQ}j+X^xQ?1^qjM?wCl~*Eg4OU;1-7rfl9JvVdwYcEaaMoJz zULJ}SZcC0<=~R@-7_f=TA#+6-(=~BIAbiq6=XHWpo*u#_Q}kL_ z%Kn)Mgsf9o;HO&TrZFM=t!7!>x*hRwNkkrAwDaci%7&K(`{W|bt%Q2fXvY}?>IE_z z#zPT-KLfMMWjUQ2_#{I2u8=PT*NzYGn9t0c<2yNj^NQrViy^f>Ocxi-T}kW%+4A?= z+$KYA0T7uvV_Tg=r^@@jn(eVpoOYCLS|m6<_F~G%=nsdZcP?fpDZENc5{|3HA0p?D z*^{*t-BNAE`ZHBG&yRl!bY~+0DaakZFU~FXRF-BQa+9O{bZW^zB~`(7J*BQ?Q$%2C z3n2HWYjD`Pwl7r9&arALuwDqg$3Mq&n4Hz{VOP6P+Kv1qC z#dN6uRqMdzoOgl&XQ3<2_+naAsg4Y8N8nlOZ$qz#^w?5gPQ0UA5wotEOMy?-sR7R4sw!?!1cP21EGBqDA`2}x8ww0-4aF(SqcOZ|<4zJBz@Tvz`$!v_ZbIM?Y9h#+J zgDXbSDb?6ukpqFdIKHjh*`ZqSR#%IMHxGuSSXe4MZoT=+w5G)BiSxx+FLBwzMF^rk znuUt5TcS@X`#jm~)O>%FbT(->kd{^hUXOg{%}Lg(a(sFclXoiHM`=+Q_H_InHGQH* za9V0Ko_97%%co%ImXK(PpdtOTp#e@H@V()T95i>wpEU%h3GEGFNIt3qStiaHP0BJ} z^;b7a2au$F1F#AJd^;c7_Rb&&QGlxxT8P7x@zSxHD}Ad{!3@x{@!HZxA_LT>Vd-UA zBlW~n@PV!$@VN06L;`gO@O&}gTWtX8e{E8hXiFdB9=hh#3kGNy$ez7i21h=jfI0mj zwt!zS9tSvrK-O=4IZ(hreZ2y7>>2|!-?i3-^54=0?4~Yk?K>y^n}wY27~d^r<_18d zSA(|Q4`#bBu;mNWkoEnAz_0N~q6rmoyMZEzmWu#g>{|{vn_G)TXQGpE9J#>E7yXn5 z#5`HvH$}f>DZl__Ndt1HfrKGiVgJRsKyXsed92~j1Cwxn+YR|$qU`_f2s%Y>zn7?O}#kipDt*~+<`*MUd#3NHMJJk6SA$QrEjjJ_vKgd}LXHnIQO}HX7+kq=slVy+?G4q& z_WSJqb6x(qK6}V3bw^~Z>uTLRiF83qGJ#l7 zrhF=q>EvB!dNyY2Eo|sWnL4-*n}k6hIVWK@XpIM;=AX!WR<^i2y^G*G9T|6=mV5gZ z;xgLAVT~DZ9!Ih3A$sZ;lXk@ZwLF_o1*0rd$Wm1+ww_^6)xYjNO6^dV)1&F$)IKrz zO7_kp2TY!I5O38Gzr18XIZ3*+GwH_4VI@AAt_qpZAd@+k>Ah7=SsThs)4)%T=3Jak z$_c&!JyB+Qjuvy@qf4{}3M_)@tAC~M0?jAG$JvO;fCc-Q_)krjd2JPHb&($x>EIeY zO+9^MEA12)Z$GndN2CP^uYj>stArlfq`fZ@f^F1;+ihTbsQk%i z^9!EbZI(Y*(TE|<6RhE~#`cfcxy+6$xF0L!DQkRy zUs-`CjmnE(Ir>U%*DQy~&(+w!Jy1{11$Z@Tc$pV%x_nh15l49UD_gNHj@r|@VvE{7 z9U$k@uY8h~*;UA6GSGM$lz~152r`s9<52vL7_9`WwvJ6;{ zySS|M31lCJuIT~rU}Bhc+nGwt{wgk_pa!t9-aE1a+x?K`uWbOHg{Vsn@?@hull&=5 za@h4tz~B-02f*P;;98e$6#%il%5Sr<&kQbTOmBl=w>gqjdSeh1z6{ zqn8&{+gahQ@T1u#9gVcEIfJLqJamad!YZZwHt;NEr*&|yiA}u` z1A+j$Kr7Kc6V4wG&O?|*>!sJvuPZkv0x3abO3>;)TjagM5c7(v*px}t>8g(~({7wh z`n~3f#XrLUa6J`4T%w8y?&xxv&?X2XV*y|NgI|3efsbmJB@EIy$}~V=8;4=>U}8?g zk#7`BODSqeAD7TnYe*&iL>jyK*GQ*3c3ySW)%zXX&l$gXY5!pDh!cIixu>sx;Og>w zk;3~dQ~Vi}b-hVnWpP1%{VTDdmydi2u|&!W>S=|}0Ur!VK2kk2xoEK?qoA4SkQmAq4y(|s@K zj$Uqbd_I+qQXx$Sj|83`uytB0cHD{vvT%f9I#ok>dD2pVtFFMD{YTrJ8=Mh*yo;Cc zk1^6i*=uN>jqBwR8-^tjv$LuZ5ylNLn!Rm`WuO3iT$zqUxcTZdurd#~vvNWIN+YxF zS-CuHD=EEi)H!il?kJ%H$%>bTe^j@BM=o&1u3fw$W&SwVlT9glxSW7%axr!lm%ug^ zX@41nBqls%VmrE#h*u@skkS#voEoe27hUJVE7Nkl*N@S^3MTc?Z{fp{asSPyvh zff{c#zn%4sP+XTT?B!T#-|IWN=%5~>fOUCKvBaEnKlv5gzJ*xEptEIr-XY_nZr43# zVlO=2v+I63qOs1io#UkWiE1{M390n7vz&ADW5HCs{aTL(dq$DskI-8qNGBukocYpP z9Sf1rXOP7d1M>I`QhBa#GI@+cWhp;$Ge3=7laA5 z!-AzQsGDO2g*lP(YH?Su-5?2BPll1Em0py2!4~+(hso+F7|kz5yU?i9V6Wi{kJ4^R z+(oM0MA~9y`_}#MI9Lt<5VER%Tha6XkS5mN>N;ppwUM|4$Hz5~V0g3xwzyh@tt@Ls zaeboKb1(Z_c1W&)5A)x`h|u5P@Y!T1mPm-|NRvbKVv=%|KYgOTLy5~XyV<3h7CKlV z=xR7jm(%>>47yJ#`T&RQ98)&LgpR|4 z>;S9U3s=8_@Sx-M&#@bN%qseY>*es*qsn&C#_h3;8yuBnj_jt-c{E z;?zg%o+Z9cOiohOx3ij7I?JRd8PbM9lVVyvk14TU^L=Dkb?W zK3jSQZLl8F(aM!P&1)cgwu4RKgW zZuMAq^sFV|gsPhq%L-(!eVeEwPrWLp*#`0$cmM67mk66gsvarGFS&oF&oYl_VwTmD zwEuFynsaBW(wu){7&c-`Ml4350prIEoLzMeMukIub?vbRR&GA>YV-TRDuir~ zR|Z0gKs=Ht0HGfb{?&CU1*;UJaC9otIfvkVf?RIdp3>p^;LhU76p#kjQ;|WRkos$$ z*ztRbsc`k%S`W5y!+$;X1I4XjFY%Qn_;8w2*0ZOa**uM6%20BaqURfX=l;>**0Fp3*@-ZS{6@&)&=MtdU&4l~(;-PwEt9sH;JllB^8qn2g@6P8mm1P8BH^Ws?%r9e zYpsicnIo!vua1{DdIp?6UefYkr#EufSL5gJyPN)=rLg?XtEOt%Ewx57Mr$F8WO;K( z8pMq(dO-Vlm|FLB#lyii*MCB(>6C-n=!DsBN?e%3gQW!FIx(XM#Vmrmgx)Eo`SQp( zD6!FbrGm?+Z(yi90mtNJ(%)X{IM=Y-BbqXo?YbK#g!YuO=79bK|buDB@;t~YXPoTbhH;M|5O>u z!z2^l)Qxy$VOk#P$l`y}{_&%n7$2WHJ&R%LDI^5b3T$XH2PXr>Wr>E1@_luYIZ#vr#cO{3&4J`*RvjaNun9)qBw@wcgW$Pk{{3OyTel$-?x%fR_K=k$V5# zd;VPq%RohIJlEo%qhMa?vr+{^y|P?`!55PTV%WQJp_qH|FCoNGyWyUa+r8~B8xr#= zejg5VFS4nSqq@UkHcSb}vryDjo+CRf2EGWq69=jG*^| zcK+gs{MjM-{c#2k_mENU<)JeV)-tFTpl+q`TYY2yh+97N3M@vcSFQsJA8XLL}-#^J8 zU)P&w1BX~93wSr5pY>Q1ZQd3j+l#-rbASBhP0;T&7ypi9VRtA<2uc4G(|Zi$QV4n< z1tMH8=1qZ|gM(kNH9=u8)nn`PO8ESl;k*~s_VfL+#Dd`SQV)4)=PxVP@k5+v2Wj~H zWt!W~RI_1gNzcfQYXD2u>42-LrlhXICn#x=HZe-TW>EV959_)i zp{=A;Dx2pFN+rYOo`6z!N@~-mzW#S^R>Q5F`kw4$pE210d(?w5lQo4(eMqtWPN(Am zE)dHJO82U`m%3YaxM_^Pb`yu%`jmcoQ5pX2hmzQ)&?|KjR-Ik8KNW5K#8&+C4gY@! z2L6A)_pby);zz={-qsuGg=$pnh`^(0N!};WQ5R6+?{qFNoGFLG{9aHcM8dUkdA z#ANuzbb^6Wd{6SLp;O(koYhp_^&`d3uY*Ry}5m;!~GahirRHKg0Jixg9 z^NZua>50>_C77@uKLXJ|vPSwCV z^|;rPg4=mmY!e<`;8~85Z$|@I-s*mQ)vcT|Vr(RpV$KU&X}08?x^B>Xy5mtCy-2cEBOBpeG3o+hhrCm8(mp_uW~vsKj$Bt-Uj_o>@LILDdU>?78 zVLnYE40!-`nH0_d1ybRslD!|*Dr>njZQcr2WLlc_is~oVkPc4OnU3y{^j9mmRq!cJ z7E>@ZwF*=iX!;7m-HL0BandVnqITsrG86~?~0?D^3BH9;$PlAKj)bEm^+NLyrMcv)Qr2t4=1 zhXK^3l}=N8oGX2pl{xhUUY$HgQlXl8_o)R# zPx(Hiei~mm1$)T=>;_b-MtL6NZD)#)D)-f=F;)a684 z$D`0L>E6{$oqodtZqqA#m;>>pDXrMgmLk?}j(1K+bR)1TgW4|L-WTwG7mJ^ZZ|9)` zpV&8JbTsr&ty&Pqy!|3cL4uBYH7WdVrYV%9w&^U6QdGZAKP%giIRm6w*GV-=9Ttbb zGOP8>P-USj*|B?^KcUmE0n}>MFW_p5(Q-o^4eJ`X<~ozH7H)V-^?LV1LDKWxY{y}^ zGLNJ)cTZM_>S!p<>=p*TB;v!^e4ZaToIRWlQ~s7~+nbEEF}Pe{Q9iN>>x zF8iawLBBV%glLY3&`*w-yIWPs9&tXhFz}!!$jX4)GCGI6T<@#d&n#Ys+WWq zx!gU8Hu!Qg#XmAk*D4Z8nbaMk2%@FUS|=tqn$n$}wo+PN8%$xZoN!N33o;Zj;k%K# zdi<^B+sfZ}AuS@S?zL;BFHC1XvrJO@b`y(R^VG@2Srwl8qWI_p-|dBO1n)?>K4?F^ zg_BAOrf>3~;~<;k5ZW@dhCYm3St0>sHrkC6EVa2^M-j>ZeNnuG18tFGJdklsNJHo_t6r#G zew!lqtDM;^b&Lq-3T)IeOO#h@FHVmEdb@JL8{yX^^7WGJB1I~GwONAiPHd&>q57$u zfT+GaVjRwa={)@!Y^CclO`hxcP#&N#oT0ru^ZqX#JdWyhYQe1zUx||Ms7^1qf5D-~ex_ZQykp;_(d*FvLOGWf>N7No~ z@3op7x+oRe1{^tNeLXTivVQ$oB|plzv}KB}Aqdnw?K-kSrI}&Ihe!1qM$mBe21%d` z6))-GGw1Y8n#Em_j`q)T2(SSU%vnK!_N6CM_p^)Xa(DwexWRU4 zcS#|K$g`*4*%UbU!QSC?IipACY(yIY`D@xNbz>j@l-jr_PT7psPa|9q5MQnlK6}n7 zNeIV!{RZ}qr{Az4s$W~-G~i``5`kVnC#kLT`nFy7Q`d{@>iK1d*H%yM;WTQWC@Te| z8e$RY5R=_=?-tn1*(IMsi8}`O!t{1htT(`=yA zoX`FQW3xVwRtn8Vs&rlVP^!heG?t4Rs(hXHf7<)*xTdyrZLHWZA|QeY3W^jFr3fMr z6#)SOsi8+fK?p^f^h8ksX;Bam5F)*Xi1Zp25Ty4YAV_agLJc8_-->6>jLtph-g{=w z%y(wy{*hk_dvDg-d#$%V@ADe1o)@E>AUK|`O0&05Y9bi5R+sb_v7x*QSYN-5=vR-%9VPDDode&-uu1rie8H#tjgev+ zLT8JjN32Zb7c(8xr=3mv5XkIifY0ubcM-tiwx>Jn|G1sO*Vq^OQF0ppXOXx_{2232 z{o?yISU>q{tiKsTv0Lw)?Q8-0#3#337o@{~7QLQd8K!0N6JHr4#(HW+E^^+lOy09Xp*BL5$p8Pr9NKwX<^85z3H=54d z;p_a8{Wz@dJbAx(?n@GXSseS@9SOI(ZuWojekKda$f<0u?`=LE`AT^|dM-z}v3f!` zVtmD$tgFx~#ojofu$#SGNgdYu8K-DFoR>w?HSk7Uhj%vH<+ zPARdxWWpd??qsEMyY^?wR(5=oC}y48iM9>DMcC1{w&NH|wXtHM5^J+tGUKVTd+2-z z*0YIK;e55?Mz=ZkEomptv0fB3XNVpO%?{S<%-Z`hm{WCC#8@%6i;s7jZLnAI;0+St zg?msHK}I$f6cwJ>Ex?nojB{X&(XOt9C6}R|E^-tx4Lgm^vg7y8q-CA~F4ZzRZkNz% zRLX#j)0ezQn8U`2xK4sYGKs&N0hfNo{upg|A&DF_Qav&`lpjlY-M{ksOE~-U87297 zH&DItU{RL5<9M;plgo=ziGuuvcHV+n{S3&au`SP^;k(sS=Hh$uyW^VW zZ-PA%;;gsKig;IkZmKM4=*)AW9-*8FcObKjLHfez#oOQ8l>hE}{|wH5ZdanzkVeqR zZL`lpJu?j6MUDdNEjNzPj?Q;iY7&bcNaD8JD0LVW2qa^KT*uETXJV$hD_63qUWy$B z)U(D+X6?dRhTC^t5l7$g;&C)(&E4;Z*0siG=HjR;xbJ zh?Soy2ARCpJC6jmK4JJEyGg~=;hw~OH+Mo_j`_Widuu3KfyHASmzE1^CHrczp z(w<)Ux2~4854}%C21Ld9ZZDBdwyOhH3(nMs6;^G2hNW7)ISG36&2@p#0+|weQNxrT z@GtIv;|TeAO7!aKfjm2zDSv||Ia>4zThZT|93=F#XvQfdjo_4USO7^mKN8kjUg%UNh2JV{KM`CS)H>lxy z;xj>Y<*6?d8bNXZ?^S+@R*D`ojqujDqOCw@5;h=vQFX7;N#Dly#_NW9IMvvPNhbCL zxsc3xVAzYBi>}lKjpuo=$B_ZZ9pCJ|Qeb{y7TLgC4s_#m&ZWQl&ecHeM*8Pf4K9i$ z&5*_0ZYKtk!dJgpE;ueC55*s-^qD-A%&H8rg&B;=6R5&6sU@7-QQAo=ByhxG%?3V9 z-7_taSownTxo-hZ3GRmKgB7Um+2kSR3kn`AbICIFM(3`)e; z9|ZxYK%aXcT)L^sJ^XS>x3|vgqp#T>OVxXd!W#*C$<}8}^@;<#PeX|3Hkwud8lp(&?sX?_mHsMgXe{hH|^*;NBYhJW_$h zLA@#=i|tEivrT>ALzgHRP5HyMAufqCPFNiS`y?7tEpej`Yd2Kz$h{@p&(x-xBgjHuOBr#&d(O*eO zIf1o)B&4NEL2G$x;=71ADFVckz;g{ z=&H;mYt?0(i+hF?@IKlt%eiP~w7pZpWK<()sao!^mya@!>aw6Y>$0bKSPJ`L{o3bk zK)RTJzwLjI&uqQkV!($gn+F|=VuokF*9~uDP^Fq<>q-4X_;rr<6;!y~W>7a22)+Kz zI4AlbXbS9uD+8L;9fW*X&U?teji>OJZiZA{nm_&KL5MThQbYsaHGWF z!D;=RZB-(Trd7dGpB3H6XR&r!M(f?!x}hg4I`by(*_DOthFLk2D~pXAJEP@QHQK+d zt-DEpr8y)=hc#|KAD}(F^Js*E(zM?FIpJE#SjG-l!C5%z(3pHuF3u{4Z&=;Wq6mMn zo;KA|q(w#ae8`n=F3R&#Tawoaa#{&^=VLBB;6J(s{6SyfM$AAOd@H=(6`D6(%ts%z zG@V_MB|NPb=Y?ns_w!wQnz=WS0k=gyz4PeaHIy6W+!`0lMOCa(i%ZxBAanKyx?)Lr z@;|W0WFS?MhH&TixgTP}kx$R3J;=HFa9o&F<49RxMpiPxdP4 zI^zI}phd}gog+1bp*)y}&ZN`yKEi4&QS&;qQl3`}6OU0Nms->9;zYVCy zCW{pZ>Q+KFA>3V*ri9?hLQV2lSXRR1XtF*~ePr}o)SS*&hC`G@u-DwvdVt~wc@}+S zR@O&r>(aUF*pG}SQM=MF>3HYDEL{;gNsKaKDyOnr6N`nvG90cm2->Zu_oe*>rVbTy zt0=g@_v8NRq>aQgfy+bT=9W3_X4&h#{l-z+Q9|}+L6k3^A#87~?9dpV915(<}89vjL=An%`I`ziEN& zCb&s%dlXd&K41~h5ea?d#7>hbaZ(H^cNDb@ABPT^KKHJyJZi?=Q6?LF*TeTsrBU8m znFyQ$iJsJjg%<^`_?_D@8{jXCa3XFsG}Mv0p!`c#_g^xjA~(YM%2K|o)= zT7P69fdcBHBKf-aaQy3=WrCyz$ zF5IwxVt%NY6ehkhCx>S1Qrkb0TzGE!nx+Bhm1EjcjrNNfyq^M)59Y4}N5BgIYtgEH0gu=RnndS_AYhO?NCY zbK&dA+iTJeG$oe`_-M-oupx)hJwScF+*}Sf0w#6M8r3bF?gGvJ=v8?gB~47C14dAv zfx>;N0R%uQR7Otc4QBCV9|{EI$jmvuC0`kS(4(ZQ4jVO^uystq7|$Flw2J2g*u*Ba ziEM`(+xm<@NHi5ncjob25K?UPHy{!~YYF_@~5vzCuxD1TCllIwvfsWrZP#1oI_ zB$aPp7h#IO!|IB1rm;1*Df*0Yo=)IawW*`1u?J&=)29g$eYAWakqXZF=1a(Fmz^@q$nLl$epYs$`myO^TFdfpW2NW7Ixn31t`igl8L^VI^pPg5SvH zMT0SHe>$04c_k;l62hX`nkD3r1hso(9x2YWYi3rMLZ$&TT>pfDzSUz9#c_X+y<&F{ z@MzW1^wO?eIP#!Q(q(+AN!>*Zrvgh6((YBkl`qr_A$rP#KF~vx_6_Tte)tO-(~}f_ zEywVnPZ0mdI@BSv&&guGGTewN;g1r#6TFg)H1?_~Z6b$4riI_MyzIXLh0iR=PxuO@ z$aI$GV?Lpl=z4uUZ}|w=i(AA^+pq<(Hx9oayLC?4EEDmlQ z-R9nnOTKD|+&hyeYg>Et?YP)PCic{fGVLnyeAb~Xr9NkelgiW--Yi1N>nwVUoLshO zw;}4Od3o1ohH@Qa&=k+kyh+fgsLrRh!CaajVIrqX)x)!)>WF0F)_L#Ux7W``?XUJ? zJoLz!?Ehdz%_RRyl)(A-9+VJIi9A_JQ`T(Cvg{t%Udw}aw=`x-2{g!O=Vv9uMHfD=+5M7Z3WO79n7rRbT-ofx*%7GvLLNc}*=R{A|Ej<5BX8h{$9>TlG}sK66> ze7`vtIryP8$|HOH44T#`{N#sk{=n#T#Uy-LSfd1|lWD=rM!-ZoM*T@m*2*daH8}%T z`+8i}|}H6&XBeORJNaaaTvCEGCOI+zQClbIOtSg9?>BG1G` z%ukFv5Rz8m(ck_-7JIC@g^w%JA`Fp4@&^}(P;=okCQUx6hL>0;m9&fsGS(r?7Q*Id z3IsbNMh>)_g8Ruf$-M)1V_{zVHg4c;-K3N*rTC1c3#49WMcKV@ahf-LOR z*|4ljjy`C$6V(k&At~CTnVM~rE!TmR7}D(ZrAvttQLdRH8HWgj)kBYKx#7he@3*-Y zOrZ9Ihjjwj5GJTq3o}2MhUA$u!Cl>H52}j;fyQ%A5td^k#Gd0goP5{gU|#MAz~XMl z3hU*0QsiSmf7%tFn-sGpi(zhaY4XOQ{male&jNR!vUTV8Bbl!p>IlWKMyvcz8zn?0 z1wYi%MiOQgY|T)t;O&5)2QmxHZ-~c+e$yOLER5vCPeE072qmj!rWMJLs*Q-Ex087qqv3qx)p&jmMsF#Os zRxPieK4b3oN*7Yb!r`Q)0_fI^n`0P_6UR^lF%X$3Mahtr0^8$Mblfg$>_X@)!W*ALj?2T;I;yl4WbUZJCgAZZJv(K4UuHgA3@ z>Tp^XrKNWfy|#;I2t5k6Mvg_>uJ81ik!$p@!#ev4ggiW0UjVYt2jsJs&N&XwbwS^O zLDr@x25u}Zv?l&!O>HS4-8#xH)pl0^yS^Wabz7N0F>RuCmYy5=Fa?w<+hhdsd~KYVw# zI#F9N$4hC96>gm{{X>V0_fTuo?ERyk-#TdW?kx7O7nWy`X65(cPz~R)z`E%>dVV`n z2^plUs$82_60HJiT|B}lR1Zi3c(??#)qUWNaFsr?vc0lwTZrE&b|cABs7SMzXNjqq z$_#tnJjhMi0eFi4lmBpkiraz+p(*ifnB%)kVx}sO0e#d{9*mEjZ(m9W+i0cqK<46}pOVlbrjj(M>8UhWkiwxW0# zq6i8=?q~hy$$k3H5(+Iy?m>9E6v`AN!|sL!OE~2y6M8nfrUiN+5%Pi*rpi{ztlGOo zHLAp|_`d8`w8K1vuugdVMEA)XUqE9qS>d5vBl^HdGw5l6J?N2w*`-U z7Sdg2ZSHw>JVPN{6P=DlvyvS27Yfr?IX}hrnTYbqW})?Nyk$e1W~61FGkWzcGWK`W zm%+Zv{siT83`J3V22sWMM#6S$+7vWu9PJG7#iLdrhIe%t=BS2c|wr^q);gulPGMRbuuxKY_XKoCA;&7U>|1T1SRO~;5;DyN4&&6 z1Z-X9x+|#Y4Kpp;kOJilg>G#$dLA{5=LhK{6o>}qh)}XL^2(L6$xP&zC*L1)hvns2;@lfr!jEsOaIADgh0LG(L0$ogKohp zXT(k~w`g{-mBE>xcj-BbhxIv>DXg0K?^*chBjhwd*jS-n0;-0OuuC9SowtUijv^#z zvCMkh@oZR`tXH*|N9ov7=+bFQsIWXn*3U5cY2{KbX!&vr`9MSViPjhNJ4EMd!7ZIK zhk0<*vg?G#<1z-U+wJp;-Kn}f&!~zziO-TaatwtNr#j;V1v~5{yAHpP9Vt9$dZ|Z> zDiJWUEZN@Uj#jI96FAGcBnZE@D$ zvq^i0T#bl3HZ{S+@?$|sjOZ;V#xdhaq(tZEH&OL*s6G>(Mp!hA56d(30s4k4E5$Kn zmEtaquRx*~`Pwn@#6*>C!GZ00!~5@#S@f$k6PSE{o&^8PmI@npTm}#7{<<_>`s_`tXfHqM$~G6V1mY5uvUQsO;2kIhcxy$d)z4s5 z6PlHD%U`63?cPMVvgKQwFXjw?2HO^(%wW()0#b5@6|vHd3O%Tt}^-itE7X z{sy7}(uOW^e(%>mD{++g%VE4N>@~=P?cRE+a(+d(qxQw?1ysHF(JOrb7oupB&S$yH zHu$7Dbh>D@#3ZdfL9f1|))47oJCW}mCHG~^;Rqc_tJX8nIW$UgG#e`ZJ z`$NybN@%_JD+6WtpML*dX|S87Rf&=|Ko-am6`ixEgW8S~ZQ`C_0!B$QfCNO-d8E9x zHojGM{8hpT5SsrG7Ms5b<)ShZxg02*z|RnDQ)FANolwqn{{cJz{oZ|m?*!Q<_l#-a zwXBvWQQ!HKEWRzGp9IuiX!QWITA^2*#7XCPqf>zseXWE|&JL2EV6TtQt1<`tEh zHk_`h3ET~J`?Y$W0-WRO9mDF$lwFmJ6wRd!ZgR3~9XsvqYR1u+p*qk^3*ma?BEPQ{qg!s6 zs(o_!oH;50;4NSZsU*&U6A;vSP4wrB1)78k*H#5C=9AYFlWk73DKX^iv3J}H+r?Sb zxw>f2k~Pa_Ym*QA_z4f^y}-b*iLQ>F zY6IPdB#$iS*u6Z8p(gd{yJo%*KCJR3G?@QD>Z$zO+D5>hggd6Ck)%J}=C!go`~@Sk zrNiMmvf#r&ujZ{gt)@zCbrlzJc3gY*MrdjS9Vg&jR;PclXN^YzF6rN##!;kaCb|H< zRJ-tqGvEAGOQfSd!P;{bX%*CFTA23PP&cx}YHvjtsPZ%E8h@N&7(Vf_o?$Ml7-?QX zmhNS%+$q{g3N}*@d5a(DxI0$>_Y_Zm>AM%QJ#bp~O&VXmJ;%YUEl)1KW$o>0qnt-& z)Ri5*ogjDX$#sjq?z}8*){!QykB5d9R&rUcB^?+W_2hZzklHCBZCQLiWov@NyhVj= zm#J92h|K##2J)j89C9jiHeKGwJ(OIJ3kdTsLv7#cWT-(j*y2XR%9el`#`d(GEn*n6 z&W;1Xgr``ex5V;c$X=7B#@K7f6BKddA@eb_oo?{xnrL+eMt#loTkG69z*-r=1aLoC zX!7sD!SK%^Q>Jmi^{qNbbo1B*_1JQMS(;Wb$wrLM_Hfi*DjG$WRk5)y*593SCA|XD zu~}{bY|brB-d@QSIf)Jy+IACT0W|jcb%L5b(=h@?hkY7=trfreql#uDj$;NWk3VH>(epSK3vAMX)ks zp}7zlWvL*cem->Z1-c>Su5R<4Ruk+gjmr!HQEz5R$*YuzG#EbE^ zh!yREzON9jBC{2S>}0+*;piVxin%cr!Wk$CP{4xN8HaZ>Pp;Osbzql0Qa@Sg0~ykgHOW@xiHDR=YjjQq&_& zVY=rSjc<5`_nsc@kk->?JaQ`q~jN+7p)ry$tN+?tu*e4 zPip()!_V1VLl|JrX=#7zQ@3_LUeT$oAZx2P@>KQp)6RW1LNn*y{VlP&KYo1f7uQc} zS@!MbFKoIu!|pJ=JXDySl{HwKYQ6YLy^V3pM(rycS83dw{jqqR=ws%MK23HcyopS~ zMARnUcD{!^1JHpSWM!4mTUkkYYl=2S7F{nLD`h~YHHWVqEs6y?zk#*`2;pfS2^ELk z{hC~-GprB$rXT)*gSWbLw&(X?!?)dQC|Y~IlC$Ma_{WRcmJ72_qijziCoQOmPT}#q z#>@Q?hYTG+%3%-k@!Mif)n~bG8k(AlpagvpY>DzMbDP1V+L2o1>#)*Vdtzgn2_~Zv ze}%t(5KXoenq^Z>9?27n+vV)`OlO^K=yk!p@k8zw@|pKIa}`-%yePQ!p4m5F&liBM zuF-8~*d`*+HHhp1<`?T0ji`)CsOW^LJw8hz*&x6D$xT_MI1pfa1#I)IlV2Anm>5U29HVWD4WF8VbaF7#DegGMI)mg`jtu1s(S6RgzYA5JBe- zS2&fQ>!x>bNU7@ z91na0c}y1c>Sz3#AP6?nwCE2(=L`KIK+b&c2e}OH0~fmSm7!3WiipOQyg`*IwDAN@ zpcYSKY5c(6bd5LANBg5j&x2!3qt?4!Ziz1jzO0zsjL%o8Ypi9q9 zov>2aqfp=$Rk}gHGDM-Ets8TU;KNsj)p=S!xVrb$+FLqGfP*Mblg-A@B+&FGx+bA4 z(2@EEfGim^^MPADLzUJ}tYgB60J%6ZiKhdhJgu64j%@q{()c^m*WVS?{^JWu|M#Vd zcToG02Pfmt6`oY%i|w;$BqncMj&@~?%}K`IemuO)p`ASGIwq6 zsx0ACmnN+Ji))Cy@nP+5;Vcy;Zb9}Vpq|5MAi_hZZ8|tpTZfRbKB4G^se#^ii&2iv zaKEhLbzQ8*YAq!Il}8;SQxR2$s%P}K8F1}W+JRGI3GNzptUqN?pg#e@vQ|#;mW>KGS*|zA{WKBbQwwet>OX{>xc}7dAQ~?~`05JU$-Fp21vF z!6Epox>C-`JVj`v)j8fh&lH%VaxFc%!|VLAFn!-LzKj1GGd3;6t+?%vy-r@$zWg}a zz;%1Ruz2<0qUbcwN)0{&anUSWXD-whfnd_YjiH_f4dK}p-1iS^2{YARJURrYFuA@Q zz3C+UZpKZKC`XGfl%x582zR6tH%6hi@&QL!hP=0YJbcvqlvd4LjBai(Zrqqcfa_&U zFT>8Iy5p~wGh7|-R_N8h#5q(+=m^}tslvLZzZKTfRn?U^t1ZIg>{ok(=s2DrDXL7i z2Sn%k%Gb26^_v>^2_ha3KUQ|TE*#tCZEa!8Vf(f;Dwj)ug7B2KDfpC8lNZGPeCUlBc_@y&Z3pgXM2Ls?KUl z$0SjAOI$lQrDzV?9ADF5lwi=(vv)mo{4~e%1#Wv*6{vJBs*dCu(6Zq_KPX zRY4|&*><>k+`vSr!sfB}%0`ilXEmW**(y0f`yC>_O>k;uIp>5v5t-o4ag?{4uNPX5 z;+!yuG&cy-JQlP1s)P@J_9!K49dk6A7;HQBSs&jB`Z3Z_jt+XAIg)CH#ic$?ZJ=#J ztDcMiE+JTVf3RMd{H_$0uGl!`yK5l7r?-zS<)uyg6#I;r+30i zEuSGY9x3X_=0~QlV$YHXwF=aNV0(gP+;k>wy4;(?Gfk~w+;+*2oY$T$VprG(rntp& zOZC(mdmH-Wb!mV#ju@?OBvKzUjvhv0o)MJ}v9s+_7{$c5w9$g6Z>PU3_8Z!A@uZXB zq}F{l5f2f`B>6-!ZJ5T8A{$dpSHz#cGk6s807gpMmC}%xj9MSFAz^IcfeK+$_T`fAG9Eo&w z89m?h6_h#g{8^l>lh>mbfvZloaz=W`rWLV}u;G5> zA+}2|ZtH4WFS||Z06S??^TA(XMa~bE)n!YTA!2IT67}CV--rpXDj;Mf2${ zn{JI4#`*w3!nkw%pvkHIcW_#YOHx@ymtCI0r2cYj(qBfU-028inx)ouf)<>=S!6rm zzc*^IICh(}_a~#=58(gd=rxyVHP;q)#%rJB;h z-^YDra;#ZWaxxIICCs%)EZ?*+bi0PTj7C{HJPZqQS2gCxi}woTIQ)6w$`pQ2<+X9p zk<_yC#80YZWL0!F8&5+i&nv6^qJom}QDyej55=uJ3=pv}7Vu;z~p7)N7el@;z9ZBt}sog|S>K-%;`@87iFJf?tyPomoUsGc-vT2V+o!uJdq@fNPl>K6jioD>> zfN{aYkjxi)HQkcO4;pX0luL0SX0qzmzqmydj=5B!irN~pd~2d>g=0H6dCI>W3`PA@ z|JcXpaXdT|6bC^+;97sG4*(a9Q**ezzoUOXJe( z*rp>zA3P3jRNk*$z_gdImW>=g?&1V`pn&{^AYDZM<~-j&Z+Cv@F8}6R{V#s*A1ORQ zcZa@zDa)QQxwO#2slwu$6DoF)`(@zz8pwva#5Q+Q_HR~c_>h+8jRk<&7Kb&>p((h6 z7Hn78gHrxGI-pk1ZY+Bhrrl?zaEzV#3@lVps9xDzh;ze#7+Ph=QhPRepk~!a5yotG z`Si?}!2#~t1zfoN5%(=1K)e=_`L?pFnYS!}g$skW_=0EyNKA=oN8RM1Njl%&Z|`a7 znf4?2(V4atWZVBzH@)zDa4j$%w**DhL;D3#e$X&r76HuP_l~r6_jfvn0HGL=agAfO zs4`v9s+of32C8trrRvh(5_A4HIp?28cKJ6l?`CC8t;VDT7kY-2!_K}AmeoErc0)h? zB_xU_NZDD^A^S<~ZRVQ3!nhZ&n%Y57pZk?z-|EZ?Zl}xbPft6uGTv&RzKCnHSrQH8 z*^!UpNREn3kf0(s*mvMhrnM13m!$48L36w0W}9cC*_t5&*!hF*ng$D@soh4OlHRSa zAKP-K9vEMoZ*xXFV9LV!ZNDYnc&HQ>D7xHmtf0tX&ux`Mo#7u^u*SghAJn6b;iwC! z_a&afkp^6{ea~Lzw5Y~Aho)@Xp=T{}AWaK6P?cHK2KdHj5bEVwh@Hmw7XS(=2#Fx7 zsJV(fw9bs`D?w9eQ3I~u|Fu)9@>HjT2!Bz89i>D!%kI)%$cV`-YxfoiyX)4gdD-5> zBSP)gKM|xo|6XMLr+xlM6QX@@VE!GSH$@C%AZvqH|A&_OM+J|U$-)y8nQc@_X!#f2q~>C$|!0{8)y%{YPTuN&#|dagU zp}?|S#)Z+2Qe?X+Oddtr{!Op%R!cqI^PX1Qy1}^-pEZ5pY-z}g!l(shM?0Ze_fgaO zswv}koRFuBScZzGS=xjT+x#FaZL6lmgbi{x#L-r)-QY~)W3cv)e(@j^?$fI|l!e4( zUWmPV%h(#Yd{kQ;_PAC+y6jB8mZIU&e~{C=>EGzOe@F-Zf8$kp;6du}VXst3QrPry zouI7Bht!=7&yqU#yp&^tm_B#tz>&oAh!= z{_OR!=nK_4nALk5Y5hpa#fgCY!l7x;U^; zh1|mSo&>4ulVe;e%q#lS&noX)ReQ3R+p&oyes`0`c8fKfmw2W+*~sb!p3Vh-DJM)z<5L1FQwD(zQg9iq4LI_<^^ zO9N)zQ%tkdru4Gr&;d}03zW<7;EMECx4<)OqZfAS^vTuc;=+-<6e*yZ?;=03W76`> zg?s$#25L9bdH_0C8^;;rDm|@zhmcU+rpQ(TlJ@^K>i!@8+rOfz_KU{LHMn6PyjKn3 zwH0u!eiQ^Q(I@o(wX-XK6@)NUoI%HDmV#s#J_ zg4#ACzo#=KwZK-(03+n`C&tN=(phy$7pMGgI$^>$9)th1pvJGw_g^H~@xOI}Heqrm z$;gV-h8tPEwXW6zRmUDc1C~Kd}M?uok{_VWa0c?^{VGhbgXy87+u$nOx zr>zb($`9)uliAKCy`s~Kb~Fo-X-m(SPE)UP<_8P|SLnI;|9d6g`y*i5|CW5&e|L^B zq-KDvjbfZC8Cr5XirH0Zl8Di>f7w08+JVqzf-8AD5EX{D=YA%HE!N2=C0X&h6D9&Y zWkNApwe97Vc*ReEa`|4p(hj~OfeZLR)Ml(C{Ji#pdua2v z@yL|Dx$-87*y&Qcxu+P*=Bu)lV@v$1m={*gEDd4vZ})qiJ#1RB`k3JwVwUE%g0sI&n2iL)D*mkLC8?G(*# zi}bQ+{F(J3L2Z8u03#g-V5Djyr{EQ#D@OnaL_197-hZf5@NeS`{14Gh#%G|CTPnr9 zSPhl(1=w3H&y$!NQGHwR8wwb#ch)e5$)@r^7mp@Dz4f||veXv?b1RU_knO#`mB`)8DW??zAT`*QdjdFJQp$p}{=)sp4fVnF$s~-V^88T% zfa~jbT*e9Kkebv@xtx9qf}~&k9|ro1p3cAUIsfVrwBG_Q+Hv9fMTQ4&x0fpbYRv`_ z1Rwz5=gp-P0HF8P)A3uHX+~?igoe&t95_}F_#Y|D-7ZYyeP|E(rc>`;>+dTyRqC*PBi=N(GZ zb6F4;4CtvtQ`nh2xtLhBjH7?c;Q!T+{Uf8Ie$6}ndwyqQYbux0U%cqNUu{pI-iT}c zE4hG-+bE&NL3Pc^Vh6`%4~-NcMPRUKn_Ut@NTHB0dDMaC55 zW7tmOiJ{2HBe9lV&udcClb8pp@uydBP!wiJFw>K2Mpq{?F3OpED=NAoGXesNBZyRasF?t*|aR{NTZsEg+?ja zF#Rw9iOTtmYQcK9fINfgc=&{k8S{R#p5VM*?puGV&O2G0AnO2 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_U1CXBVYIWS_medium.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_U1CXBVYIWS_medium.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..86c36082c9a38d8633d3c6bd49d9dc81873df427 GIT binary patch literal 47416 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

&Q4^+n=WlZ+B5cJ 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_U1CXBVYIWS_thumbnail.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058729_U1CXBVYIWS_thumbnail.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..c0cd734e588d951acfe6176b638d67315038134a GIT binary patch literal 13272 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058964_HVHBI7CM27.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058964_HVHBI7CM27.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..9243190e1916cf45fd931f046a75ab536a810834 GIT binary patch literal 63842 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058964_HVHBI7CM27_medium.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058964_HVHBI7CM27_medium.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..86c36082c9a38d8633d3c6bd49d9dc81873df427 GIT binary patch literal 47416 zcmeEu2|Sel+V=66Rk*NJ31OOeKVD*|(WemJlHnG1+&MJz2)Sgk;~B zvF~QA!)))}d7k%q&pH3+InQ~Yb55W4Iq!qp$NbE4U-$3&UEk~by{_L)9i~o$PHJmt zX@F>GXh08uZxEFNx~gWNe(kQFfznx<#~1CKt!&PENL{*oR>a-a*3ssymbT_u?K|q4 zXFcSlMJ3L*jq{lr69@tV(a-~?{o`*yQKfQM!Z5loC2ua5!=#Cf( zf*zxS#vG|2B6RN_A!w}pgBukjh^$M}+(kDr9o{NHZ0L33r8cK@+C)-A_w;0e-=Kfy zgAxu{M-W)O9xC$b$S*p;AE=<|)Y14oc=PBV$5KL2MvplN`0wZ{&B z{L0lc?L-iFM9+JH>wF$K+Vp3A5no|D`-uvgUBF0-Z&E=7agVYELTjDUou>y}3fzJ| zPEjx_=rQ`a`08h-VJ|A^zFtyu)!;*Q4w!cvhrT-KpZP#)0M_}j?xv`7+@bkJ2g4ka z#K~<==_RM1FJg(xvgN`@E!``jf-Z34$-ay06KE=^L5*lkIJ`5KR$dv}q-4Hch`Ecj)CLi0gLrw>hn z=_2LaYCG16#w+sPvm?W{L)^-T=}+qr?|Q2~CzfYnxm(^KDQuBp$x;IqPTW3aou87Y z+lI;F;aNK3-Qw*6T%JN7A`SEmbrF2Xx ze+3sUlyKSk-zvB{QuaySyD4UQ*6rMgBpDw=c4>Jjoj#%coiX!=1xna|~O_@6z zsw-{V4b#&A<0K3_A~j;9d_g{Z)~j^B{u?qJN5qp=OXryj9pjFRM4xmy^Nt0&8ed?_6}K@;OjXxR1kjAZf9$08-{u8 zJZoiVyy4dfocaJiNMD`fpZ%aApVC+7#Ayx&a_Ij?{|k;|ovb=iG{3bAmmz3KPw+1I z#bz}3DlsgBlH(%#mP1k&$_8H`gclu+%(Je~E%eTK->p`FxILb75)uc#v!O1K}j zM&H);Jup`lB}EEAB!kWaHf-|W{Ga*y`_Can2wAUN{X|)gDA%1Ku#URlf3}o)JhdWW z>JUU)-D0byf`IT>N|;(#mBhhbrNxNb+FVGJe%X`m{~FaGPgJB0pZprXeB?FT^w~?HK4GEeS^e3*roeTC5CnX_{=$A;^WbuVEr2FOTQViV6sTCTa2 z_on0H%Bc5qaUK$S3wDA2U5Z=RG>f)M;$=Tp0X7-`&r17yM@qj|0K7Qy`=oQwk_(mg zyor}~+f(}lKwCzMZYaV^6k(_@_87bvu^``LxhGS zr;Wy5X03}i`%z{S*;Ta-Z0Z2)^?$Jq0P!#59sgb`@THc{YsSy9Eh3D zgR)_Y5IkP4^>JG>vnJIFFedQLhrBi`YtYRYcs%D$1&Kp^#)%~1G^%GFFzF<7`$v@Q z7tob6Z>NtXvkT}a7YS>uI)`uyR^L)L?MR}(F&fM|p#}_S{KDya?H9;|sZF5WLG$?kG z3gU+@X+sFoK&+U3hit5&C?kKogW|00si0PIaK1jpeLqdl5_SL|_ghr=KTQRl*eBqr zpzC)4!(suUhPO~=CTpz#uP}`cP90Kyi0#R+jlNyJsxWpgUi#$CQ<}n`RB~f|3_J{A zyX+bI;$JXdw(LYw%VmC-}GhHTGdp>&Dcomihtl6 zo-1LG)|<81Fwx#Hf~U+|=05oplARiQNZa}sr{oYrVT`4Mgs<6BK@So!b7fnB{%|UY z?i&>p^Qn{y`m8&(Wjf!CJc0-!AZJ?p$$o%rHKUiqL>QwmB?98!K_+t85klq`@{fU^ zLDm~0=cZ)4i7bs&Q0iP^r!XG7;`#{rQc`Wp1JK4xRUiC!4^Y1*2#M9EWt%N4`FHJ( z=dl!&>dF=?sH>kAq#St;y$T=+h8y8@oOGoarOXGrqpqTqFNVbLk@B`vYruOi2q`Z3 z$XRfxGE7D)bPrGI3`fi{c{E}6+aMA4-Qw$)`)l5a)E$HlsAOmYx}A7>(_fks@fAQ@ zzW^g8(PxnZfErcw&^OLqB#B*|ayy`pQm=q)w~yx}F&>`5QbA!5!`&@lnqRA?i_ba{ z#GFWp3qV=r;TPnNdF-C9p97Fh>H?1C*9Tow08_%Et1hEjgLArGQ;yew+ed+-!jFUJ z|IL-eUnA@u=uw!i;NSYm5SU$3P4(vOD59fgNuGCaG>5&FO8X7x(Hok_awzty1?sDDYXzn(JmAYyelDQ z<$A6B&q^8<)Lv_g+B*|W1*x~g9}l%m;qFcyD0InC8l9=2rAqMb3b&`b>z&w1?)W53u(H(SA$Cl+N=LxC@<>JLn-kBw`*%1pggCo|KlMKI z(ByuYHDOord1S;)$J0}WF5=k9sw)2HH0^S`t@V+F3-L{QA$P`BR7H}(^tJ||Nu%Bm zlBf03)dH!Y7bpGmHlZU`d#X6t+Gd8@#e#gF)=(2E*Mf-)=E*a1S6I<~G2;pQp$BUk z)?@Q&`^jCrOr`_G$aj7WK=AA*s9+V-{0;i<;B}|#ZRMC#qZUUM_rm?P(2sgB*2q95 zx`z_A5HEkwrcl65r2Nqu>Ci19i+qZx%#Sx>E~6qda|ChR;gdK895mQe4tsam`GLQx z>AashQGmpczu{Pc0qD;bWMpuQKKQdD_@9zc(_QW)1${B+7{@RE&0 zFIfm@wyo9YaEEe?;&Mb(vPYaoB2Z&&<^>FM>X>*zBt1CGh< zc=_FuVy*UE^NP*B9ab6&P)GMu^!|OsOUQa?^+o*b^g{eBpI+0~!X$4`iJr2We3y53 zBJMtjN*=(KA=&_EyMmb;@Vi0Bjc_2saR8M@Ne)Ucv|KpC z`jvbnR>wEnc+(_78b;Q(I?wCm^us1$vwrr%qOhr{;Il{0J^I@3+$(Y2LCT`}KtB0W z>xWxM0o>r@dc^2=0fCC?tG+{{NCwPwT(TIR!x>H>n!Ic@M!zJZY>32sWGP~X;sEz2 zag$MgoH*{^bQK!D-tgEejx2?DY>$sCJlpUl9R=;6Z|0ys+%`4nK?oVtwJ7xupcK8% zP#AY{uzD%YszyEZINI!uH8xWMIdKLc7<}kE6$EBn_pNeq$n$CiUJ+?e+qWNSHjJwd zXDG6S4}NEGku6GN^4*`8VKPU73!~w3I8)tiZ=9Y#H!`udTyM*PI7KoXxaf|4oh)8k zR1wtMGJCf&dUEnugP_Z4vq!lRAm$g%&-#}iOSf&bN;DN6COG(iHP(Vi05zm?KcVRf z*H)j2+DPoW*y_FhMa78G-t!y&ge8@3&Lva{a&2e^KC0Ftp0Tv#Ae;BBAlt6@oRrx) zC}BIqQ~QOs=i{4PL}K2`6g{O5O0RsHjRp%} zuk3eKEpqNF@rMPH;%Y-vYdtchYhd{WOTL#I4T9RH*Hn*?LJhw2b~A(9i&k+;!M zw#v*E2nfMrS5$o8i@C8l&z%Z-{a8$IhFfk*-+Z#ycOr31vuHc`4Q3hzr&qS&Nc59F zWNAEcN!B;$RSp&8Hjl)Q9*EB>kl3c0RZmxZ>Gz|}SD&%JS?{#AS~NE?OODy+1)N*% z@U^CzgCmI4DD9AaXGVj@!$^-Zx#;8I-sr+odrQs%!6lI^u@vE1qujIAm0F z&j=T}eg->ouD_s956>YLWmV9CstGKr^Rx8v$stL>BjDV%Y+k|Bs0b&L$Aqocp?*Gya~eq{Wf}}pK^|eIwwq8|uwxA!b zR@LO>o|W=TiwFqBAf|Mpr`e2=DHPv>dd_LD(~EYmB0P0l)!IbcJcu$IYk3^i{Yk$a zody)f9&Cp2pjwm6u2AByCQxp_Q?chqey0p3D%(n3 zihLA5x_y>gX<~FE*)YOm?~Q*vzRTAL-=E$5Y-MD@py77f&<#IUoF#=ZYIcgZ`DofK zbDQ#+%r7i^whW0rjpX{RtQpsJIGn&tVeG=$*GJtLN>Cqqy~!PiGOiTT+Iu6P4Y^~# z!X&=9GHX9TG`7$z(qq;}=^Q5eW>UJL$KBr3$=8B#Yc4OIpV58Yz3@%L3RBmw=4@2E=?wNF3m++&Y;7nAe_!74Iy{X7fm~9PS62nAi(7K z-c$c$SKJQ+5I-j%Y&tlsmfkC62g+sp#zty^`Tf=pbM$_i-!rKGIBe71d$Sc9V8Kt= zoj$Y4V!!uo@)1{QHSK;}%!BnPy`mK*)_SxK%lo^T>-rN2VV0-77tk!H2^ZDf;T*NR zc2kUg{`mwwvyfI~=J!X|EnMm=#gasc%bJ6gribtPV|%7c`$~Wm>-~S4_x&e%A$X|) zAOHcXrGJT2{H_1PcaILu@*?R}=?P&LCfDs^t`90b5sq;^AA21-F^`#+BRIGy#~ZfG zet#UNacQGf==BxBp%GvX3^8w}aEX3KWjJ}1Dp|5oobr}~g!Y)`I$SrG>@V{(fy17s zt>w6?jBFFWe%zHP|5-XpclnRXE&n$Dd&+!p{m&ERXET@=P&Gd&-f{&|3sevB!u^52 zXGFE_XaEXwFk4OuHsl~6p!7V0S+f&xyH|>!_3znfyYJ`y z0I%r!y#WG434!}ebDp1m|9YcRmF+0(J;w9J!Z+yU=TuM)Jhg25N`%mN#J8&#$$GBz z>Kdv&8$TzQWA{ktcRVS>A7g%ykMrN>VO$9h77Dj02=ezLy|lk`!>mF`doyTkGd{G=NM|%`jMz z^8&5ws`x>TQ`@l}W!`f$pHEGc{$`s%%|&Ovhj3oR)Ksx9rWwgNPMB>p)=s|M`%qH( zPUeXe&`Q=o|KpeAM^wbue6G{bvA*@?8I3j3PxyE%*YM4~FKra}586d^44&<9Dw;2DhCaxTDy@9j4vWalL1atjO7=Imzu zEVLSf!~A@)7@84g46A({&&A5`{Cn6(6FcpTxJ*F((;YK*f_0&AcQ!eZ4N^o2uVgv@ z7`ta<%gf&xC&Ws?#Ce{ARZImHR5x+d`gOH+MXzW*m%QsO=;o&{WcxYD*z8^vPO~)< zMDV?ayT3az6oVLo*}FXbF)em7$!Du|3$4ij0avPH|*eNbh=>SrS=! zL}BwDvU=7LIV`vHK4G#U24xW(d_BHA;q~rRJ@};(xDFLO>KRxna#!=n(aNu4bn%9e z8iC|FRNDiXKd^?L#l$>|TkK0u%?}6_Z=r&0BS$?OktMKKF?R-NFoTi`7f=~DhP{Wu z)vbg(p3vu6$F$SmJt`d5)@DvH<8N0-$daxB==_TG7~hg2TmJRX%epOQe$ zRM|od@zxUTvfk4K%`mrJ7VoS)wuoTbu8?Gp#Q?AX9-XqC|^s_49o4Yn|&8s9)GQRdtS0! z&u~pNeKN$%@u@!iBni-b0ovdQB7w>HB_a&XwtfWW6tZVl*a;8dkKAxoD8+yWI*)t@a`k{6Ljut z|BxJ$&8udd{_gCBeK)%a?yd3pvdDP-wzRR$-N%_|?oU9dr9Z}TR02e|tGKG8?u_E3 zC=kS_lf&gCPuahKiqSz2ycAP071Y;+ zEUpOeIBP4xc4mnjs$B`)Z?_|vqVqef7q54x6>Sd>KO6YE0)U23@ZZ{A^p9`f0TP|f zFnKr~g5B$7^H6w3{yk zsWGwrTdGFT4N=nYA{{iTc%sNU|=Fb zi_~S18cMtvneZD+eqek9WMc_XXx(I*we2bA6%S+dtw>kLmWb#Ye5sS-5nT$ z2y|V>8*etFKK0Tqu^i6mg|MWFkUwxNeD`)sA=_{Z{UdpIOHRk#MQFaW?_2YHY)2J; zdQ7yP7%}1QtOgV7l3#|_*XUU&&fbScodxmH zBhJTf?2Fd(w~p3spc|;5k*h7PUf{jgxJ!8)0I2jtjG9AgcrjHE{67y6Q{ckyd+z2= zUX@vLcIVUf@IL}1ClZ^gKZE*5zpHwji0;*yW2r2@*wv-QaR${}!5uCK@1TJQE!YMX zv@Vk({2FRQ1%+^(sCxNiNcx1#w{y-&IdlWlVR)fu`s_&Z303**j0yUGM{b&R|NUTJ zC-Av7$j*Cz4B}s$!r(mB5S(UXWPw)&EKKhu!?))r`^FXaEhFO^I$(wBzMKEcznKT(CuMo)DnshGo5K6 zhi|Ij@qBR(=re?>3ub>ktFo52V)hoLzBpN{IwVKdYZ`s`H3*G#&6(1Ew$E))8Zl*X z+b)oE_g!{hY5~3FqMcEaruSzcq+3ei8ur-*^>e)W0&=t41#$EUB8eX-2t8iQ?Pb}3 z%gyeSIR9dAN7|tS)%;qH5R7BKuE$1tij)6velueBUb!(`f8hu+7O^+fy--3px2+Oh z3*`U^1sbLDqWET!#`iT&4lcnR5DXi+u7{6Uodt09q*s8}hn=1Gqqg{%CXb$Ic8JA2 zVi{t={2E_Bo0_yl)XfaOQ;(&i0$9AANYbeK?!ya+Q-JjM6ppnB0<#{M{g__(U0L_E zv9^>4lqnlM0Vh~NU!rzyk)(jBX!|Mo{psTx__`_bTQ`)bitLPR(;G3IxkUvr)v9`_ znMW9MR#xliq$Km4zTL{O-90V7uTfnrIbu0YVoQ%A$>F};d?j7HToiC@$p@C>h88~^ z5_CaZ-&p0i-03@E;YrQFMeM!`4na=mP9D8)hGhsQ>GOJ{ZuU1Z(wYET=Bs=eco0P9DP|^Wphul^JGfLDwl(XqTFMR& zoGOL4Msky!@CzyKGxyaq%1bNb#IGK!HS>LZ{5PiBFT6!_LmPpuB;O9oGR26r=aom% zcwyhC5Pal2ORd=F>@MjTA>9`jz>qxO@$<%uNv~x z%<0avtGH#V6Bg8dNb`ec_yj#-)6WvP99pw_MB4kn<%mPqnI1~yi0keuKdrB71}K^Z zehzY-QjcMEq(CUWwOltdtcnM%C>>Sd{Ctm3nUf*5=utb;Y4XS6tk$VuD=h{hq9Y2} zFA|6G&RcN3nPDLLRi%^Gk;OX@crVcvel_C#=SXYlYwtyhU_9iI1#ud-;k51v)I!_w zl9alC9qO}!H^%2+7a+WUh)GYj0NV67ZD#t1GwW?CC=$xGrh3dzY^WNJxJwS@HEU=k zyRC1vyj*EQoJKc*SqTBeW*`?X&Xr5CNe)_~;a&yp7=Nbf)|ECCsT+QN|YdjakJ(mMt|#_f zgBL?_x1A2;;3tqnT|<;Na)gQB4Q1MpI9Dl;A3#IN;LPmQ&;!dUCemGU4Rmf*8DP!# zQvens3NLd9IwpLO?So#clqxl#$-toIBbw0Nx2yRflESmAiUiJgx%=2pg=sf({SRZ~_U~uZSvO2Xcx=DkwDV&pvML8|B*W ziiOQG!}^mHj5Q|Y({s%|^(@+#wSE+}K~FS$Jhf-{`ILnQPf<@eHlYXa%=LDH4~!yhANDJt_og#%D{ zD89-@RFHrMAXLL+kW1E>l&g`1{djU8Deqtn4J`s@pt&}D7Vq-mg>&g@8E)~Q;2{)y@1TLQ;gqT|E zD?bT={@8m)&e(zga@upBMLv8aMN9Y%bd3cF5=jc;3Fk@5h#l(b$fD+%`Bu^#yR6 zrQSg9{5N&g{zJP}Ip?Ozz%8qK&yfseXtuT$_wzwxy3doXDxI&pEMdHeOuNy)(ZxK! zIy4JN6yke9R8U|$-eHID(qb1-2Q^uLAcD|ZRNS`OpL5{vNZuWLm9oB)zGwFFOG528 z?v?KNG6#cI^8rb@Gs_7xdmA=il5MWocmO@Dzkyrfl2tOZ zkcq=cd%M`1eb=QEDtVB46i$0Hm`A9$3vKVy8uEkp;7r^Ic*v&ffE9kHM2xq>6JuHL zC00>%V5sf`Zru!$Ch-v(XCfJuq?@xQK37~sB^;%hxuSy5Fx_6+NfQ!GFSlX7eK8X; zf@nf=3J?m2N`y!28Eh*&cLlVn>E>U(&CM2GNd(VYY{TCI!nq!J^a2!!^c?WUgs!%i zjTVC9% zO80B)ysY3iXrx{c<%H%Mzs>$4q@+&nX@Ym9}H64Jn8Wh63%v9J77Zd{_*~ ztW&iwyPKTh&XasYYTghE69g1o@$E}B!h5d?t={;tnZe4Lj)Hdjrevdc$6e>f(5&eB z_WcXhqqTj^n!^Tne0NnXSq(?~pPBu&M0SY(4`^(2o0fq7acBoPe4Gjn-K;m(N4Z_n z3f~=tklA91FnB1cB8!w{6M}TZx>fUPH9Y}D`-H;pH6_6^1{KjXU7TbCY2|b zYR^A#;MZtq_EH3pj}{`?f6@CxAh+?G!BxX52G~e{$p|VBSoVHVJJ;av;W&Ndh?@Qn zWg~Aqv{7EqHDyImHII(Tq(Hb;`Yprw)8omls%3}1`r~HJo^)^Zcyy14>d!{0iPQF< z^a)*V9E#)j!(T4YGL967CWVUcG0dPz>|t2FCbNSw7I|j$v8hg-n{E}I60MkbBeY9j zIOhORqKoWP?TIBsdYq*!_YQe3E?DlM(<3aeYp)OEg#z-ELl+k&loXR~7WF`(;=zR?e}p=^>|>U(ZnG7AL@gdKT!Bzd;zOpt(4&tCYGy z%ev2-GYp6QgmZx;N%yVpgTMyY4rHipO0jI!vsqjHg>8i9{yykJ@Ww9 z{F^pM{sVV{84T_wN}gES|C*WR99PceT+_7O=A0cW-9$O^rVc1&Y>`u8(>4vK*Gk8t zAfbN0p{*XBB;is{MLRisnuZvth~rZx(mkw&K165<3nuJhM#E;yHrE;Ou10vl{N;=^ z|HNBmmQBX14fvHQFJi)$67e`6_~Gqt&&uuh3AKZsW@h871Hl`hA*yhv-RiNGPo33bDht2|;DVR`lJL?zAGgv`+3e4p#5m?ggrc`-T1uc3?v#}oxpI1i(UM-ijtlPbMW zfv&?z@eF^&Hn-Vv}k=+}Y0!H&03}Yn7tvZOAkG65qAy}32XD6rry7Lk0C&Gn_#V^ZG2*8lltbr~MQ6 zVm+54x!J=O&EM_)BmpHIOynqlFu5Yza|y1LT1U)|ExKjp_hSMw6efU&$n?}9IS(Pa zKy7EMwU+bGsVv5B6>qq=1CWD!{1Dj#Aa^%1e6PUqsH~Mp83;*k1&Ct6CF)B)`E$R> z-MAs?k#TipHd&b_>}=GD)Ax7-z>ioo)j^1AL?f!44+RuNkB~G zE<*+tb#pDpOyVg7AlfIqBQRswWzrI!!M0?)gl=THgy*;5Nmf=bdk$xIl<=n z^dJHNlLA#Y@!yp?;oaol;afu&;*?*vnIs|I-3`b#unSo6put0WH;n_$=}ZpwV?l2R3~XLH>7SQRO@=stxZ74m}5W(N}NbRK$18 zmIVdqz8gjWcc;?KKx_|9x2~;HK|-6@DRS@{X2%D%sp2b!4Y3J=O!QeU34K5XcCT4vRNbJ&_;OOZNc~_wYq^H zp2{^hI)^fRBDN0mkmT?-q3)7ryy6Xp%EIES`*#^9XJ%}aON!b_DtW^lIMUW$@d&~u zF}$DyVqIaa3vAcc_5$er{~fWnh{7TFg7N!xRST#Fo}IzVH^(xH-!eL6P(eQOv#PTU zlhu=Dqk|Xf&dQh*69-840!#5rSxcDL$mxQx`C)Nck#;*gw$G@Y8S2sRCFG?uJ?sqyN@@c!F*D(JlULa6w?2TO(nn!D%kC6r96 zC+Qeg4N7L#V7?~oZCFnT?w`0nei)~mP@v8^?D_{a*o~i=@M1PxTyrt2PDMI+KkV#3((jJrFX`$?TkYCG+iK@* z*K+|NB6g<%6b*~vM=rVPkvK<@_*^^$LLizGP%}U%U?W81;HwIqJTucbqBhT6kBt3| z_PG+%Vs>7jnA-%FqxOb&`a;2lVF1W-X#U8189Z?vv#1EliHXzI=Vr_%cx4>D1pI$g z0=K@C|AzWjn~l>XaoV&h-O}QIT`iMgU*~L7Ze8}4Li>yoP|KTIx$w)u)`yxOun zo3n$gS99^~kIx6)GXv9ijd2Zkvhp8UNvNA_p4-+HYC=mC_THQ;x%x3qrCs)$NMXsx zpMgw#NmpQxK)|Nh8k;)AhqIlzs%S8IiV7-mFta-!s2{_B8^mY7_*G2!Hm9oW2bYg2 zxGIPy71Y>>PzLI2ODKXkz;|cwBL8f_XgiQIejpoIIm9T$D0&-OeRu^kor2`&(?;=} zqk90PQ-yt0&>PtIAx{JNeO*@>pjXhAuPD6eitx4^Cuh271FD`QfvmCFPfzIFC*_CJBa-N8hPeFF1bV1R5f|2!9??Khksmm9KLIXc4U z!GXhvPzwJU{30dz2CzczXHh{sfSCNJ1L%(;`IG|91Mcy#y#eG0^5PHE?)6nXrin*= z;{$SLC*j)`cSSOEZOmvqJmcPD&8W9}+6Q|;L@*45z7Vt#{f1-*GCt17394uCfZM;S zzLsV0V?JfwUrK!SwLI~OL$ttA!+>LoXPwo3_eshMv)$wqKeg~GJ&GJ$QQzcytWv^v z(RiMi;D$AS2q2EL5P(9KS}Jn-4;M-QcO$Ux9L7lFG*Z}xI?)c(N!gwtJtR8&S>YE0 zccEZ=gt$(ZfWeD`!cRQz#okdBbdLAxfu`UFk<_w4MML9&qK{dktHMB8M6CUteSCCQQv(5DIoaMvHpA48RXR`KAS_1p78z(2w6#S(kqiY?#M?lG30Vr?8-4oV zZ3!=SQDC5`#RsIay2vAfF3!3wo0`HGwJnqQ@nbp=#h(2CaKDcKsFms;&?05*8B(p5 z#2aVLBH30A z9zOBgZ4=7FE1LVd7e^%yn;FaK!S*ds&NReP+q=RW%2f(^lb8W~h7a%bEWfSn1NZJlS&WY%iv-PlRW zxPeX$g&v9WWW^BLgV3cX=w`bG#czFkF?korCgc}ESW*_F)}6G#fJlNNaSVo(Y4-CZ zs*ZLzT$~C%osx9T(nf#{y_UWO9<1h?nKaXy3wkmtYWb)z;_}^yPebFe+e7Qz*n6d9 z<8IT6-vYL!Izh8uOGs8y-p&c0?mXlC&-Zd%;aDesCDlc2@_FamV8&N*PH~Mfv4vg* z`4fflI`V~aaYLE4R{(yzytwW4m+$BC|NK4v{(cB%vwBEL!a8v6!v+dxKKDJHu`Rr0 z{Z!(~Q7t(S@7b~dt<0!Lp>mft7MVUh(^3gU=prWJ<>shUyC3}6vErF}^usztVn{oK z{s*no`{5pZ-Kwzz!HpfPszw!t$z@g=3cep1)?`z;s zOY>rV7pa?CD~L{|(D_{tHtao*BhRHs7HFIuM-H|wsQ?=%Ud)jAv!mC4ut*%dr3!l#9fMMx5s{Tyb@$E1|}KS}+jhj-mpm?b?&f5!Up z-O_l-ljHk&023EN%4-TA4Wd1t7Z^jHx7QFaC`?4WMqK2*2&fQWAT8Jao5T3u2Zq@| zf*)zdd*FP?dQH{K1R{2^aacBz$(FnP&^Y%Ef5yzsqw^JNt0*PDP-j{mG2{g-tzQJkJoe!GOUtU=0nVK5!!j&1C? zmkgF4J^MzejQRAs<*S>PO`X@;T0g0lb-Zr+(a^W|{!J|qAkZG!1MR%sG!nsry^rgKF5?F5P;YZP=oIl0zlJI z9+*g&BI!c@^r4@wsh^Cg-ZBJQVpy}yFAr02--py0(XOVchdIF{)9&vmSeyheSJxSlZ^hRv` zazCtho={y5UmHHHsrwWov)xIQm^r2J-sbLNUWIJcj)g^-8UTHnIOuaZFvNV~+CvFe zm9GqzS5F=j=6%4kYY)o`&RiLYO|7u$oW^;D`+H$Oe|G&?uH)Ii@C0oP=$_B;5EA(w zatct?X8;lKPafO1Q6@T|izK}qD#+kV80C4^KC)gpGj!L9U?a&&1$7SulSxjL3zcwQ zl0tv=k@Da>ag*@UIL0|C#TJ>5FILTdbbVG#2#w{*HSj+n;l#XX;?DTU#mnS6>qJ>< zRmH+HH=_ato$A3$(WBo+&2q-4HQD0iRuE^%NpcR?*zTKdsmOs<4ZzZRi!V%PWq2>p zz}fXRJJ920SGEcvT{4x==686BHPO2zw2#tZaak)_pfjIPvP`_2w>qSwUxU^vc?kO^=5e0hd}SseR4vbhQEN3 zQ1DI17&Pe^GSFM$b@iEiFO9|a{RFlqzp|Bd;zjs)p}!-n!F$q$Q< zbg@$<7RAU^n~oh=?-0DCBFGx@HExS`MUaKm?D{opt-Db^slavQ(nDZ>!V5}lk51^b zuMyuJRdXouiY}@=E*JPLWYeIyc0{@IrO@?iHJocRKc8{b8p|B>pqbjjaepOBY_pQ= zQ=@lzCyoiw!FumB-)YuNe$^izQ+cp)^?&sS7Oll6NEAw4UL-0&)yWoKTwkJfI+ z0`x)ergy%MuDX8vD;8xU(Nh8lNO%?Su6PJ$i3tgGeSLdU*b1y*<}y7RQ?qe+m&5tn z^BmWkKE6nPM(;APg2i(b-5oGxTL@c;{LuP9%EZAlsKwAZp{#G}{vBZNl<^xJx5eO2 zqRMsVM4_M+&JVOzkB^={Jv&*_Z0Sr8$$4}k73G?C>6_F+^}HU9R=*3()=e{^#{vNN zi)696@q>38kw_hk%iqHX*8%-k1@uC*6UpmfpsFgpF{swckdn7)R(P|V?|LxL_ld# zu_FR1y+lwzKtw=5YQBJU5kX3TkSHifjdVpM0wN+pl-?sn0TJm$x*_zM1On;4o4I%H z?^|Z>o!>ii-#c^v;6u(1hn$?V_gc?-p0(Dtig9)c`zW&(6(v))ykzy3!SN*n>!CpDLP;gpNgIBcZs^zd&qC`&-0f_U(|IIi9G~;E{k`{i z&hA`Tk6v}3pRBY3LHZSX=RZx5o1Zu9AJmUgkIVGfvzXifWS@$QlnmvcD^ot-XVZEelbC;O)>n%ebaA71K)wReV4;siVSdPsINW9vvFf2zv>t6Uvf+v zB=D%+Rte=hg3Hn;VFxd*K}kl72Vd8euMw}1&$va0+-c`Z;!i&l#<7x7=hJv-Q5ybL zOntDbaQYc+d(N_H7v^!2MLr^!omS()RGlAlAWMhO@3e_FG#{EOtCVOTzzrzLPs*># zB{^4SEulq>z5O@HE3N-i5`lgY3;)9iGP(&xIbAS=yMWyG5kfKjg$;7A zUR_~q2h-;xdg%~Wjg~`NnjYVtnbnXG9jvrFR;@W<)6jixpFr%TtZLb(Z!1oJsTFpU z8k%|+X!+VQ;+t$yy+hyZ7reDyyDDN48y`vV+CHnn`@z~%q0d2yJ|%hXN{D6tohT`u zQWvW`IeH3X=Kcgy@7<*AOMKf7=w41J0zrkj?nN_0Q@!H;g;ynY<#BVPn&ENUE&0A? zIwoNz(Z!c<R^3$N7g_-+!E2ko*Mj1(D@D*R=dM@Z=F+#miwsFdwc;;F#yjlL{(iSI|bN~CG@9@o3C))%II5$?$Y3HdVeV& z9tAn4L(lr)O?k5b>t!PST3I_tw~$B4=f_5Lju!WP40)&I#y7$}8;as#+`-9@$53*1 zhUo5I;PlL6wHcVsQBl$zh^P_MMK0okm#g-|Voln7VWekvNcg4Q{^T*q)-Zz|*L z>pV+UO;BjS9SI2F2trJ4pXxu2^2?^0_YM_(n3yV&l_&WLIUV?tVA%G|HDQDvRA7`? z<3-rX;>Q}iwlNg1zb<|&hHBa)`C;uHPj$h(unH&N?~QF`!Z@Mr11Ide?^`prXoQ)g z{^{bHg+e?sm$Y5wrrY>i18!jv!@3(IgR!gCtLjo~Az*SxK-fWTY^m8fdc<#!Spm@? zOYj!10jGengqwnbav-&06IfZ`D}|UC13#P>Qhui=V9pCUY@VQr%?M2OO zu2aH(;7FiaA_&V2QrJ(fi-Hs0xy(`s8QQ|Y?SM|$NB!j5{FAFYE*xBXOUK-G?28h< zuvFxEvNJXWY1ei|xX1p(O8ZLr$l5wK%Z-aic02b=tmCFMi#A7N`_-gwi^bU~#OuID zxW=Kp2>7DCUU^%$;Sfq%Va~->L#zjJ-k~hb^OgSx-fHeMA$oI$ZpPYyrs;3>zTH`* zy7U-l8}S*MWD6cB*r(qb1d``Y@6oF45T%J;BY#d>E02GazGY(g!OG{6%2ldhSC_SM zfhnJ{*{j84#maJU{R4CR8XbPg*(r5l&E>NdCaly@v8l*aXJ8U%sc>57JgyV%pp3mu z3nA_6xq1bQ&=R67hYP)MFj~jepo|KF%GV4rYw^7r?T{e^PO4*Q#q*nYS~<-AsrNN0 zVVw(k!D+$mcgC#!VUctlZNh8o8|1Fog zQ)N__g;OWdvsk zO!d#)rZIUOM+%k?JEMjb7i-?RTWe%_qJNr%-gm3G<-Gp)ovLrDKdndiL92DAo&tw5 zhdH;ge*d1If8_2>+jXPWj%CxPFIIQSFRJXZ(681?w6=LYY0u`Xn6Q`%STq9AezdQK zf30I5>%3)Y?alxLg{XnxYJ2=TYNaoTydF`Dd}VBVLrze3i5%oFo3A67{0gOFs(GxW zV}DnPN#^O9AK@mzabLQM{k}XHvBpP2H$T1O_d?vB)Ev`tMkdo}ctp|WYFHk0OpNV8 z$sWhoqlFRs$QPCWd6yx?v7sR1g>d1>wsh4k87z})W8tuvL$MM@h8_4Y})URqsN!&LV&AjqeAUS#T

&Q4^+n=WlZ+B5cJ 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058964_HVHBI7CM27_thumbnail.jpg" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/1778058964_HVHBI7CM27_thumbnail.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..c0cd734e588d951acfe6176b638d67315038134a GIT binary patch literal 13272 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 literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/awards-certificates/1778057851_IMD365C7C8.pdf" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/awards-certificates/1778057851_IMD365C7C8.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..ecc5b8c487efc114f7f2ff91bb0ec5e5b476dfb5 GIT binary patch literal 31687 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 DTh)VX literal 0 HcmV?d00001 diff --git "a/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/logo/1778057791_250x250.png" "b/public/C:\\www\\api.extranetwork.com\\public\\/property-photos/2026/logo/1778057791_250x250.png" new file mode 100644 index 0000000000000000000000000000000000000000..ba0610a603d99bec4e809893919a5d1abc748da3 GIT binary patch literal 6497 zcmXw8by!qU)2Ej1Tv}2>D*>6BhtI;FcLR$4+NmPS}9kwz)$a9Ln!l%?PG zd%yROd(S!Z%*^?nndi=#nfvagjs^)K10eurs_Brii=8u%h&mL8zTlu$yem`gDVADHo~~dOc`u3dgpW%*O$r;=8R4A1ThP!QsVdgU5sW4$$e9J;P-%a#+@r+LP*b}6 zfR$wQzv)}w53^k4>`75a9CAXB)}L#Dw@P^s3R_-X)==qew6Ga9n00ibJz_hka_o^R zEnrncwD~++Fp@kC&E_MY6s2=ZhbGR7`uvZW=_9X=R)3m+*10gtgBT3gn-1mV6<7Ix zBDS2+G(|B=oUn_`-hOw+o_=cqttveo%v`2vazpumMi`l7Vvr*knNEn7+)%bnL_^eP zL-P;5{!#@dh%rovvQmk9#IC?*wkL}JksQz)r$5_*kO6O9@*u>)68HZnz)KiRQmmk> z^!Ppv&Hi|bi^md+EvF2!fH86^rSc+x5(CYgo}L}ogOxPvK(7CfpGn?V?;lJ;Kre1; zQL;%Fj&hH(QX{t>wRe7$4i*lew=h#5d`p2 z#0i7NPkJIkNAC5zh?RTPl^WxI>{4Z9IT`3*a84~h|5F}og4+hI8*s~}1JE9+_%5bJN3JPx zC`TwcvM?I5>Z=jrywv<4FGv@^?IbiedXu$*)*UtXu>9KT7;1fn=ez>3DsW!_oqIX; zZTcBw6R=Q&g)DWn20qGINF>?-g^3M3UKRJs6NrLSk1E$C4$G5FHT%mTA+U*4(?25$ z|2F-_gRoi*+2POJyWi!E(3|f43_c{S)IK4|DJ|Z~JC1$?jhETx%g8KAVaC(pvs8z! z%=S=}Am(T;{SG7MCm3PQ)3D$0^um&8s$D_5gD+W9%;7Z30MMPegnLBEB{=hqB28XG z$C%n>k=qJ{Ie+&nD_*vhy$>Thr_WMd!4uTD}q z`@_$qK%bT(o5m(0y=o`GybOK1pySTDe94+N0Bz;ZCl0Axbte@VV;2C? zy}B&!KT8SGvu?_{O*OAnURYk<_!xf#TPKRu zVYQL#gTV2gMz;+tnj z*ytP6O5P3b5QD!!Y3}dtpI-E|Q<P`ZvLwyX)9> zOKbPJRicoqq*YzuJDU`?^thX9ySv`zBcuX*CjT8LwCl3F$w_g5Niz<4CRj$~Tkg zDmA;M5vdXlk4OM%^{w74WWff$>&Y=*bGhGZ$amGKqf-JXkn~S=Hy0bu53ovi*Q$2h zu|7ZAUJSVqT5`Oe-epy(_7~`>t<2}alIW(g0Q|_FtK4ssE&4Xm9*8V=CTD&Z^3WKX z<9cH96MSiXx%mUrZ#5{sZ9)VRKsJiL1?D}Z6U-Fdp9UMVJXe>uLij4Ge^Geq)9N}` z&xZRaoZL6hq@lAgyYQWzli=3uoS2371mE6ZaKKAeAc@TAc{}osieq?6%|nUJkPmmY zdnNJrdMH-(J>1vKIGSxrK2f(?@YBT|KxuG3^zmosISC8R=W4y*gp(gmXfC?iTaf3C zoK73@m5fd2)AWnXI^exUh9;^t6*SAJuWYM(Wa!9t&#g8}h_H)Tk6OfSG#E!;==Ztq zEeQkEygBs8r}Dt?m7>gY3!N_j5bbR$RuWB?23d~W!*OUx5c9=(UY1BG%p55L#-*+6 z9k;7jj}A)%XWgdG9rf3Y5@o!4i4Wnah}W^|zdqDB(eK0iF4rE=_z5WMPB5qy+~*`{ zk+gTdkP9xVbbxP_za#Pr`~x24RY7kSx`v|cyplXFXh#F*H|?Y;UklSY`$gmDh)YS> zl|1lx+Z(lmzQk%WZ@T=`-%zPhfapGDxqlb5>?UIEnP+{$pq{t1*5pp4s+OPb*=CFi zV+>##N+9s%!W5dXa^M(L)zEBn+AW-Um^sQUu6YK}{@^lj?QE*pJR6pIPQaVFcIxJ0 z#>3OfE+g7A?jJv3#Jw}LCHOik^w64qzZ#Oqk;v{i{u)~ezhYZ@y0!qk_}v!Dp&+96 z_exMQ-oX1^CZwev7mdP%;eyJWfJKEAL!j^*Svk~oo}5a97yZWq2#C64WxQ{ z)G*&QvJ@k*6)SNK=u;6#^;Xld>BeeNWX_(Szs%~!6BHKRFQ(?@-aXi-WSxWeb_uWz z%iTvGfy^`76si;k;kN=Csc;PIw#AoJfe1nB*&wlW5PL5^rNO4W?64@m0?a zDHa1;6VxBNI;$805__2r4C|XngXZN2apbY{ax;>&-+tQetcJ}Yt|jqMHjGp!uX`9; z<@7tRfX$@-#n>=#@fb6qjOV5iEp92ZqJIWDC6Osx|Li!1S(K=4*p*D_nDjz)q1Ztj zb-fMC@AP7BWgsc?kldn5lv$p4SB%nqxMD}gncS;lc#7TUur)fA`*|4p(@=Wet0{M* zN`{W3IXm(Iy}>f0<+u8MJc%fmL!GzhqX(HI+^Gyvxf2YwcYg-|M1n8#>t%(3?z85HmsStUDvO)S2OId0X3Il6G?X7djEiY(}DvV-`v z^H0QF1E?@tW?yLiGhLF2XQOe}B{H@S2y)2K6`E+3wFLfks2^d}Z)OmGuHK7GfN6(@ zT-B2*D~;srfCuk;&`Ans8>>`oL(a5?t6t6?OiH*s&N42(s zFbBRQ48@SW9*S6#x1M$zle2P$ZX*hk&^bcoxPSZmfuEgjpWPpLdn7xAf>)R6^(eSU z4`hoxih8|hh>Sg8!^I;TKEBO<9VLaFx=*y=I^yj+`MAt~e7ga=wY(;0b9mzADdV6wR{OgsS5drmC_b&H%AV1U_+ z^&i61(Ya8E_Gm|LuGt<5Z>u>ih3I*2gTF@3S1W$@@q|?OR}htDcnuvrZt8AEjzI)! zkebs%Tf+v>a!4F3@LZJ2ncukk&0=}^SFSOq)2N6ZESE)SJuZcf`l$c=ReEY0vLy(e zX?-Ag`f-PPl0qgqdGjv9z_AgMo|R6Ksk9!B6yi5)vHX`?g4pQ5z8^^JxP>62K+cL7 z!)Y}NRPelz5yL(kClI}CN6NE^N!mTC;8W&CxD-eOLN05t%Z0l0@b-b+*#O zef1yHChx{s2gBv);Hxn~*1w;N)4$_bPKU1tlN;>T6C+{)L~>$N=dqBRV|G8znlIAv zIz=NIucsO=7~H}o8(*fKl}7Y66{qjx7fyDdV(feF_S$I#Ey#I{m%zdz{Wlc;gEctz z=X`ISM66R@yt)#iV&~wYEz$f^61%%z98t$hua&oD5Uj2mqZMOjy-@?3F2s%S+J5aP z(y??)h0)jdqn=PrJmgi~_3(L=Ulh2Xbw)aMq`P#2ur|GY{-nd?3G>avk4hyh6R_J3 zw<~5Zcy_YW*MnrTbI!S*_M~VgHXiIDW<=zL-LYeAS8SPdNJ^_C|8fA4ZPDN7gfIGt;?@vLzqE+!K;tjfiW;hY0uuX(<@IE& zyc8CDBtx2Fh=qeex9VEoy7ze*#(v`XI*^uH<3e%D>TAujbFU-Y%-KrRAbA#5OIhVX zVQ4;MTOr*uT zEYkR{f*_;sSRL*K?arP{ZO&TTX49;KnRfhb@}$M%?BlnKUZuPelT%1*-FAs43Rb`V zLx;iNvMr3*5UNC^>%<_GXeWRXddwS5bihzJv#Rn96H**EqANm6EZp_E z;RC%8Lvb@3UZS$d;Lf1N=6$=2mmffFIS?1mz&qkfYvD zHWQoa+wLJldK|Od8=PXx9&yZ%JF2N<8Kij}H>%>S?N+_n^!W>bL>`GV#>ir$(aN{M z*n<(pgLpb~Hk@|ZMf9F7JjIFS7!%4@qhOVjz{8N%H(4j=zw>9db7tYC|0sMnMQ4fQ zJnV7OW~WUc$73e6)lf^dDuj(u_#2LPmipi}I6L=)b2-wk~y)C-R?+#l93Ox%JQQp7)w75$e#?Rej8My)FP zctRL&K4<^L_3@9$VN75lRUH2pWGYCRYs)B$3a^+!P4-h_fP^CLzV~pYdcM}+BEIZ3 zaA%=-YQrespk{=-oAwGTT8BSv5uq%WAw9c3>0_GG&pJ>1OQpoT;VZ<6Ww4KiEm#w(~{&K@XvE28=fkSH<_MePdX+?g(40;kn@_y*f-`*)DRlW6&`w zyJ)!(fZHMvT;#(W(qrm6R~~MRGykhqbK|Gf;p|55itwVnLZgBGv@8e&WGRhI#) z`#c7-?J^6_B7`_Q(Go{VAclUmxhB4LN*U^lBZ--TyghGjWAtK;F@>sYB@Qmi2iG#fVvWjS{*wpp^MlRRw!mruL+#F^523-7Vh zziaF2Om;;#d%t2wM3Z932qaN>68G;fhHv{9+(gAEws>5HomN#A)+g0p6AHx&tcegy zNH_5hN8P2Io%-iv*Rx9GuNWcp!B6`7YK_<^E^LPeygcI(!bKJav>0NQ@Knp+hQNI0 z$a4><3Fy1IE6(#er=jq&`VW5`2!+`$!>K472N%lY<938ClH(oyubBHOs&UgCRktRJ zgx@r~WeC*pG0E6r^w7flx36p0!5KeO(urhVUJUWG#4(-(lGQ{x19i4-w6xi=8%$j?k=$Q_#qn3xY$+EL+d87@3=$tCvi9PwP~ex%xoIA5 z!lo>R{^Y5LNJNcS<=E*v824kAnQpF!rch$R6<$zJ?i1k-LqWu9E0av_K4gpvi1cno z4ZnHXH;#WXm7FgBRZ`IJ)3J-zce`Md0+16kc8{wM`)ra~V8ylau_}}aTI&q_bW_YM z96T$>gaQdd=o-+0_p|75vLciDt;Y}cXp;}!sRQ4 zoGA&-w;560%DC~v`Iln~h!*-C1NbcUp-s&D$dn!~q-rZHR9V zW)&pm?8uI)ZBD{34si|5`-Cs9PxDe1g28u?E|7 z$P>}N5lj^4y5U|Fy?+L50ieD!6C4OlyzgR(c~6`#cJ0S+Yz>@@E!(U7{SX8Sgid@K z$|}e}m0CU_D8PSIY_hq2rQVapXbfNJL7gtq%Ck%+45q~fSDf&rQ9YBw80{haF@YsKixyMcb0^|t$qyXB*)*nE!aD- zhEq}#>HcgRFM9h{!d*lHDTSp}bGNp+#Z~dW1^@ZzTR&YQ-RFYBwuws^xN1*ozzaUk zRkRzbGed%S#r$XJoR*QEqPO7Lw#nFeXT*g=+&V&rH;!cAI0Ut3S#ig z8nfbzkIC1?Yc$l?p%9pZVbqzorG_hA>G}tLtZ&|`l}#*}$j

1w$MWLAdG%i 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

|BF_2p| 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 code { + color: inherit; +} + +kbd { + padding: 0.2rem 0.4rem; + font-size: 87.5%; + color: #fff; + background-color: #212529; + border-radius: 0.2rem; +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700; +} + +pre { + display: block; + font-size: 87.5%; + color: #212529; +} + +pre code { + font-size: inherit; + color: inherit; + word-break: normal; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +.container { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .container { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1140px; + } +} + +.container-fluid { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +.row { + display: flex; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px; +} + +.no-gutters { + margin-right: 0; + margin-left: 0; +} + +.no-gutters > .col, +.no-gutters > [class*="col-"] { + padding-right: 0; + padding-left: 0; +} + +.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, +.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, +.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, +.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, +.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, +.col-xl-auto { + position: relative; + width: 100%; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; +} + +.col-auto { + flex: 0 0 auto; + width: auto; + max-width: none; +} + +.col-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; +} + +.col-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; +} + +.col-3 { + flex: 0 0 25%; + max-width: 25%; +} + +.col-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; +} + +.col-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; +} + +.col-6 { + flex: 0 0 50%; + max-width: 50%; +} + +.col-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; +} + +.col-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; +} + +.col-9 { + flex: 0 0 75%; + max-width: 75%; +} + +.col-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; +} + +.col-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; +} + +.col-12 { + flex: 0 0 100%; + max-width: 100%; +} + +.order-first { + order: -1; +} + +.order-last { + order: 13; +} + +.order-0 { + order: 0; +} + +.order-1 { + order: 1; +} + +.order-2 { + order: 2; +} + +.order-3 { + order: 3; +} + +.order-4 { + order: 4; +} + +.order-5 { + order: 5; +} + +.order-6 { + order: 6; +} + +.order-7 { + order: 7; +} + +.order-8 { + order: 8; +} + +.order-9 { + order: 9; +} + +.order-10 { + order: 10; +} + +.order-11 { + order: 11; +} + +.order-12 { + order: 12; +} + +.offset-1 { + margin-left: 8.33333%; +} + +.offset-2 { + margin-left: 16.66667%; +} + +.offset-3 { + margin-left: 25%; +} + +.offset-4 { + margin-left: 33.33333%; +} + +.offset-5 { + margin-left: 41.66667%; +} + +.offset-6 { + margin-left: 50%; +} + +.offset-7 { + margin-left: 58.33333%; +} + +.offset-8 { + margin-left: 66.66667%; +} + +.offset-9 { + margin-left: 75%; +} + +.offset-10 { + margin-left: 83.33333%; +} + +.offset-11 { + margin-left: 91.66667%; +} + +@media (min-width: 576px) { + .col-sm { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + .col-sm-auto { + flex: 0 0 auto; + width: auto; + max-width: none; + } + .col-sm-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; + } + .col-sm-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; + } + .col-sm-3 { + flex: 0 0 25%; + max-width: 25%; + } + .col-sm-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; + } + .col-sm-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; + } + .col-sm-6 { + flex: 0 0 50%; + max-width: 50%; + } + .col-sm-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; + } + .col-sm-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; + } + .col-sm-9 { + flex: 0 0 75%; + max-width: 75%; + } + .col-sm-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; + } + .col-sm-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; + } + .col-sm-12 { + flex: 0 0 100%; + max-width: 100%; + } + .order-sm-first { + order: -1; + } + .order-sm-last { + order: 13; + } + .order-sm-0 { + order: 0; + } + .order-sm-1 { + order: 1; + } + .order-sm-2 { + order: 2; + } + .order-sm-3 { + order: 3; + } + .order-sm-4 { + order: 4; + } + .order-sm-5 { + order: 5; + } + .order-sm-6 { + order: 6; + } + .order-sm-7 { + order: 7; + } + .order-sm-8 { + order: 8; + } + .order-sm-9 { + order: 9; + } + .order-sm-10 { + order: 10; + } + .order-sm-11 { + order: 11; + } + .order-sm-12 { + order: 12; + } + .offset-sm-0 { + margin-left: 0; + } + .offset-sm-1 { + margin-left: 8.33333%; + } + .offset-sm-2 { + margin-left: 16.66667%; + } + .offset-sm-3 { + margin-left: 25%; + } + .offset-sm-4 { + margin-left: 33.33333%; + } + .offset-sm-5 { + margin-left: 41.66667%; + } + .offset-sm-6 { + margin-left: 50%; + } + .offset-sm-7 { + margin-left: 58.33333%; + } + .offset-sm-8 { + margin-left: 66.66667%; + } + .offset-sm-9 { + margin-left: 75%; + } + .offset-sm-10 { + margin-left: 83.33333%; + } + .offset-sm-11 { + margin-left: 91.66667%; + } +} + +@media (min-width: 768px) { + .col-md { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + .col-md-auto { + flex: 0 0 auto; + width: auto; + max-width: none; + } + .col-md-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; + } + .col-md-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; + } + .col-md-3 { + flex: 0 0 25%; + max-width: 25%; + } + .col-md-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; + } + .col-md-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; + } + .col-md-6 { + flex: 0 0 50%; + max-width: 50%; + } + .col-md-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; + } + .col-md-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; + } + .col-md-9 { + flex: 0 0 75%; + max-width: 75%; + } + .col-md-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; + } + .col-md-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; + } + .col-md-12 { + flex: 0 0 100%; + max-width: 100%; + } + .order-md-first { + order: -1; + } + .order-md-last { + order: 13; + } + .order-md-0 { + order: 0; + } + .order-md-1 { + order: 1; + } + .order-md-2 { + order: 2; + } + .order-md-3 { + order: 3; + } + .order-md-4 { + order: 4; + } + .order-md-5 { + order: 5; + } + .order-md-6 { + order: 6; + } + .order-md-7 { + order: 7; + } + .order-md-8 { + order: 8; + } + .order-md-9 { + order: 9; + } + .order-md-10 { + order: 10; + } + .order-md-11 { + order: 11; + } + .order-md-12 { + order: 12; + } + .offset-md-0 { + margin-left: 0; + } + .offset-md-1 { + margin-left: 8.33333%; + } + .offset-md-2 { + margin-left: 16.66667%; + } + .offset-md-3 { + margin-left: 25%; + } + .offset-md-4 { + margin-left: 33.33333%; + } + .offset-md-5 { + margin-left: 41.66667%; + } + .offset-md-6 { + margin-left: 50%; + } + .offset-md-7 { + margin-left: 58.33333%; + } + .offset-md-8 { + margin-left: 66.66667%; + } + .offset-md-9 { + margin-left: 75%; + } + .offset-md-10 { + margin-left: 83.33333%; + } + .offset-md-11 { + margin-left: 91.66667%; + } +} + +@media (min-width: 992px) { + .col-lg { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + .col-lg-auto { + flex: 0 0 auto; + width: auto; + max-width: none; + } + .col-lg-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; + } + .col-lg-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; + } + .col-lg-3 { + flex: 0 0 25%; + max-width: 25%; + } + .col-lg-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; + } + .col-lg-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; + } + .col-lg-6 { + flex: 0 0 50%; + max-width: 50%; + } + .col-lg-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; + } + .col-lg-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; + } + .col-lg-9 { + flex: 0 0 75%; + max-width: 75%; + } + .col-lg-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; + } + .col-lg-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; + } + .col-lg-12 { + flex: 0 0 100%; + max-width: 100%; + } + .order-lg-first { + order: -1; + } + .order-lg-last { + order: 13; + } + .order-lg-0 { + order: 0; + } + .order-lg-1 { + order: 1; + } + .order-lg-2 { + order: 2; + } + .order-lg-3 { + order: 3; + } + .order-lg-4 { + order: 4; + } + .order-lg-5 { + order: 5; + } + .order-lg-6 { + order: 6; + } + .order-lg-7 { + order: 7; + } + .order-lg-8 { + order: 8; + } + .order-lg-9 { + order: 9; + } + .order-lg-10 { + order: 10; + } + .order-lg-11 { + order: 11; + } + .order-lg-12 { + order: 12; + } + .offset-lg-0 { + margin-left: 0; + } + .offset-lg-1 { + margin-left: 8.33333%; + } + .offset-lg-2 { + margin-left: 16.66667%; + } + .offset-lg-3 { + margin-left: 25%; + } + .offset-lg-4 { + margin-left: 33.33333%; + } + .offset-lg-5 { + margin-left: 41.66667%; + } + .offset-lg-6 { + margin-left: 50%; + } + .offset-lg-7 { + margin-left: 58.33333%; + } + .offset-lg-8 { + margin-left: 66.66667%; + } + .offset-lg-9 { + margin-left: 75%; + } + .offset-lg-10 { + margin-left: 83.33333%; + } + .offset-lg-11 { + margin-left: 91.66667%; + } +} + +@media (min-width: 1200px) { + .col-xl { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + .col-xl-auto { + flex: 0 0 auto; + width: auto; + max-width: none; + } + .col-xl-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; + } + .col-xl-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; + } + .col-xl-3 { + flex: 0 0 25%; + max-width: 25%; + } + .col-xl-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; + } + .col-xl-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; + } + .col-xl-6 { + flex: 0 0 50%; + max-width: 50%; + } + .col-xl-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; + } + .col-xl-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; + } + .col-xl-9 { + flex: 0 0 75%; + max-width: 75%; + } + .col-xl-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; + } + .col-xl-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; + } + .col-xl-12 { + flex: 0 0 100%; + max-width: 100%; + } + .order-xl-first { + order: -1; + } + .order-xl-last { + order: 13; + } + .order-xl-0 { + order: 0; + } + .order-xl-1 { + order: 1; + } + .order-xl-2 { + order: 2; + } + .order-xl-3 { + order: 3; + } + .order-xl-4 { + order: 4; + } + .order-xl-5 { + order: 5; + } + .order-xl-6 { + order: 6; + } + .order-xl-7 { + order: 7; + } + .order-xl-8 { + order: 8; + } + .order-xl-9 { + order: 9; + } + .order-xl-10 { + order: 10; + } + .order-xl-11 { + order: 11; + } + .order-xl-12 { + order: 12; + } + .offset-xl-0 { + margin-left: 0; + } + .offset-xl-1 { + margin-left: 8.33333%; + } + .offset-xl-2 { + margin-left: 16.66667%; + } + .offset-xl-3 { + margin-left: 25%; + } + .offset-xl-4 { + margin-left: 33.33333%; + } + .offset-xl-5 { + margin-left: 41.66667%; + } + .offset-xl-6 { + margin-left: 50%; + } + .offset-xl-7 { + margin-left: 58.33333%; + } + .offset-xl-8 { + margin-left: 66.66667%; + } + .offset-xl-9 { + margin-left: 75%; + } + .offset-xl-10 { + margin-left: 83.33333%; + } + .offset-xl-11 { + margin-left: 91.66667%; + } +} + +.table { + width: 100%; + max-width: 100%; + margin-bottom: 1rem; + background-color: transparent; +} + +.table th, +.table td { + padding: 0.75rem; + vertical-align: top; + border-top: 1px solid #dee2e6; +} + +.table thead th { + vertical-align: bottom; + border-bottom: 2px solid #dee2e6; +} + +.table tbody + tbody { + border-top: 2px solid #dee2e6; +} + +.table .table { + background-color: #fff; +} + +.table-sm th, +.table-sm td { + padding: 0.3rem; +} + +.table-bordered { + border: 1px solid #dee2e6; +} + +.table-bordered th, +.table-bordered td { + border: 1px solid #dee2e6; +} + +.table-bordered thead th, +.table-bordered thead td { + border-bottom-width: 2px; +} + +.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(0, 0, 0, 0.05); +} + +.table-hover tbody tr:hover { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-primary, +.table-primary > th, +.table-primary > td { + background-color: #b8daff; +} + +.table-hover .table-primary:hover { + background-color: #9fcdff; +} + +.table-hover .table-primary:hover > td, +.table-hover .table-primary:hover > th { + background-color: #9fcdff; +} + +.table-secondary, +.table-secondary > th, +.table-secondary > td { + background-color: #d6d8db; +} + +.table-hover .table-secondary:hover { + background-color: #c8cbcf; +} + +.table-hover .table-secondary:hover > td, +.table-hover .table-secondary:hover > th { + background-color: #c8cbcf; +} + +.table-success, +.table-success > th, +.table-success > td { + background-color: #c3e6cb; +} + +.table-hover .table-success:hover { + background-color: #b1dfbb; +} + +.table-hover .table-success:hover > td, +.table-hover .table-success:hover > th { + background-color: #b1dfbb; +} + +.table-info, +.table-info > th, +.table-info > td { + background-color: #bee5eb; +} + +.table-hover .table-info:hover { + background-color: #abdde5; +} + +.table-hover .table-info:hover > td, +.table-hover .table-info:hover > th { + background-color: #abdde5; +} + +.table-warning, +.table-warning > th, +.table-warning > td { + background-color: #ffeeba; +} + +.table-hover .table-warning:hover { + background-color: #ffe8a1; +} + +.table-hover .table-warning:hover > td, +.table-hover .table-warning:hover > th { + background-color: #ffe8a1; +} + +.table-danger, +.table-danger > th, +.table-danger > td { + background-color: #f5c6cb; +} + +.table-hover .table-danger:hover { + background-color: #f1b0b7; +} + +.table-hover .table-danger:hover > td, +.table-hover .table-danger:hover > th { + background-color: #f1b0b7; +} + +.table-light, +.table-light > th, +.table-light > td { + background-color: #fdfdfe; +} + +.table-hover .table-light:hover { + background-color: #ececf6; +} + +.table-hover .table-light:hover > td, +.table-hover .table-light:hover > th { + background-color: #ececf6; +} + +.table-dark, +.table-dark > th, +.table-dark > td { + background-color: #c6c8ca; +} + +.table-hover .table-dark:hover { + background-color: #b9bbbe; +} + +.table-hover .table-dark:hover > td, +.table-hover .table-dark:hover > th { + background-color: #b9bbbe; +} + +.table-active, +.table-active > th, +.table-active > td { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-hover .table-active:hover { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-hover .table-active:hover > td, +.table-hover .table-active:hover > th { + background-color: rgba(0, 0, 0, 0.075); +} + +.table .thead-dark th { + color: #fff; + background-color: #212529; + border-color: #32383e; +} + +.table .thead-light th { + color: #495057; + background-color: #e9ecef; + border-color: #dee2e6; +} + +.table-dark { + color: #fff; + background-color: #212529; +} + +.table-dark th, +.table-dark td, +.table-dark thead th { + border-color: #32383e; +} + +.table-dark.table-bordered { + border: 0; +} + +.table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, 0.05); +} + +.table-dark.table-hover tbody tr:hover { + background-color: rgba(255, 255, 255, 0.075); +} + +@media (max-width: 575.98px) { + .table-responsive-sm { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + .table-responsive-sm > .table-bordered { + border: 0; + } +} + +@media (max-width: 767.98px) { + .table-responsive-md { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + .table-responsive-md > .table-bordered { + border: 0; + } +} + +@media (max-width: 991.98px) { + .table-responsive-lg { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + .table-responsive-lg > .table-bordered { + border: 0; + } +} + +@media (max-width: 1199.98px) { + .table-responsive-xl { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + .table-responsive-xl > .table-bordered { + border: 0; + } +} + +.table-responsive { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; +} + +.table-responsive > .table-bordered { + border: 0; +} + +.form-control { + display: block; + width: 100%; + padding: 0.375rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} + +.form-control:focus { + color: #495057; + background-color: #fff; + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.form-control::placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control:disabled, .form-control[readonly] { + background-color: #e9ecef; + opacity: 1; +} + +select.form-control:not([size]):not([multiple]) { + height: calc(2.25rem + 2px); +} + +select.form-control:focus::-ms-value { + color: #495057; + background-color: #fff; +} + +.form-control-file, +.form-control-range { + display: block; + width: 100%; +} + +.col-form-label { + padding-top: calc(0.375rem + 1px); + padding-bottom: calc(0.375rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; +} + +.col-form-label-lg { + padding-top: calc(0.5rem + 1px); + padding-bottom: calc(0.5rem + 1px); + font-size: 1.25rem; + line-height: 1.5; +} + +.col-form-label-sm { + padding-top: calc(0.25rem + 1px); + padding-bottom: calc(0.25rem + 1px); + font-size: 0.875rem; + line-height: 1.5; +} + +.form-control-plaintext { + display: block; + width: 100%; + padding-top: 0.375rem; + padding-bottom: 0.375rem; + margin-bottom: 0; + line-height: 1.5; + background-color: transparent; + border: solid transparent; + border-width: 1px 0; +} + +.form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control, +.input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text, +.input-group-sm > .input-group-append > .form-control-plaintext.input-group-text, +.input-group-sm > .input-group-prepend > .form-control-plaintext.btn, +.input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control, +.input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text, +.input-group-lg > .input-group-append > .form-control-plaintext.input-group-text, +.input-group-lg > .input-group-prepend > .form-control-plaintext.btn, +.input-group-lg > .input-group-append > .form-control-plaintext.btn { + padding-right: 0; + padding-left: 0; +} + +.form-control-sm, .input-group-sm > .form-control, +.input-group-sm > .input-group-prepend > .input-group-text, +.input-group-sm > .input-group-append > .input-group-text, +.input-group-sm > .input-group-prepend > .btn, +.input-group-sm > .input-group-append > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +select.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]), +.input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]), +.input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]), +.input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]), +.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) { + height: calc(1.8125rem + 2px); +} + +.form-control-lg, .input-group-lg > .form-control, +.input-group-lg > .input-group-prepend > .input-group-text, +.input-group-lg > .input-group-append > .input-group-text, +.input-group-lg > .input-group-prepend > .btn, +.input-group-lg > .input-group-append > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +select.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]), +.input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]), +.input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]), +.input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]), +.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) { + height: calc(2.875rem + 2px); +} + +.form-group { + margin-bottom: 1rem; +} + +.form-text { + display: block; + margin-top: 0.25rem; +} + +.form-row { + display: flex; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px; +} + +.form-row > .col, +.form-row > [class*="col-"] { + padding-right: 5px; + padding-left: 5px; +} + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem; +} + +.form-check-input { + position: absolute; + margin-top: 0.3rem; + margin-left: -1.25rem; +} + +.form-check-input:disabled ~ .form-check-label { + color: #6c757d; +} + +.form-check-label { + margin-bottom: 0; +} + +.form-check-inline { + display: inline-flex; + align-items: center; + padding-left: 0; + margin-right: 0.75rem; +} + +.form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: 0.3125rem; + margin-left: 0; +} + +.valid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #28a745; +} + +.valid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: .5rem; + margin-top: .1rem; + font-size: .875rem; + line-height: 1; + color: #fff; + background-color: rgba(40, 167, 69, 0.8); + border-radius: .2rem; +} + +.was-validated .form-control:valid, .form-control.is-valid, .was-validated +.custom-select:valid, +.custom-select.is-valid { + border-color: #28a745; +} + +.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated +.custom-select:valid:focus, +.custom-select.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated .form-control:valid ~ .valid-feedback, +.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, +.form-control.is-valid ~ .valid-tooltip, .was-validated +.custom-select:valid ~ .valid-feedback, +.was-validated +.custom-select:valid ~ .valid-tooltip, +.custom-select.is-valid ~ .valid-feedback, +.custom-select.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { + color: #28a745; +} + +.was-validated .form-check-input:valid ~ .valid-feedback, +.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, +.form-check-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { + color: #28a745; +} + +.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { + background-color: #71dd8a; +} + +.was-validated .custom-control-input:valid ~ .valid-feedback, +.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, +.custom-control-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { + background-color: #34ce57; +} + +.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { + border-color: #28a745; +} + +.was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before { + border-color: inherit; +} + +.was-validated .custom-file-input:valid ~ .valid-feedback, +.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, +.custom-file-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #dc3545; +} + +.invalid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: .5rem; + margin-top: .1rem; + font-size: .875rem; + line-height: 1; + color: #fff; + background-color: rgba(220, 53, 69, 0.8); + border-radius: .2rem; +} + +.was-validated .form-control:invalid, .form-control.is-invalid, .was-validated +.custom-select:invalid, +.custom-select.is-invalid { + border-color: #dc3545; +} + +.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated +.custom-select:invalid:focus, +.custom-select.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated .form-control:invalid ~ .invalid-feedback, +.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, +.form-control.is-invalid ~ .invalid-tooltip, .was-validated +.custom-select:invalid ~ .invalid-feedback, +.was-validated +.custom-select:invalid ~ .invalid-tooltip, +.custom-select.is-invalid ~ .invalid-feedback, +.custom-select.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { + color: #dc3545; +} + +.was-validated .form-check-input:invalid ~ .invalid-feedback, +.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, +.form-check-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { + color: #dc3545; +} + +.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { + background-color: #efa2a9; +} + +.was-validated .custom-control-input:invalid ~ .invalid-feedback, +.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, +.custom-control-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { + background-color: #e4606d; +} + +.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { + border-color: #dc3545; +} + +.was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before { + border-color: inherit; +} + +.was-validated .custom-file-input:invalid ~ .invalid-feedback, +.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, +.custom-file-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.form-inline { + display: flex; + flex-flow: row wrap; + align-items: center; +} + +.form-inline .form-check { + width: 100%; +} + +@media (min-width: 576px) { + .form-inline label { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0; + } + .form-inline .form-group { + display: flex; + flex: 0 0 auto; + flex-flow: row wrap; + align-items: center; + margin-bottom: 0; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-plaintext { + display: inline-block; + } + .form-inline .input-group { + width: auto; + } + .form-inline .form-check { + display: flex; + align-items: center; + justify-content: center; + width: auto; + padding-left: 0; + } + .form-inline .form-check-input { + position: relative; + margin-top: 0; + margin-right: 0.25rem; + margin-left: 0; + } + .form-inline .custom-control { + align-items: center; + justify-content: center; + } + .form-inline .custom-control-label { + margin-bottom: 0; + } +} + +.btn { + display: inline-block; + font-weight: 400; + text-align: center; + white-space: nowrap; + vertical-align: middle; + user-select: none; + border: 1px solid transparent; + padding: 0.375rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +.btn:hover, .btn:focus { + text-decoration: none; +} + +.btn:focus, .btn.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.btn.disabled, .btn:disabled { + opacity: 0.65; +} + +.btn:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active { + background-image: none; +} + +a.btn.disabled, +fieldset:disabled a.btn { + pointer-events: none; +} + +.btn-primary { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-primary:hover { + color: #fff; + background-color: #0069d9; + border-color: #0062cc; +} + +.btn-primary:focus, .btn-primary.focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.btn-primary.disabled, .btn-primary:disabled { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, +.show > .btn-primary.dropdown-toggle { + color: #fff; + background-color: #0062cc; + border-color: #005cbf; +} + +.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, +.show > .btn-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.btn-secondary { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-secondary:hover { + color: #fff; + background-color: #5a6268; + border-color: #545b62; +} + +.btn-secondary:focus, .btn-secondary.focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.btn-secondary.disabled, .btn-secondary:disabled { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, +.show > .btn-secondary.dropdown-toggle { + color: #fff; + background-color: #545b62; + border-color: #4e555b; +} + +.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, +.show > .btn-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.btn-success { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-success:hover { + color: #fff; + background-color: #218838; + border-color: #1e7e34; +} + +.btn-success:focus, .btn-success.focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.btn-success.disabled, .btn-success:disabled { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, +.show > .btn-success.dropdown-toggle { + color: #fff; + background-color: #1e7e34; + border-color: #1c7430; +} + +.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, +.show > .btn-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.btn-info { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-info:hover { + color: #fff; + background-color: #138496; + border-color: #117a8b; +} + +.btn-info:focus, .btn-info.focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.btn-info.disabled, .btn-info:disabled { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, +.show > .btn-info.dropdown-toggle { + color: #fff; + background-color: #117a8b; + border-color: #10707f; +} + +.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, +.show > .btn-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.btn-warning { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-warning:hover { + color: #212529; + background-color: #e0a800; + border-color: #d39e00; +} + +.btn-warning:focus, .btn-warning.focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.btn-warning.disabled, .btn-warning:disabled { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, +.show > .btn-warning.dropdown-toggle { + color: #212529; + background-color: #d39e00; + border-color: #c69500; +} + +.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, +.show > .btn-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.btn-danger { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-danger:hover { + color: #fff; + background-color: #c82333; + border-color: #bd2130; +} + +.btn-danger:focus, .btn-danger.focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.btn-danger.disabled, .btn-danger:disabled { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, +.show > .btn-danger.dropdown-toggle { + color: #fff; + background-color: #bd2130; + border-color: #b21f2d; +} + +.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, +.show > .btn-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.btn-light { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-light:hover { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; +} + +.btn-light:focus, .btn-light.focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.btn-light.disabled, .btn-light:disabled { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, +.show > .btn-light.dropdown-toggle { + color: #212529; + background-color: #dae0e5; + border-color: #d3d9df; +} + +.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, +.show > .btn-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.btn-dark { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-dark:hover { + color: #fff; + background-color: #23272b; + border-color: #1d2124; +} + +.btn-dark:focus, .btn-dark.focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.btn-dark.disabled, .btn-dark:disabled { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, +.show > .btn-dark.dropdown-toggle { + color: #fff; + background-color: #1d2124; + border-color: #171a1d; +} + +.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, +.show > .btn-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.btn-outline-primary { + color: #007bff; + background-color: transparent; + background-image: none; + border-color: #007bff; +} + +.btn-outline-primary:hover { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-outline-primary:focus, .btn-outline-primary.focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.btn-outline-primary.disabled, .btn-outline-primary:disabled { + color: #007bff; + background-color: transparent; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, +.show > .btn-outline-primary.dropdown-toggle { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.btn-outline-secondary { + color: #6c757d; + background-color: transparent; + background-image: none; + border-color: #6c757d; +} + +.btn-outline-secondary:hover { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-outline-secondary:focus, .btn-outline-secondary.focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { + color: #6c757d; + background-color: transparent; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, +.show > .btn-outline-secondary.dropdown-toggle { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.btn-outline-success { + color: #28a745; + background-color: transparent; + background-image: none; + border-color: #28a745; +} + +.btn-outline-success:hover { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-outline-success:focus, .btn-outline-success.focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.btn-outline-success.disabled, .btn-outline-success:disabled { + color: #28a745; + background-color: transparent; +} + +.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, +.show > .btn-outline-success.dropdown-toggle { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.btn-outline-info { + color: #17a2b8; + background-color: transparent; + background-image: none; + border-color: #17a2b8; +} + +.btn-outline-info:hover { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-outline-info:focus, .btn-outline-info.focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.btn-outline-info.disabled, .btn-outline-info:disabled { + color: #17a2b8; + background-color: transparent; +} + +.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, +.show > .btn-outline-info.dropdown-toggle { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.btn-outline-warning { + color: #ffc107; + background-color: transparent; + background-image: none; + border-color: #ffc107; +} + +.btn-outline-warning:hover { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-outline-warning:focus, .btn-outline-warning.focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.btn-outline-warning.disabled, .btn-outline-warning:disabled { + color: #ffc107; + background-color: transparent; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, +.show > .btn-outline-warning.dropdown-toggle { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.btn-outline-danger { + color: #dc3545; + background-color: transparent; + background-image: none; + border-color: #dc3545; +} + +.btn-outline-danger:hover { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-outline-danger:focus, .btn-outline-danger.focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.btn-outline-danger.disabled, .btn-outline-danger:disabled { + color: #dc3545; + background-color: transparent; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, +.show > .btn-outline-danger.dropdown-toggle { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.btn-outline-light { + color: #f8f9fa; + background-color: transparent; + background-image: none; + border-color: #f8f9fa; +} + +.btn-outline-light:hover { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-outline-light:focus, .btn-outline-light.focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.btn-outline-light.disabled, .btn-outline-light:disabled { + color: #f8f9fa; + background-color: transparent; +} + +.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, +.show > .btn-outline-light.dropdown-toggle { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.btn-outline-dark { + color: #343a40; + background-color: transparent; + background-image: none; + border-color: #343a40; +} + +.btn-outline-dark:hover { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-outline-dark:focus, .btn-outline-dark.focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.btn-outline-dark.disabled, .btn-outline-dark:disabled { + color: #343a40; + background-color: transparent; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, +.show > .btn-outline-dark.dropdown-toggle { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.btn-link { + font-weight: 400; + color: #007bff; + background-color: transparent; +} + +.btn-link:hover { + color: #0056b3; + text-decoration: underline; + background-color: transparent; + border-color: transparent; +} + +.btn-link:focus, .btn-link.focus { + text-decoration: underline; + border-color: transparent; + box-shadow: none; +} + +.btn-link:disabled, .btn-link.disabled { + color: #6c757d; +} + +.btn-lg, .btn-group-lg > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +.btn-sm, .btn-group-sm > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.btn-block { + display: block; + width: 100%; +} + +.btn-block + .btn-block { + margin-top: 0.5rem; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.fade { + opacity: 0; + transition: opacity 0.15s linear; +} + +.fade.show { + opacity: 1; +} + +.collapse { + display: none; +} + +.collapse.show { + display: block; +} + +tr.collapse.show { + display: table-row; +} + +tbody.collapse.show { + display: table-row-group; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + transition: height 0.35s ease; +} + +.dropup, +.dropdown { + position: relative; +} + +.dropdown-toggle::after { + display: inline-block; + width: 0; + height: 0; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; +} + +.dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 10rem; + padding: 0.5rem 0; + margin: 0.125rem 0 0; + font-size: 1rem; + color: #212529; + text-align: left; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0.25rem; +} + +.dropup .dropdown-menu { + margin-top: 0; + margin-bottom: 0.125rem; +} + +.dropup .dropdown-toggle::after { + display: inline-block; + width: 0; + height: 0; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-right: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-left: 0.3em solid transparent; +} + +.dropup .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-menu { + margin-top: 0; + margin-left: 0.125rem; +} + +.dropright .dropdown-toggle::after { + display: inline-block; + width: 0; + height: 0; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-bottom: 0.3em solid transparent; + border-left: 0.3em solid; +} + +.dropright .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-toggle::after { + vertical-align: 0; +} + +.dropleft .dropdown-menu { + margin-top: 0; + margin-right: 0.125rem; +} + +.dropleft .dropdown-toggle::after { + display: inline-block; + width: 0; + height: 0; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; +} + +.dropleft .dropdown-toggle::after { + display: none; +} + +.dropleft .dropdown-toggle::before { + display: inline-block; + width: 0; + height: 0; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0.3em solid; + border-bottom: 0.3em solid transparent; +} + +.dropleft .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropleft .dropdown-toggle::before { + vertical-align: 0; +} + +.dropdown-divider { + height: 0; + margin: 0.5rem 0; + overflow: hidden; + border-top: 1px solid #e9ecef; +} + +.dropdown-item { + display: block; + width: 100%; + padding: 0.25rem 1.5rem; + clear: both; + font-weight: 400; + color: #212529; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0; +} + +.dropdown-item:hover, .dropdown-item:focus { + color: #16181b; + text-decoration: none; + background-color: #f8f9fa; +} + +.dropdown-item.active, .dropdown-item:active { + color: #fff; + text-decoration: none; + background-color: #007bff; +} + +.dropdown-item.disabled, .dropdown-item:disabled { + color: #6c757d; + background-color: transparent; +} + +.dropdown-menu.show { + display: block; +} + +.dropdown-header { + display: block; + padding: 0.5rem 1.5rem; + margin-bottom: 0; + font-size: 0.875rem; + color: #6c757d; + white-space: nowrap; +} + +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-flex; + vertical-align: middle; +} + +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + flex: 0 1 auto; +} + +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover { + z-index: 1; +} + +.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, +.btn-group-vertical > .btn:focus, +.btn-group-vertical > .btn:active, +.btn-group-vertical > .btn.active { + z-index: 1; +} + +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group, +.btn-group-vertical .btn + .btn, +.btn-group-vertical .btn + .btn-group, +.btn-group-vertical .btn-group + .btn, +.btn-group-vertical .btn-group + .btn-group { + margin-left: -1px; +} + +.btn-toolbar { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} + +.btn-toolbar .input-group { + width: auto; +} + +.btn-group > .btn:first-child { + margin-left: 0; +} + +.btn-group > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.dropdown-toggle-split { + padding-right: 0.5625rem; + padding-left: 0.5625rem; +} + +.dropdown-toggle-split::after { + margin-left: 0; +} + +.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { + padding-right: 0.375rem; + padding-left: 0.375rem; +} + +.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.btn-group-vertical { + flex-direction: column; + align-items: flex-start; + justify-content: center; +} + +.btn-group-vertical .btn, +.btn-group-vertical .btn-group { + width: 100%; +} + +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group-vertical > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn:not(:first-child), +.btn-group-vertical > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.btn-group-toggle > .btn, +.btn-group-toggle > .btn-group > .btn { + margin-bottom: 0; +} + +.btn-group-toggle > .btn input[type="radio"], +.btn-group-toggle > .btn input[type="checkbox"], +.btn-group-toggle > .btn-group > .btn input[type="radio"], +.btn-group-toggle > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} + +.input-group { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: stretch; + width: 100%; +} + +.input-group > .form-control, +.input-group > .custom-select, +.input-group > .custom-file { + position: relative; + flex: 1 1 auto; + width: 1%; + margin-bottom: 0; +} + +.input-group > .form-control:focus, +.input-group > .custom-select:focus, +.input-group > .custom-file:focus { + z-index: 3; +} + +.input-group > .form-control + .form-control, +.input-group > .form-control + .custom-select, +.input-group > .form-control + .custom-file, +.input-group > .custom-select + .form-control, +.input-group > .custom-select + .custom-select, +.input-group > .custom-select + .custom-file, +.input-group > .custom-file + .form-control, +.input-group > .custom-file + .custom-select, +.input-group > .custom-file + .custom-file { + margin-left: -1px; +} + +.input-group > .form-control:not(:last-child), +.input-group > .custom-select:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .form-control:not(:first-child), +.input-group > .custom-select:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group > .custom-file { + display: flex; + align-items: center; +} + +.input-group > .custom-file:not(:last-child) .custom-file-label, +.input-group > .custom-file:not(:last-child) .custom-file-label::before { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .custom-file:not(:first-child) .custom-file-label, +.input-group > .custom-file:not(:first-child) .custom-file-label::before { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group-prepend, +.input-group-append { + display: flex; +} + +.input-group-prepend .btn, +.input-group-append .btn { + position: relative; + z-index: 2; +} + +.input-group-prepend .btn + .btn, +.input-group-prepend .btn + .input-group-text, +.input-group-prepend .input-group-text + .input-group-text, +.input-group-prepend .input-group-text + .btn, +.input-group-append .btn + .btn, +.input-group-append .btn + .input-group-text, +.input-group-append .input-group-text + .input-group-text, +.input-group-append .input-group-text + .btn { + margin-left: -1px; +} + +.input-group-prepend { + margin-right: -1px; +} + +.input-group-append { + margin-left: -1px; +} + +.input-group-text { + display: flex; + align-items: center; + padding: 0.375rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + text-align: center; + white-space: nowrap; + background-color: #e9ecef; + border: 1px solid #ced4da; + border-radius: 0.25rem; +} + +.input-group-text input[type="radio"], +.input-group-text input[type="checkbox"] { + margin-top: 0; +} + +.input-group > .input-group-prepend > .btn, +.input-group > .input-group-prepend > .input-group-text, +.input-group > .input-group-append:not(:last-child) > .btn, +.input-group > .input-group-append:not(:last-child) > .input-group-text, +.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .input-group-append > .btn, +.input-group > .input-group-append > .input-group-text, +.input-group > .input-group-prepend:not(:first-child) > .btn, +.input-group > .input-group-prepend:not(:first-child) > .input-group-text, +.input-group > .input-group-prepend:first-child > .btn:not(:first-child), +.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.custom-control { + position: relative; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem; +} + +.custom-control-inline { + display: inline-flex; + margin-right: 1rem; +} + +.custom-control-input { + position: absolute; + z-index: -1; + opacity: 0; +} + +.custom-control-input:checked ~ .custom-control-label::before { + color: #fff; + background-color: #007bff; +} + +.custom-control-input:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-control-input:active ~ .custom-control-label::before { + color: #fff; + background-color: #b3d7ff; +} + +.custom-control-input:disabled ~ .custom-control-label { + color: #6c757d; +} + +.custom-control-input:disabled ~ .custom-control-label::before { + background-color: #e9ecef; +} + +.custom-control-label { + margin-bottom: 0; +} + +.custom-control-label::before { + position: absolute; + top: 0.25rem; + left: 0; + display: block; + width: 1rem; + height: 1rem; + pointer-events: none; + content: ""; + user-select: none; + background-color: #dee2e6; +} + +.custom-control-label::after { + position: absolute; + top: 0.25rem; + left: 0; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background-repeat: no-repeat; + background-position: center center; + background-size: 50% 50%; +} + +.custom-checkbox .custom-control-label::before { + border-radius: 0.25rem; +} + +.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before { + background-color: #007bff; +} + +.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"); +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { + background-color: #007bff; +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E"); +} + +.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-radio .custom-control-label::before { + border-radius: 50%; +} + +.custom-radio .custom-control-input:checked ~ .custom-control-label::before { + background-color: #007bff; +} + +.custom-radio .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E"); +} + +.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-select { + display: inline-block; + width: 100%; + height: calc(2.25rem + 2px); + padding: 0.375rem 1.75rem 0.375rem 0.75rem; + line-height: 1.5; + color: #495057; + vertical-align: middle; + background: #fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right 0.75rem center; + background-size: 8px 10px; + border: 1px solid #ced4da; + border-radius: 0.25rem; + appearance: none; +} + +.custom-select:focus { + border-color: #80bdff; + outline: 0; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5); +} + +.custom-select:focus::-ms-value { + color: #495057; + background-color: #fff; +} + +.custom-select[multiple], .custom-select[size]:not([size="1"]) { + height: auto; + padding-right: 0.75rem; + background-image: none; +} + +.custom-select:disabled { + color: #6c757d; + background-color: #e9ecef; +} + +.custom-select::-ms-expand { + opacity: 0; +} + +.custom-select-sm { + height: calc(1.8125rem + 2px); + padding-top: 0.375rem; + padding-bottom: 0.375rem; + font-size: 75%; +} + +.custom-select-lg { + height: calc(2.875rem + 2px); + padding-top: 0.375rem; + padding-bottom: 0.375rem; + font-size: 125%; +} + +.custom-file { + position: relative; + display: inline-block; + width: 100%; + height: calc(2.25rem + 2px); + margin-bottom: 0; +} + +.custom-file-input { + position: relative; + z-index: 2; + width: 100%; + height: calc(2.25rem + 2px); + margin: 0; + opacity: 0; +} + +.custom-file-input:focus ~ .custom-file-control { + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-file-input:focus ~ .custom-file-control::before { + border-color: #80bdff; +} + +.custom-file-input:lang(en) ~ .custom-file-label::after { + content: "Browse"; +} + +.custom-file-label { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 1; + height: calc(2.25rem + 2px); + padding: 0.375rem 0.75rem; + line-height: 1.5; + color: #495057; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: 0.25rem; +} + +.custom-file-label::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 3; + display: block; + height: calc(calc(2.25rem + 2px) - 1px * 2); + padding: 0.375rem 0.75rem; + line-height: 1.5; + color: #495057; + content: "Browse"; + background-color: #e9ecef; + border-left: 1px solid #ced4da; + border-radius: 0 0.25rem 0.25rem 0; +} + +.nav { + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav-link { + display: block; + padding: 0.5rem 1rem; +} + +.nav-link:hover, .nav-link:focus { + text-decoration: none; +} + +.nav-link.disabled { + color: #6c757d; +} + +.nav-tabs { + border-bottom: 1px solid #dee2e6; +} + +.nav-tabs .nav-item { + margin-bottom: -1px; +} + +.nav-tabs .nav-link { + border: 1px solid transparent; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { + border-color: #e9ecef #e9ecef #dee2e6; +} + +.nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent; +} + +.nav-tabs .nav-link.active, +.nav-tabs .nav-item.show .nav-link { + color: #495057; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff; +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.nav-pills .nav-link { + border-radius: 0.25rem; +} + +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + color: #fff; + background-color: #007bff; +} + +.nav-fill .nav-item { + flex: 1 1 auto; + text-align: center; +} + +.nav-justified .nav-item { + flex-basis: 0; + flex-grow: 1; + text-align: center; +} + +.tab-content > .tab-pane { + display: none; +} + +.tab-content > .active { + display: block; +} + +.navbar { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 0.5rem 1rem; +} + +.navbar > .container, +.navbar > .container-fluid { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; +} + +.navbar-brand { + display: inline-block; + padding-top: 0.3125rem; + padding-bottom: 0.3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap; +} + +.navbar-brand:hover, .navbar-brand:focus { + text-decoration: none; +} + +.navbar-nav { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.navbar-nav .nav-link { + padding-right: 0; + padding-left: 0; +} + +.navbar-nav .dropdown-menu { + position: static; + float: none; +} + +.navbar-text { + display: inline-block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.navbar-collapse { + flex-basis: 100%; + flex-grow: 1; + align-items: center; +} + +.navbar-toggler { + padding: 0.25rem 0.75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: 0.25rem; +} + +.navbar-toggler:hover, .navbar-toggler:focus { + text-decoration: none; +} + +.navbar-toggler:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: no-repeat center center; + background-size: 100% 100%; +} + +@media (max-width: 575.98px) { + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 576px) { + .navbar-expand-sm { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-sm .navbar-nav { + flex-direction: row; + } + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-sm .navbar-nav .dropdown-menu-right { + right: 0; + left: auto; + } + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid { + flex-wrap: nowrap; + } + .navbar-expand-sm .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-sm .navbar-toggler { + display: none; + } + .navbar-expand-sm .dropup .dropdown-menu { + top: auto; + bottom: 100%; + } +} + +@media (max-width: 767.98px) { + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 768px) { + .navbar-expand-md { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-md .navbar-nav { + flex-direction: row; + } + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-md .navbar-nav .dropdown-menu-right { + right: 0; + left: auto; + } + .navbar-expand-md .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid { + flex-wrap: nowrap; + } + .navbar-expand-md .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-md .navbar-toggler { + display: none; + } + .navbar-expand-md .dropup .dropdown-menu { + top: auto; + bottom: 100%; + } +} + +@media (max-width: 991.98px) { + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 992px) { + .navbar-expand-lg { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-lg .navbar-nav { + flex-direction: row; + } + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-lg .navbar-nav .dropdown-menu-right { + right: 0; + left: auto; + } + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid { + flex-wrap: nowrap; + } + .navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-lg .navbar-toggler { + display: none; + } + .navbar-expand-lg .dropup .dropdown-menu { + top: auto; + bottom: 100%; + } +} + +@media (max-width: 1199.98px) { + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 1200px) { + .navbar-expand-xl { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-xl .navbar-nav { + flex-direction: row; + } + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-xl .navbar-nav .dropdown-menu-right { + right: 0; + left: auto; + } + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid { + flex-wrap: nowrap; + } + .navbar-expand-xl .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-xl .navbar-toggler { + display: none; + } + .navbar-expand-xl .dropup .dropdown-menu { + top: auto; + bottom: 100%; + } +} + +.navbar-expand { + flex-flow: row nowrap; + justify-content: flex-start; +} + +.navbar-expand > .container, +.navbar-expand > .container-fluid { + padding-right: 0; + padding-left: 0; +} + +.navbar-expand .navbar-nav { + flex-direction: row; +} + +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute; +} + +.navbar-expand .navbar-nav .dropdown-menu-right { + right: 0; + left: auto; +} + +.navbar-expand .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; +} + +.navbar-expand > .container, +.navbar-expand > .container-fluid { + flex-wrap: nowrap; +} + +.navbar-expand .navbar-collapse { + display: flex !important; + flex-basis: auto; +} + +.navbar-expand .navbar-toggler { + display: none; +} + +.navbar-expand .dropup .dropdown-menu { + top: auto; + bottom: 100%; +} + +.navbar-light .navbar-brand { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-nav .nav-link { + color: rgba(0, 0, 0, 0.5); +} + +.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { + color: rgba(0, 0, 0, 0.7); +} + +.navbar-light .navbar-nav .nav-link.disabled { + color: rgba(0, 0, 0, 0.3); +} + +.navbar-light .navbar-nav .show > .nav-link, +.navbar-light .navbar-nav .active > .nav-link, +.navbar-light .navbar-nav .nav-link.show, +.navbar-light .navbar-nav .nav-link.active { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-toggler { + color: rgba(0, 0, 0, 0.5); + border-color: rgba(0, 0, 0, 0.1); +} + +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); +} + +.navbar-light .navbar-text { + color: rgba(0, 0, 0, 0.5); +} + +.navbar-light .navbar-text a { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-dark .navbar-brand { + color: #fff; +} + +.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { + color: #fff; +} + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.5); +} + +.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { + color: rgba(255, 255, 255, 0.75); +} + +.navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, 0.25); +} + +.navbar-dark .navbar-nav .show > .nav-link, +.navbar-dark .navbar-nav .active > .nav-link, +.navbar-dark .navbar-nav .nav-link.show, +.navbar-dark .navbar-nav .nav-link.active { + color: #fff; +} + +.navbar-dark .navbar-toggler { + color: rgba(255, 255, 255, 0.5); + border-color: rgba(255, 255, 255, 0.1); +} + +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); +} + +.navbar-dark .navbar-text { + color: rgba(255, 255, 255, 0.5); +} + +.navbar-dark .navbar-text a { + color: #fff; +} + +.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { + color: #fff; +} + +.card { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #fff; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; +} + +.card > hr { + margin-right: 0; + margin-left: 0; +} + +.card > .list-group:first-child .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.card > .list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.card-body { + flex: 1 1 auto; + padding: 1.25rem; +} + +.card-title { + margin-bottom: 0.75rem; +} + +.card-subtitle { + margin-top: -0.375rem; + margin-bottom: 0; +} + +.card-text:last-child { + margin-bottom: 0; +} + +.card-link:hover { + text-decoration: none; +} + +.card-link + .card-link { + margin-left: 1.25rem; +} + +.card-header { + padding: 0.75rem 1.25rem; + margin-bottom: 0; + background-color: rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.125); +} + +.card-header:first-child { + border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; +} + +.card-header + .list-group .list-group-item:first-child { + border-top: 0; +} + +.card-footer { + padding: 0.75rem 1.25rem; + background-color: rgba(0, 0, 0, 0.03); + border-top: 1px solid rgba(0, 0, 0, 0.125); +} + +.card-footer:last-child { + border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); +} + +.card-header-tabs { + margin-right: -0.625rem; + margin-bottom: -0.75rem; + margin-left: -0.625rem; + border-bottom: 0; +} + +.card-header-pills { + margin-right: -0.625rem; + margin-left: -0.625rem; +} + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem; +} + +.card-img { + width: 100%; + border-radius: calc(0.25rem - 1px); +} + +.card-img-top { + width: 100%; + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); +} + +.card-img-bottom { + width: 100%; + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); +} + +.card-deck { + display: flex; + flex-direction: column; +} + +.card-deck .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-deck { + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; + } + .card-deck .card { + display: flex; + flex: 1 0 0%; + flex-direction: column; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; + } +} + +.card-group { + display: flex; + flex-direction: column; +} + +.card-group > .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-group { + flex-flow: row wrap; + } + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; + } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; + } + .card-group > .card:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + .card-group > .card:first-child .card-img-top, + .card-group > .card:first-child .card-header { + border-top-right-radius: 0; + } + .card-group > .card:first-child .card-img-bottom, + .card-group > .card:first-child .card-footer { + border-bottom-right-radius: 0; + } + .card-group > .card:last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + .card-group > .card:last-child .card-img-top, + .card-group > .card:last-child .card-header { + border-top-left-radius: 0; + } + .card-group > .card:last-child .card-img-bottom, + .card-group > .card:last-child .card-footer { + border-bottom-left-radius: 0; + } + .card-group > .card:only-child { + border-radius: 0.25rem; + } + .card-group > .card:only-child .card-img-top, + .card-group > .card:only-child .card-header { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; + } + .card-group > .card:only-child .card-img-bottom, + .card-group > .card:only-child .card-footer { + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + } + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) { + border-radius: 0; + } + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top, + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom, + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header, + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer { + border-radius: 0; + } +} + +.card-columns .card { + margin-bottom: 0.75rem; +} + +@media (min-width: 576px) { + .card-columns { + column-count: 3; + column-gap: 1.25rem; + } + .card-columns .card { + display: inline-block; + width: 100%; + } +} + +.breadcrumb { + display: flex; + flex-wrap: wrap; + padding: 0.75rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: #e9ecef; + border-radius: 0.25rem; +} + +.breadcrumb-item + .breadcrumb-item::before { + display: inline-block; + padding-right: 0.5rem; + padding-left: 0.5rem; + color: #6c757d; + content: "/"; +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: underline; +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: none; +} + +.breadcrumb-item.active { + color: #6c757d; +} + +.pagination { + display: flex; + padding-left: 0; + list-style: none; + border-radius: 0.25rem; +} + +.page-link { + position: relative; + display: block; + padding: 0.5rem 0.75rem; + margin-left: -1px; + line-height: 1.25; + color: #007bff; + background-color: #fff; + border: 1px solid #dee2e6; +} + +.page-link:hover { + color: #0056b3; + text-decoration: none; + background-color: #e9ecef; + border-color: #dee2e6; +} + +.page-link:focus { + z-index: 2; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.page-link:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.page-item:last-child .page-link { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; +} + +.page-item.active .page-link { + z-index: 1; + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.page-item.disabled .page-link { + color: #6c757d; + pointer-events: none; + cursor: auto; + background-color: #fff; + border-color: #dee2e6; +} + +.pagination-lg .page-link { + padding: 0.75rem 1.5rem; + font-size: 1.25rem; + line-height: 1.5; +} + +.pagination-lg .page-item:first-child .page-link { + border-top-left-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; +} + +.pagination-lg .page-item:last-child .page-link { + border-top-right-radius: 0.3rem; + border-bottom-right-radius: 0.3rem; +} + +.pagination-sm .page-link { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; +} + +.pagination-sm .page-item:first-child .page-link { + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; +} + +.pagination-sm .page-item:last-child .page-link { + border-top-right-radius: 0.2rem; + border-bottom-right-radius: 0.2rem; +} + +.badge { + display: inline-block; + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25rem; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +.badge-pill { + padding-right: 0.6em; + padding-left: 0.6em; + border-radius: 10rem; +} + +.badge-primary { + color: #fff; + background-color: #007bff; +} + +.badge-primary[href]:hover, .badge-primary[href]:focus { + color: #fff; + text-decoration: none; + background-color: #0062cc; +} + +.badge-secondary { + color: #fff; + background-color: #6c757d; +} + +.badge-secondary[href]:hover, .badge-secondary[href]:focus { + color: #fff; + text-decoration: none; + background-color: #545b62; +} + +.badge-success { + color: #fff; + background-color: #28a745; +} + +.badge-success[href]:hover, .badge-success[href]:focus { + color: #fff; + text-decoration: none; + background-color: #1e7e34; +} + +.badge-info { + color: #fff; + background-color: #17a2b8; +} + +.badge-info[href]:hover, .badge-info[href]:focus { + color: #fff; + text-decoration: none; + background-color: #117a8b; +} + +.badge-warning { + color: #212529; + background-color: #ffc107; +} + +.badge-warning[href]:hover, .badge-warning[href]:focus { + color: #212529; + text-decoration: none; + background-color: #d39e00; +} + +.badge-danger { + color: #fff; + background-color: #dc3545; +} + +.badge-danger[href]:hover, .badge-danger[href]:focus { + color: #fff; + text-decoration: none; + background-color: #bd2130; +} + +.badge-light { + color: #212529; + background-color: #f8f9fa; +} + +.badge-light[href]:hover, .badge-light[href]:focus { + color: #212529; + text-decoration: none; + background-color: #dae0e5; +} + +.badge-dark { + color: #fff; + background-color: #343a40; +} + +.badge-dark[href]:hover, .badge-dark[href]:focus { + color: #fff; + text-decoration: none; + background-color: #1d2124; +} + +.jumbotron { + padding: 2rem 1rem; + margin-bottom: 2rem; + background-color: #e9ecef; + border-radius: 0.3rem; +} + +@media (min-width: 576px) { + .jumbotron { + padding: 4rem 2rem; + } +} + +.jumbotron-fluid { + padding-right: 0; + padding-left: 0; + border-radius: 0; +} + +.alert { + position: relative; + padding: 0.75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: 0.25rem; +} + +.alert-heading { + color: inherit; +} + +.alert-link { + font-weight: 700; +} + +.alert-dismissible { + padding-right: 4rem; +} + +.alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + padding: 0.75rem 1.25rem; + color: inherit; +} + +.alert-primary { + color: #004085; + background-color: #cce5ff; + border-color: #b8daff; +} + +.alert-primary hr { + border-top-color: #9fcdff; +} + +.alert-primary .alert-link { + color: #002752; +} + +.alert-secondary { + color: #383d41; + background-color: #e2e3e5; + border-color: #d6d8db; +} + +.alert-secondary hr { + border-top-color: #c8cbcf; +} + +.alert-secondary .alert-link { + color: #202326; +} + +.alert-success { + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb; +} + +.alert-success hr { + border-top-color: #b1dfbb; +} + +.alert-success .alert-link { + color: #0b2e13; +} + +.alert-info { + color: #0c5460; + background-color: #d1ecf1; + border-color: #bee5eb; +} + +.alert-info hr { + border-top-color: #abdde5; +} + +.alert-info .alert-link { + color: #062c33; +} + +.alert-warning { + color: #856404; + background-color: #fff3cd; + border-color: #ffeeba; +} + +.alert-warning hr { + border-top-color: #ffe8a1; +} + +.alert-warning .alert-link { + color: #533f03; +} + +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} + +.alert-danger hr { + border-top-color: #f1b0b7; +} + +.alert-danger .alert-link { + color: #491217; +} + +.alert-light { + color: #818182; + background-color: #fefefe; + border-color: #fdfdfe; +} + +.alert-light hr { + border-top-color: #ececf6; +} + +.alert-light .alert-link { + color: #686868; +} + +.alert-dark { + color: #1b1e21; + background-color: #d6d8d9; + border-color: #c6c8ca; +} + +.alert-dark hr { + border-top-color: #b9bbbe; +} + +.alert-dark .alert-link { + color: #040505; +} + +@keyframes progress-bar-stripes { + from { + background-position: 1rem 0; + } + to { + background-position: 0 0; + } +} + +.progress { + display: flex; + height: 1rem; + overflow: hidden; + font-size: 0.75rem; + background-color: #e9ecef; + border-radius: 0.25rem; +} + +.progress-bar { + display: flex; + flex-direction: column; + justify-content: center; + color: #fff; + text-align: center; + background-color: #007bff; + transition: width 0.6s ease; +} + +.progress-bar-striped { + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: 1rem 1rem; +} + +.progress-bar-animated { + animation: progress-bar-stripes 1s linear infinite; +} + +.media { + display: flex; + align-items: flex-start; +} + +.media-body { + flex: 1; +} + +.list-group { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; +} + +.list-group-item-action { + width: 100%; + color: #495057; + text-align: inherit; +} + +.list-group-item-action:hover, .list-group-item-action:focus { + color: #495057; + text-decoration: none; + background-color: #f8f9fa; +} + +.list-group-item-action:active { + color: #212529; + background-color: #e9ecef; +} + +.list-group-item { + position: relative; + display: block; + padding: 0.75rem 1.25rem; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.125); +} + +.list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.list-group-item:hover, .list-group-item:focus { + z-index: 1; + text-decoration: none; +} + +.list-group-item.disabled, .list-group-item:disabled { + color: #6c757d; + background-color: #fff; +} + +.list-group-item.active { + z-index: 2; + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.list-group-flush .list-group-item { + border-right: 0; + border-left: 0; + border-radius: 0; +} + +.list-group-flush:first-child .list-group-item:first-child { + border-top: 0; +} + +.list-group-flush:last-child .list-group-item:last-child { + border-bottom: 0; +} + +.list-group-item-primary { + color: #004085; + background-color: #b8daff; +} + +.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { + color: #004085; + background-color: #9fcdff; +} + +.list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #004085; + border-color: #004085; +} + +.list-group-item-secondary { + color: #383d41; + background-color: #d6d8db; +} + +.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { + color: #383d41; + background-color: #c8cbcf; +} + +.list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #383d41; + border-color: #383d41; +} + +.list-group-item-success { + color: #155724; + background-color: #c3e6cb; +} + +.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { + color: #155724; + background-color: #b1dfbb; +} + +.list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #155724; + border-color: #155724; +} + +.list-group-item-info { + color: #0c5460; + background-color: #bee5eb; +} + +.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { + color: #0c5460; + background-color: #abdde5; +} + +.list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #0c5460; + border-color: #0c5460; +} + +.list-group-item-warning { + color: #856404; + background-color: #ffeeba; +} + +.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { + color: #856404; + background-color: #ffe8a1; +} + +.list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #856404; + border-color: #856404; +} + +.list-group-item-danger { + color: #721c24; + background-color: #f5c6cb; +} + +.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { + color: #721c24; + background-color: #f1b0b7; +} + +.list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #721c24; + border-color: #721c24; +} + +.list-group-item-light { + color: #818182; + background-color: #fdfdfe; +} + +.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { + color: #818182; + background-color: #ececf6; +} + +.list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #818182; + border-color: #818182; +} + +.list-group-item-dark { + color: #1b1e21; + background-color: #c6c8ca; +} + +.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { + color: #1b1e21; + background-color: #b9bbbe; +} + +.list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #1b1e21; + border-color: #1b1e21; +} + +.close { + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5; +} + +.close:hover, .close:focus { + color: #000; + text-decoration: none; + opacity: .75; +} + +.close:not(:disabled):not(.disabled) { + cursor: pointer; +} + +button.close { + padding: 0; + background-color: transparent; + border: 0; + -webkit-appearance: none; +} + +.modal-open { + overflow: hidden; +} + +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + outline: 0; +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} + +.modal-dialog { + position: relative; + width: auto; + margin: 0.5rem; + pointer-events: none; +} + +.modal.fade .modal-dialog { + transition: transform 0.3s ease-out; + transform: translate(0, -25%); +} + +.modal.show .modal-dialog { + transform: translate(0, 0); +} + +.modal-dialog-centered { + display: flex; + align-items: center; + min-height: calc(100% - (0.5rem * 2)); +} + +.modal-content { + position: relative; + display: flex; + flex-direction: column; + width: 100%; + pointer-events: auto; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; + outline: 0; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop.show { + opacity: 0.5; +} + +.modal-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: 1rem; + border-bottom: 1px solid #e9ecef; + border-top-left-radius: 0.3rem; + border-top-right-radius: 0.3rem; +} + +.modal-header .close { + padding: 1rem; + margin: -1rem -1rem -1rem auto; +} + +.modal-title { + margin-bottom: 0; + line-height: 1.5; +} + +.modal-body { + position: relative; + flex: 1 1 auto; + padding: 1rem; +} + +.modal-footer { + display: flex; + align-items: center; + justify-content: flex-end; + padding: 1rem; + border-top: 1px solid #e9ecef; +} + +.modal-footer > :not(:first-child) { + margin-left: .25rem; +} + +.modal-footer > :not(:last-child) { + margin-right: .25rem; +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +@media (min-width: 576px) { + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto; + } + .modal-dialog-centered { + min-height: calc(100% - (1.75rem * 2)); + } + .modal-sm { + max-width: 300px; + } +} + +@media (min-width: 992px) { + .modal-lg { + max-width: 800px; + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + opacity: 0; +} + +.tooltip.show { + opacity: 0.9; +} + +.tooltip .arrow { + position: absolute; + display: block; + width: 0.8rem; + height: 0.4rem; +} + +.tooltip .arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { + padding: 0.4rem 0; +} + +.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { + bottom: 0; +} + +.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { + top: 0; + border-width: 0.4rem 0.4rem 0; + border-top-color: #000; +} + +.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { + padding: 0 0.4rem; +} + +.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { + right: 0; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #000; +} + +.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { + padding: 0.4rem 0; +} + +.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { + top: 0; +} + +.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { + bottom: 0; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #000; +} + +.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { + padding: 0 0.4rem; +} + +.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { + left: 0; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #000; +} + +.tooltip-inner { + max-width: 200px; + padding: 0.25rem 0.5rem; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 0.25rem; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: block; + max-width: 276px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; +} + +.popover .arrow { + position: absolute; + display: block; + width: 1rem; + height: 0.5rem; + margin: 0 0.3rem; +} + +.popover .arrow::before, .popover .arrow::after { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-popover-top, .bs-popover-auto[x-placement^="top"] { + margin-bottom: 0.5rem; +} + +.bs-popover-top .arrow, .bs-popover-auto[x-placement^="top"] .arrow { + bottom: calc((0.5rem + 1px) * -1); +} + +.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^="top"] .arrow::before, +.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^="top"] .arrow::after { + border-width: 0.5rem 0.5rem 0; +} + +.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^="top"] .arrow::before { + bottom: 0; + border-top-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^="top"] .arrow::after { + bottom: 1px; + border-top-color: #fff; +} + +.bs-popover-right, .bs-popover-auto[x-placement^="right"] { + margin-left: 0.5rem; +} + +.bs-popover-right .arrow, .bs-popover-auto[x-placement^="right"] .arrow { + left: calc((0.5rem + 1px) * -1); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} + +.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^="right"] .arrow::before, +.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^="right"] .arrow::after { + border-width: 0.5rem 0.5rem 0.5rem 0; +} + +.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^="right"] .arrow::before { + left: 0; + border-right-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^="right"] .arrow::after { + left: 1px; + border-right-color: #fff; +} + +.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { + margin-top: 0.5rem; +} + +.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^="bottom"] .arrow { + top: calc((0.5rem + 1px) * -1); +} + +.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^="bottom"] .arrow::before, +.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-width: 0 0.5rem 0.5rem 0.5rem; +} + +.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^="bottom"] .arrow::before { + top: 0; + border-bottom-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^="bottom"] .arrow::after { + top: 1px; + border-bottom-color: #fff; +} + +.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -0.5rem; + content: ""; + border-bottom: 1px solid #f7f7f7; +} + +.bs-popover-left, .bs-popover-auto[x-placement^="left"] { + margin-right: 0.5rem; +} + +.bs-popover-left .arrow, .bs-popover-auto[x-placement^="left"] .arrow { + right: calc((0.5rem + 1px) * -1); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} + +.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^="left"] .arrow::before, +.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^="left"] .arrow::after { + border-width: 0.5rem 0 0.5rem 0.5rem; +} + +.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^="left"] .arrow::before { + right: 0; + border-left-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^="left"] .arrow::after { + right: 1px; + border-left-color: #fff; +} + +.popover-header { + padding: 0.5rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + color: inherit; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); +} + +.popover-header:empty { + display: none; +} + +.popover-body { + padding: 0.5rem 0.75rem; + color: #212529; +} + +.carousel { + position: relative; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-item { + position: relative; + display: none; + align-items: center; + width: 100%; + transition: transform 0.6s ease; + backface-visibility: hidden; + perspective: 1000px; +} + +.carousel-item.active, +.carousel-item-next, +.carousel-item-prev { + display: block; +} + +.carousel-item-next, +.carousel-item-prev { + position: absolute; + top: 0; +} + +.carousel-item-next.carousel-item-left, +.carousel-item-prev.carousel-item-right { + transform: translateX(0); +} + +@supports (transform-style: preserve-3d) { + .carousel-item-next.carousel-item-left, + .carousel-item-prev.carousel-item-right { + transform: translate3d(0, 0, 0); + } +} + +.carousel-item-next, +.active.carousel-item-right { + transform: translateX(100%); +} + +@supports (transform-style: preserve-3d) { + .carousel-item-next, + .active.carousel-item-right { + transform: translate3d(100%, 0, 0); + } +} + +.carousel-item-prev, +.active.carousel-item-left { + transform: translateX(-100%); +} + +@supports (transform-style: preserve-3d) { + .carousel-item-prev, + .active.carousel-item-left { + transform: translate3d(-100%, 0, 0); + } +} + +.carousel-control-prev, +.carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + width: 15%; + color: #fff; + text-align: center; + opacity: 0.5; +} + +.carousel-control-prev:hover, .carousel-control-prev:focus, +.carousel-control-next:hover, +.carousel-control-next:focus { + color: #fff; + text-decoration: none; + outline: 0; + opacity: .9; +} + +.carousel-control-prev { + left: 0; +} + +.carousel-control-next { + right: 0; +} + +.carousel-control-prev-icon, +.carousel-control-next-icon { + display: inline-block; + width: 20px; + height: 20px; + background: transparent no-repeat center center; + background-size: 100% 100%; +} + +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"); +} + +.carousel-control-next-icon { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"); +} + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 10px; + left: 0; + z-index: 15; + display: flex; + justify-content: center; + padding-left: 0; + margin-right: 15%; + margin-left: 15%; + list-style: none; +} + +.carousel-indicators li { + position: relative; + flex: 0 1 auto; + width: 30px; + height: 3px; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + background-color: rgba(255, 255, 255, 0.5); +} + +.carousel-indicators li::before { + position: absolute; + top: -10px; + left: 0; + display: inline-block; + width: 100%; + height: 10px; + content: ""; +} + +.carousel-indicators li::after { + position: absolute; + bottom: -10px; + left: 0; + display: inline-block; + width: 100%; + height: 10px; + content: ""; +} + +.carousel-indicators .active { + background-color: #fff; +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; +} + +.align-baseline { + vertical-align: baseline !important; +} + +.align-top { + vertical-align: top !important; +} + +.align-middle { + vertical-align: middle !important; +} + +.align-bottom { + vertical-align: bottom !important; +} + +.align-text-bottom { + vertical-align: text-bottom !important; +} + +.align-text-top { + vertical-align: text-top !important; +} + +.bg-primary { + background-color: #007bff !important; +} + +a.bg-primary:hover, a.bg-primary:focus, +button.bg-primary:hover, +button.bg-primary:focus { + background-color: #0062cc !important; +} + +.bg-secondary { + background-color: #6c757d !important; +} + +a.bg-secondary:hover, a.bg-secondary:focus, +button.bg-secondary:hover, +button.bg-secondary:focus { + background-color: #545b62 !important; +} + +.bg-success { + background-color: #28a745 !important; +} + +a.bg-success:hover, a.bg-success:focus, +button.bg-success:hover, +button.bg-success:focus { + background-color: #1e7e34 !important; +} + +.bg-info { + background-color: #17a2b8 !important; +} + +a.bg-info:hover, a.bg-info:focus, +button.bg-info:hover, +button.bg-info:focus { + background-color: #117a8b !important; +} + +.bg-warning { + background-color: #ffc107 !important; +} + +a.bg-warning:hover, a.bg-warning:focus, +button.bg-warning:hover, +button.bg-warning:focus { + background-color: #d39e00 !important; +} + +.bg-danger { + background-color: #dc3545 !important; +} + +a.bg-danger:hover, a.bg-danger:focus, +button.bg-danger:hover, +button.bg-danger:focus { + background-color: #bd2130 !important; +} + +.bg-light { + background-color: #f8f9fa !important; +} + +a.bg-light:hover, a.bg-light:focus, +button.bg-light:hover, +button.bg-light:focus { + background-color: #dae0e5 !important; +} + +.bg-dark { + background-color: #343a40 !important; +} + +a.bg-dark:hover, a.bg-dark:focus, +button.bg-dark:hover, +button.bg-dark:focus { + background-color: #1d2124 !important; +} + +.bg-white { + background-color: #fff !important; +} + +.bg-transparent { + background-color: transparent !important; +} + +.border { + border: 1px solid #dee2e6 !important; +} + +.border-top { + border-top: 1px solid #dee2e6 !important; +} + +.border-right { + border-right: 1px solid #dee2e6 !important; +} + +.border-bottom { + border-bottom: 1px solid #dee2e6 !important; +} + +.border-left { + border-left: 1px solid #dee2e6 !important; +} + +.border-0 { + border: 0 !important; +} + +.border-top-0 { + border-top: 0 !important; +} + +.border-right-0 { + border-right: 0 !important; +} + +.border-bottom-0 { + border-bottom: 0 !important; +} + +.border-left-0 { + border-left: 0 !important; +} + +.border-primary { + border-color: #007bff !important; +} + +.border-secondary { + border-color: #6c757d !important; +} + +.border-success { + border-color: #28a745 !important; +} + +.border-info { + border-color: #17a2b8 !important; +} + +.border-warning { + border-color: #ffc107 !important; +} + +.border-danger { + border-color: #dc3545 !important; +} + +.border-light { + border-color: #f8f9fa !important; +} + +.border-dark { + border-color: #343a40 !important; +} + +.border-white { + border-color: #fff !important; +} + +.rounded { + border-radius: 0.25rem !important; +} + +.rounded-top { + border-top-left-radius: 0.25rem !important; + border-top-right-radius: 0.25rem !important; +} + +.rounded-right { + border-top-right-radius: 0.25rem !important; + border-bottom-right-radius: 0.25rem !important; +} + +.rounded-bottom { + border-bottom-right-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; +} + +.rounded-left { + border-top-left-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; +} + +.rounded-circle { + border-radius: 50% !important; +} + +.rounded-0 { + border-radius: 0 !important; +} + +.clearfix::after { + display: block; + clear: both; + content: ""; +} + +.d-none { + display: none !important; +} + +.d-inline { + display: inline !important; +} + +.d-inline-block { + display: inline-block !important; +} + +.d-block { + display: block !important; +} + +.d-table { + display: table !important; +} + +.d-table-row { + display: table-row !important; +} + +.d-table-cell { + display: table-cell !important; +} + +.d-flex { + display: flex !important; +} + +.d-inline-flex { + display: inline-flex !important; +} + +@media (min-width: 576px) { + .d-sm-none { + display: none !important; + } + .d-sm-inline { + display: inline !important; + } + .d-sm-inline-block { + display: inline-block !important; + } + .d-sm-block { + display: block !important; + } + .d-sm-table { + display: table !important; + } + .d-sm-table-row { + display: table-row !important; + } + .d-sm-table-cell { + display: table-cell !important; + } + .d-sm-flex { + display: flex !important; + } + .d-sm-inline-flex { + display: inline-flex !important; + } +} + +@media (min-width: 768px) { + .d-md-none { + display: none !important; + } + .d-md-inline { + display: inline !important; + } + .d-md-inline-block { + display: inline-block !important; + } + .d-md-block { + display: block !important; + } + .d-md-table { + display: table !important; + } + .d-md-table-row { + display: table-row !important; + } + .d-md-table-cell { + display: table-cell !important; + } + .d-md-flex { + display: flex !important; + } + .d-md-inline-flex { + display: inline-flex !important; + } +} + +@media (min-width: 992px) { + .d-lg-none { + display: none !important; + } + .d-lg-inline { + display: inline !important; + } + .d-lg-inline-block { + display: inline-block !important; + } + .d-lg-block { + display: block !important; + } + .d-lg-table { + display: table !important; + } + .d-lg-table-row { + display: table-row !important; + } + .d-lg-table-cell { + display: table-cell !important; + } + .d-lg-flex { + display: flex !important; + } + .d-lg-inline-flex { + display: inline-flex !important; + } +} + +@media (min-width: 1200px) { + .d-xl-none { + display: none !important; + } + .d-xl-inline { + display: inline !important; + } + .d-xl-inline-block { + display: inline-block !important; + } + .d-xl-block { + display: block !important; + } + .d-xl-table { + display: table !important; + } + .d-xl-table-row { + display: table-row !important; + } + .d-xl-table-cell { + display: table-cell !important; + } + .d-xl-flex { + display: flex !important; + } + .d-xl-inline-flex { + display: inline-flex !important; + } +} + +@media print { + .d-print-none { + display: none !important; + } + .d-print-inline { + display: inline !important; + } + .d-print-inline-block { + display: inline-block !important; + } + .d-print-block { + display: block !important; + } + .d-print-table { + display: table !important; + } + .d-print-table-row { + display: table-row !important; + } + .d-print-table-cell { + display: table-cell !important; + } + .d-print-flex { + display: flex !important; + } + .d-print-inline-flex { + display: inline-flex !important; + } +} + +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden; +} + +.embed-responsive::before { + display: block; + content: ""; +} + +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} + +.embed-responsive-21by9::before { + padding-top: 42.85714%; +} + +.embed-responsive-16by9::before { + padding-top: 56.25%; +} + +.embed-responsive-4by3::before { + padding-top: 75%; +} + +.embed-responsive-1by1::before { + padding-top: 100%; +} + +.flex-row { + flex-direction: row !important; +} + +.flex-column { + flex-direction: column !important; +} + +.flex-row-reverse { + flex-direction: row-reverse !important; +} + +.flex-column-reverse { + flex-direction: column-reverse !important; +} + +.flex-wrap { + flex-wrap: wrap !important; +} + +.flex-nowrap { + flex-wrap: nowrap !important; +} + +.flex-wrap-reverse { + flex-wrap: wrap-reverse !important; +} + +.justify-content-start { + justify-content: flex-start !important; +} + +.justify-content-end { + justify-content: flex-end !important; +} + +.justify-content-center { + justify-content: center !important; +} + +.justify-content-between { + justify-content: space-between !important; +} + +.justify-content-around { + justify-content: space-around !important; +} + +.align-items-start { + align-items: flex-start !important; +} + +.align-items-end { + align-items: flex-end !important; +} + +.align-items-center { + align-items: center !important; +} + +.align-items-baseline { + align-items: baseline !important; +} + +.align-items-stretch { + align-items: stretch !important; +} + +.align-content-start { + align-content: flex-start !important; +} + +.align-content-end { + align-content: flex-end !important; +} + +.align-content-center { + align-content: center !important; +} + +.align-content-between { + align-content: space-between !important; +} + +.align-content-around { + align-content: space-around !important; +} + +.align-content-stretch { + align-content: stretch !important; +} + +.align-self-auto { + align-self: auto !important; +} + +.align-self-start { + align-self: flex-start !important; +} + +.align-self-end { + align-self: flex-end !important; +} + +.align-self-center { + align-self: center !important; +} + +.align-self-baseline { + align-self: baseline !important; +} + +.align-self-stretch { + align-self: stretch !important; +} + +@media (min-width: 576px) { + .flex-sm-row { + flex-direction: row !important; + } + .flex-sm-column { + flex-direction: column !important; + } + .flex-sm-row-reverse { + flex-direction: row-reverse !important; + } + .flex-sm-column-reverse { + flex-direction: column-reverse !important; + } + .flex-sm-wrap { + flex-wrap: wrap !important; + } + .flex-sm-nowrap { + flex-wrap: nowrap !important; + } + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + .justify-content-sm-start { + justify-content: flex-start !important; + } + .justify-content-sm-end { + justify-content: flex-end !important; + } + .justify-content-sm-center { + justify-content: center !important; + } + .justify-content-sm-between { + justify-content: space-between !important; + } + .justify-content-sm-around { + justify-content: space-around !important; + } + .align-items-sm-start { + align-items: flex-start !important; + } + .align-items-sm-end { + align-items: flex-end !important; + } + .align-items-sm-center { + align-items: center !important; + } + .align-items-sm-baseline { + align-items: baseline !important; + } + .align-items-sm-stretch { + align-items: stretch !important; + } + .align-content-sm-start { + align-content: flex-start !important; + } + .align-content-sm-end { + align-content: flex-end !important; + } + .align-content-sm-center { + align-content: center !important; + } + .align-content-sm-between { + align-content: space-between !important; + } + .align-content-sm-around { + align-content: space-around !important; + } + .align-content-sm-stretch { + align-content: stretch !important; + } + .align-self-sm-auto { + align-self: auto !important; + } + .align-self-sm-start { + align-self: flex-start !important; + } + .align-self-sm-end { + align-self: flex-end !important; + } + .align-self-sm-center { + align-self: center !important; + } + .align-self-sm-baseline { + align-self: baseline !important; + } + .align-self-sm-stretch { + align-self: stretch !important; + } +} + +@media (min-width: 768px) { + .flex-md-row { + flex-direction: row !important; + } + .flex-md-column { + flex-direction: column !important; + } + .flex-md-row-reverse { + flex-direction: row-reverse !important; + } + .flex-md-column-reverse { + flex-direction: column-reverse !important; + } + .flex-md-wrap { + flex-wrap: wrap !important; + } + .flex-md-nowrap { + flex-wrap: nowrap !important; + } + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + .justify-content-md-start { + justify-content: flex-start !important; + } + .justify-content-md-end { + justify-content: flex-end !important; + } + .justify-content-md-center { + justify-content: center !important; + } + .justify-content-md-between { + justify-content: space-between !important; + } + .justify-content-md-around { + justify-content: space-around !important; + } + .align-items-md-start { + align-items: flex-start !important; + } + .align-items-md-end { + align-items: flex-end !important; + } + .align-items-md-center { + align-items: center !important; + } + .align-items-md-baseline { + align-items: baseline !important; + } + .align-items-md-stretch { + align-items: stretch !important; + } + .align-content-md-start { + align-content: flex-start !important; + } + .align-content-md-end { + align-content: flex-end !important; + } + .align-content-md-center { + align-content: center !important; + } + .align-content-md-between { + align-content: space-between !important; + } + .align-content-md-around { + align-content: space-around !important; + } + .align-content-md-stretch { + align-content: stretch !important; + } + .align-self-md-auto { + align-self: auto !important; + } + .align-self-md-start { + align-self: flex-start !important; + } + .align-self-md-end { + align-self: flex-end !important; + } + .align-self-md-center { + align-self: center !important; + } + .align-self-md-baseline { + align-self: baseline !important; + } + .align-self-md-stretch { + align-self: stretch !important; + } +} + +@media (min-width: 992px) { + .flex-lg-row { + flex-direction: row !important; + } + .flex-lg-column { + flex-direction: column !important; + } + .flex-lg-row-reverse { + flex-direction: row-reverse !important; + } + .flex-lg-column-reverse { + flex-direction: column-reverse !important; + } + .flex-lg-wrap { + flex-wrap: wrap !important; + } + .flex-lg-nowrap { + flex-wrap: nowrap !important; + } + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + .justify-content-lg-start { + justify-content: flex-start !important; + } + .justify-content-lg-end { + justify-content: flex-end !important; + } + .justify-content-lg-center { + justify-content: center !important; + } + .justify-content-lg-between { + justify-content: space-between !important; + } + .justify-content-lg-around { + justify-content: space-around !important; + } + .align-items-lg-start { + align-items: flex-start !important; + } + .align-items-lg-end { + align-items: flex-end !important; + } + .align-items-lg-center { + align-items: center !important; + } + .align-items-lg-baseline { + align-items: baseline !important; + } + .align-items-lg-stretch { + align-items: stretch !important; + } + .align-content-lg-start { + align-content: flex-start !important; + } + .align-content-lg-end { + align-content: flex-end !important; + } + .align-content-lg-center { + align-content: center !important; + } + .align-content-lg-between { + align-content: space-between !important; + } + .align-content-lg-around { + align-content: space-around !important; + } + .align-content-lg-stretch { + align-content: stretch !important; + } + .align-self-lg-auto { + align-self: auto !important; + } + .align-self-lg-start { + align-self: flex-start !important; + } + .align-self-lg-end { + align-self: flex-end !important; + } + .align-self-lg-center { + align-self: center !important; + } + .align-self-lg-baseline { + align-self: baseline !important; + } + .align-self-lg-stretch { + align-self: stretch !important; + } +} + +@media (min-width: 1200px) { + .flex-xl-row { + flex-direction: row !important; + } + .flex-xl-column { + flex-direction: column !important; + } + .flex-xl-row-reverse { + flex-direction: row-reverse !important; + } + .flex-xl-column-reverse { + flex-direction: column-reverse !important; + } + .flex-xl-wrap { + flex-wrap: wrap !important; + } + .flex-xl-nowrap { + flex-wrap: nowrap !important; + } + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + .justify-content-xl-start { + justify-content: flex-start !important; + } + .justify-content-xl-end { + justify-content: flex-end !important; + } + .justify-content-xl-center { + justify-content: center !important; + } + .justify-content-xl-between { + justify-content: space-between !important; + } + .justify-content-xl-around { + justify-content: space-around !important; + } + .align-items-xl-start { + align-items: flex-start !important; + } + .align-items-xl-end { + align-items: flex-end !important; + } + .align-items-xl-center { + align-items: center !important; + } + .align-items-xl-baseline { + align-items: baseline !important; + } + .align-items-xl-stretch { + align-items: stretch !important; + } + .align-content-xl-start { + align-content: flex-start !important; + } + .align-content-xl-end { + align-content: flex-end !important; + } + .align-content-xl-center { + align-content: center !important; + } + .align-content-xl-between { + align-content: space-between !important; + } + .align-content-xl-around { + align-content: space-around !important; + } + .align-content-xl-stretch { + align-content: stretch !important; + } + .align-self-xl-auto { + align-self: auto !important; + } + .align-self-xl-start { + align-self: flex-start !important; + } + .align-self-xl-end { + align-self: flex-end !important; + } + .align-self-xl-center { + align-self: center !important; + } + .align-self-xl-baseline { + align-self: baseline !important; + } + .align-self-xl-stretch { + align-self: stretch !important; + } +} + +.float-left { + float: left !important; +} + +.float-right { + float: right !important; +} + +.float-none { + float: none !important; +} + +@media (min-width: 576px) { + .float-sm-left { + float: left !important; + } + .float-sm-right { + float: right !important; + } + .float-sm-none { + float: none !important; + } +} + +@media (min-width: 768px) { + .float-md-left { + float: left !important; + } + .float-md-right { + float: right !important; + } + .float-md-none { + float: none !important; + } +} + +@media (min-width: 992px) { + .float-lg-left { + float: left !important; + } + .float-lg-right { + float: right !important; + } + .float-lg-none { + float: none !important; + } +} + +@media (min-width: 1200px) { + .float-xl-left { + float: left !important; + } + .float-xl-right { + float: right !important; + } + .float-xl-none { + float: none !important; + } +} + +.position-static { + position: static !important; +} + +.position-relative { + position: relative !important; +} + +.position-absolute { + position: absolute !important; +} + +.position-fixed { + position: fixed !important; +} + +.position-sticky { + position: sticky !important; +} + +.fixed-top { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; +} + +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; +} + +@supports (position: sticky) { + .sticky-top { + position: sticky; + top: 0; + z-index: 1020; + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + clip-path: inset(50%); + border: 0; +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal; + clip-path: none; +} + +.w-25 { + width: 25% !important; +} + +.w-50 { + width: 50% !important; +} + +.w-75 { + width: 75% !important; +} + +.w-100 { + width: 100% !important; +} + +.h-25 { + height: 25% !important; +} + +.h-50 { + height: 50% !important; +} + +.h-75 { + height: 75% !important; +} + +.h-100 { + height: 100% !important; +} + +.mw-100 { + max-width: 100% !important; +} + +.mh-100 { + max-height: 100% !important; +} + +.m-0 { + margin: 0 !important; +} + +.mt-0, +.my-0 { + margin-top: 0 !important; +} + +.mr-0, +.mx-0 { + margin-right: 0 !important; +} + +.mb-0, +.my-0 { + margin-bottom: 0 !important; +} + +.ml-0, +.mx-0 { + margin-left: 0 !important; +} + +.m-1 { + margin: 0.25rem !important; +} + +.mt-1, +.my-1 { + margin-top: 0.25rem !important; +} + +.mr-1, +.mx-1 { + margin-right: 0.25rem !important; +} + +.mb-1, +.my-1 { + margin-bottom: 0.25rem !important; +} + +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; +} + +.m-2 { + margin: 0.5rem !important; +} + +.mt-2, +.my-2 { + margin-top: 0.5rem !important; +} + +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; +} + +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; +} + +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; +} + +.m-3 { + margin: 1rem !important; +} + +.mt-3, +.my-3 { + margin-top: 1rem !important; +} + +.mr-3, +.mx-3 { + margin-right: 1rem !important; +} + +.mb-3, +.my-3 { + margin-bottom: 1rem !important; +} + +.ml-3, +.mx-3 { + margin-left: 1rem !important; +} + +.m-4 { + margin: 1.5rem !important; +} + +.mt-4, +.my-4 { + margin-top: 1.5rem !important; +} + +.mr-4, +.mx-4 { + margin-right: 1.5rem !important; +} + +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; +} + +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; +} + +.m-5 { + margin: 3rem !important; +} + +.mt-5, +.my-5 { + margin-top: 3rem !important; +} + +.mr-5, +.mx-5 { + margin-right: 3rem !important; +} + +.mb-5, +.my-5 { + margin-bottom: 3rem !important; +} + +.ml-5, +.mx-5 { + margin-left: 3rem !important; +} + +.p-0 { + padding: 0 !important; +} + +.pt-0, +.py-0 { + padding-top: 0 !important; +} + +.pr-0, +.px-0 { + padding-right: 0 !important; +} + +.pb-0, +.py-0 { + padding-bottom: 0 !important; +} + +.pl-0, +.px-0 { + padding-left: 0 !important; +} + +.p-1 { + padding: 0.25rem !important; +} + +.pt-1, +.py-1 { + padding-top: 0.25rem !important; +} + +.pr-1, +.px-1 { + padding-right: 0.25rem !important; +} + +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; +} + +.pl-1, +.px-1 { + padding-left: 0.25rem !important; +} + +.p-2 { + padding: 0.5rem !important; +} + +.pt-2, +.py-2 { + padding-top: 0.5rem !important; +} + +.pr-2, +.px-2 { + padding-right: 0.5rem !important; +} + +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; +} + +.pl-2, +.px-2 { + padding-left: 0.5rem !important; +} + +.p-3 { + padding: 1rem !important; +} + +.pt-3, +.py-3 { + padding-top: 1rem !important; +} + +.pr-3, +.px-3 { + padding-right: 1rem !important; +} + +.pb-3, +.py-3 { + padding-bottom: 1rem !important; +} + +.pl-3, +.px-3 { + padding-left: 1rem !important; +} + +.p-4 { + padding: 1.5rem !important; +} + +.pt-4, +.py-4 { + padding-top: 1.5rem !important; +} + +.pr-4, +.px-4 { + padding-right: 1.5rem !important; +} + +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; +} + +.pl-4, +.px-4 { + padding-left: 1.5rem !important; +} + +.p-5 { + padding: 3rem !important; +} + +.pt-5, +.py-5 { + padding-top: 3rem !important; +} + +.pr-5, +.px-5 { + padding-right: 3rem !important; +} + +.pb-5, +.py-5 { + padding-bottom: 3rem !important; +} + +.pl-5, +.px-5 { + padding-left: 3rem !important; +} + +.m-auto { + margin: auto !important; +} + +.mt-auto, +.my-auto { + margin-top: auto !important; +} + +.mr-auto, +.mx-auto { + margin-right: auto !important; +} + +.mb-auto, +.my-auto { + margin-bottom: auto !important; +} + +.ml-auto, +.mx-auto { + margin-left: auto !important; +} + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important; + } + .mt-sm-0, + .my-sm-0 { + margin-top: 0 !important; + } + .mr-sm-0, + .mx-sm-0 { + margin-right: 0 !important; + } + .mb-sm-0, + .my-sm-0 { + margin-bottom: 0 !important; + } + .ml-sm-0, + .mx-sm-0 { + margin-left: 0 !important; + } + .m-sm-1 { + margin: 0.25rem !important; + } + .mt-sm-1, + .my-sm-1 { + margin-top: 0.25rem !important; + } + .mr-sm-1, + .mx-sm-1 { + margin-right: 0.25rem !important; + } + .mb-sm-1, + .my-sm-1 { + margin-bottom: 0.25rem !important; + } + .ml-sm-1, + .mx-sm-1 { + margin-left: 0.25rem !important; + } + .m-sm-2 { + margin: 0.5rem !important; + } + .mt-sm-2, + .my-sm-2 { + margin-top: 0.5rem !important; + } + .mr-sm-2, + .mx-sm-2 { + margin-right: 0.5rem !important; + } + .mb-sm-2, + .my-sm-2 { + margin-bottom: 0.5rem !important; + } + .ml-sm-2, + .mx-sm-2 { + margin-left: 0.5rem !important; + } + .m-sm-3 { + margin: 1rem !important; + } + .mt-sm-3, + .my-sm-3 { + margin-top: 1rem !important; + } + .mr-sm-3, + .mx-sm-3 { + margin-right: 1rem !important; + } + .mb-sm-3, + .my-sm-3 { + margin-bottom: 1rem !important; + } + .ml-sm-3, + .mx-sm-3 { + margin-left: 1rem !important; + } + .m-sm-4 { + margin: 1.5rem !important; + } + .mt-sm-4, + .my-sm-4 { + margin-top: 1.5rem !important; + } + .mr-sm-4, + .mx-sm-4 { + margin-right: 1.5rem !important; + } + .mb-sm-4, + .my-sm-4 { + margin-bottom: 1.5rem !important; + } + .ml-sm-4, + .mx-sm-4 { + margin-left: 1.5rem !important; + } + .m-sm-5 { + margin: 3rem !important; + } + .mt-sm-5, + .my-sm-5 { + margin-top: 3rem !important; + } + .mr-sm-5, + .mx-sm-5 { + margin-right: 3rem !important; + } + .mb-sm-5, + .my-sm-5 { + margin-bottom: 3rem !important; + } + .ml-sm-5, + .mx-sm-5 { + margin-left: 3rem !important; + } + .p-sm-0 { + padding: 0 !important; + } + .pt-sm-0, + .py-sm-0 { + padding-top: 0 !important; + } + .pr-sm-0, + .px-sm-0 { + padding-right: 0 !important; + } + .pb-sm-0, + .py-sm-0 { + padding-bottom: 0 !important; + } + .pl-sm-0, + .px-sm-0 { + padding-left: 0 !important; + } + .p-sm-1 { + padding: 0.25rem !important; + } + .pt-sm-1, + .py-sm-1 { + padding-top: 0.25rem !important; + } + .pr-sm-1, + .px-sm-1 { + padding-right: 0.25rem !important; + } + .pb-sm-1, + .py-sm-1 { + padding-bottom: 0.25rem !important; + } + .pl-sm-1, + .px-sm-1 { + padding-left: 0.25rem !important; + } + .p-sm-2 { + padding: 0.5rem !important; + } + .pt-sm-2, + .py-sm-2 { + padding-top: 0.5rem !important; + } + .pr-sm-2, + .px-sm-2 { + padding-right: 0.5rem !important; + } + .pb-sm-2, + .py-sm-2 { + padding-bottom: 0.5rem !important; + } + .pl-sm-2, + .px-sm-2 { + padding-left: 0.5rem !important; + } + .p-sm-3 { + padding: 1rem !important; + } + .pt-sm-3, + .py-sm-3 { + padding-top: 1rem !important; + } + .pr-sm-3, + .px-sm-3 { + padding-right: 1rem !important; + } + .pb-sm-3, + .py-sm-3 { + padding-bottom: 1rem !important; + } + .pl-sm-3, + .px-sm-3 { + padding-left: 1rem !important; + } + .p-sm-4 { + padding: 1.5rem !important; + } + .pt-sm-4, + .py-sm-4 { + padding-top: 1.5rem !important; + } + .pr-sm-4, + .px-sm-4 { + padding-right: 1.5rem !important; + } + .pb-sm-4, + .py-sm-4 { + padding-bottom: 1.5rem !important; + } + .pl-sm-4, + .px-sm-4 { + padding-left: 1.5rem !important; + } + .p-sm-5 { + padding: 3rem !important; + } + .pt-sm-5, + .py-sm-5 { + padding-top: 3rem !important; + } + .pr-sm-5, + .px-sm-5 { + padding-right: 3rem !important; + } + .pb-sm-5, + .py-sm-5 { + padding-bottom: 3rem !important; + } + .pl-sm-5, + .px-sm-5 { + padding-left: 3rem !important; + } + .m-sm-auto { + margin: auto !important; + } + .mt-sm-auto, + .my-sm-auto { + margin-top: auto !important; + } + .mr-sm-auto, + .mx-sm-auto { + margin-right: auto !important; + } + .mb-sm-auto, + .my-sm-auto { + margin-bottom: auto !important; + } + .ml-sm-auto, + .mx-sm-auto { + margin-left: auto !important; + } +} + +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important; + } + .mt-md-0, + .my-md-0 { + margin-top: 0 !important; + } + .mr-md-0, + .mx-md-0 { + margin-right: 0 !important; + } + .mb-md-0, + .my-md-0 { + margin-bottom: 0 !important; + } + .ml-md-0, + .mx-md-0 { + margin-left: 0 !important; + } + .m-md-1 { + margin: 0.25rem !important; + } + .mt-md-1, + .my-md-1 { + margin-top: 0.25rem !important; + } + .mr-md-1, + .mx-md-1 { + margin-right: 0.25rem !important; + } + .mb-md-1, + .my-md-1 { + margin-bottom: 0.25rem !important; + } + .ml-md-1, + .mx-md-1 { + margin-left: 0.25rem !important; + } + .m-md-2 { + margin: 0.5rem !important; + } + .mt-md-2, + .my-md-2 { + margin-top: 0.5rem !important; + } + .mr-md-2, + .mx-md-2 { + margin-right: 0.5rem !important; + } + .mb-md-2, + .my-md-2 { + margin-bottom: 0.5rem !important; + } + .ml-md-2, + .mx-md-2 { + margin-left: 0.5rem !important; + } + .m-md-3 { + margin: 1rem !important; + } + .mt-md-3, + .my-md-3 { + margin-top: 1rem !important; + } + .mr-md-3, + .mx-md-3 { + margin-right: 1rem !important; + } + .mb-md-3, + .my-md-3 { + margin-bottom: 1rem !important; + } + .ml-md-3, + .mx-md-3 { + margin-left: 1rem !important; + } + .m-md-4 { + margin: 1.5rem !important; + } + .mt-md-4, + .my-md-4 { + margin-top: 1.5rem !important; + } + .mr-md-4, + .mx-md-4 { + margin-right: 1.5rem !important; + } + .mb-md-4, + .my-md-4 { + margin-bottom: 1.5rem !important; + } + .ml-md-4, + .mx-md-4 { + margin-left: 1.5rem !important; + } + .m-md-5 { + margin: 3rem !important; + } + .mt-md-5, + .my-md-5 { + margin-top: 3rem !important; + } + .mr-md-5, + .mx-md-5 { + margin-right: 3rem !important; + } + .mb-md-5, + .my-md-5 { + margin-bottom: 3rem !important; + } + .ml-md-5, + .mx-md-5 { + margin-left: 3rem !important; + } + .p-md-0 { + padding: 0 !important; + } + .pt-md-0, + .py-md-0 { + padding-top: 0 !important; + } + .pr-md-0, + .px-md-0 { + padding-right: 0 !important; + } + .pb-md-0, + .py-md-0 { + padding-bottom: 0 !important; + } + .pl-md-0, + .px-md-0 { + padding-left: 0 !important; + } + .p-md-1 { + padding: 0.25rem !important; + } + .pt-md-1, + .py-md-1 { + padding-top: 0.25rem !important; + } + .pr-md-1, + .px-md-1 { + padding-right: 0.25rem !important; + } + .pb-md-1, + .py-md-1 { + padding-bottom: 0.25rem !important; + } + .pl-md-1, + .px-md-1 { + padding-left: 0.25rem !important; + } + .p-md-2 { + padding: 0.5rem !important; + } + .pt-md-2, + .py-md-2 { + padding-top: 0.5rem !important; + } + .pr-md-2, + .px-md-2 { + padding-right: 0.5rem !important; + } + .pb-md-2, + .py-md-2 { + padding-bottom: 0.5rem !important; + } + .pl-md-2, + .px-md-2 { + padding-left: 0.5rem !important; + } + .p-md-3 { + padding: 1rem !important; + } + .pt-md-3, + .py-md-3 { + padding-top: 1rem !important; + } + .pr-md-3, + .px-md-3 { + padding-right: 1rem !important; + } + .pb-md-3, + .py-md-3 { + padding-bottom: 1rem !important; + } + .pl-md-3, + .px-md-3 { + padding-left: 1rem !important; + } + .p-md-4 { + padding: 1.5rem !important; + } + .pt-md-4, + .py-md-4 { + padding-top: 1.5rem !important; + } + .pr-md-4, + .px-md-4 { + padding-right: 1.5rem !important; + } + .pb-md-4, + .py-md-4 { + padding-bottom: 1.5rem !important; + } + .pl-md-4, + .px-md-4 { + padding-left: 1.5rem !important; + } + .p-md-5 { + padding: 3rem !important; + } + .pt-md-5, + .py-md-5 { + padding-top: 3rem !important; + } + .pr-md-5, + .px-md-5 { + padding-right: 3rem !important; + } + .pb-md-5, + .py-md-5 { + padding-bottom: 3rem !important; + } + .pl-md-5, + .px-md-5 { + padding-left: 3rem !important; + } + .m-md-auto { + margin: auto !important; + } + .mt-md-auto, + .my-md-auto { + margin-top: auto !important; + } + .mr-md-auto, + .mx-md-auto { + margin-right: auto !important; + } + .mb-md-auto, + .my-md-auto { + margin-bottom: auto !important; + } + .ml-md-auto, + .mx-md-auto { + margin-left: auto !important; + } +} + +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important; + } + .mt-lg-0, + .my-lg-0 { + margin-top: 0 !important; + } + .mr-lg-0, + .mx-lg-0 { + margin-right: 0 !important; + } + .mb-lg-0, + .my-lg-0 { + margin-bottom: 0 !important; + } + .ml-lg-0, + .mx-lg-0 { + margin-left: 0 !important; + } + .m-lg-1 { + margin: 0.25rem !important; + } + .mt-lg-1, + .my-lg-1 { + margin-top: 0.25rem !important; + } + .mr-lg-1, + .mx-lg-1 { + margin-right: 0.25rem !important; + } + .mb-lg-1, + .my-lg-1 { + margin-bottom: 0.25rem !important; + } + .ml-lg-1, + .mx-lg-1 { + margin-left: 0.25rem !important; + } + .m-lg-2 { + margin: 0.5rem !important; + } + .mt-lg-2, + .my-lg-2 { + margin-top: 0.5rem !important; + } + .mr-lg-2, + .mx-lg-2 { + margin-right: 0.5rem !important; + } + .mb-lg-2, + .my-lg-2 { + margin-bottom: 0.5rem !important; + } + .ml-lg-2, + .mx-lg-2 { + margin-left: 0.5rem !important; + } + .m-lg-3 { + margin: 1rem !important; + } + .mt-lg-3, + .my-lg-3 { + margin-top: 1rem !important; + } + .mr-lg-3, + .mx-lg-3 { + margin-right: 1rem !important; + } + .mb-lg-3, + .my-lg-3 { + margin-bottom: 1rem !important; + } + .ml-lg-3, + .mx-lg-3 { + margin-left: 1rem !important; + } + .m-lg-4 { + margin: 1.5rem !important; + } + .mt-lg-4, + .my-lg-4 { + margin-top: 1.5rem !important; + } + .mr-lg-4, + .mx-lg-4 { + margin-right: 1.5rem !important; + } + .mb-lg-4, + .my-lg-4 { + margin-bottom: 1.5rem !important; + } + .ml-lg-4, + .mx-lg-4 { + margin-left: 1.5rem !important; + } + .m-lg-5 { + margin: 3rem !important; + } + .mt-lg-5, + .my-lg-5 { + margin-top: 3rem !important; + } + .mr-lg-5, + .mx-lg-5 { + margin-right: 3rem !important; + } + .mb-lg-5, + .my-lg-5 { + margin-bottom: 3rem !important; + } + .ml-lg-5, + .mx-lg-5 { + margin-left: 3rem !important; + } + .p-lg-0 { + padding: 0 !important; + } + .pt-lg-0, + .py-lg-0 { + padding-top: 0 !important; + } + .pr-lg-0, + .px-lg-0 { + padding-right: 0 !important; + } + .pb-lg-0, + .py-lg-0 { + padding-bottom: 0 !important; + } + .pl-lg-0, + .px-lg-0 { + padding-left: 0 !important; + } + .p-lg-1 { + padding: 0.25rem !important; + } + .pt-lg-1, + .py-lg-1 { + padding-top: 0.25rem !important; + } + .pr-lg-1, + .px-lg-1 { + padding-right: 0.25rem !important; + } + .pb-lg-1, + .py-lg-1 { + padding-bottom: 0.25rem !important; + } + .pl-lg-1, + .px-lg-1 { + padding-left: 0.25rem !important; + } + .p-lg-2 { + padding: 0.5rem !important; + } + .pt-lg-2, + .py-lg-2 { + padding-top: 0.5rem !important; + } + .pr-lg-2, + .px-lg-2 { + padding-right: 0.5rem !important; + } + .pb-lg-2, + .py-lg-2 { + padding-bottom: 0.5rem !important; + } + .pl-lg-2, + .px-lg-2 { + padding-left: 0.5rem !important; + } + .p-lg-3 { + padding: 1rem !important; + } + .pt-lg-3, + .py-lg-3 { + padding-top: 1rem !important; + } + .pr-lg-3, + .px-lg-3 { + padding-right: 1rem !important; + } + .pb-lg-3, + .py-lg-3 { + padding-bottom: 1rem !important; + } + .pl-lg-3, + .px-lg-3 { + padding-left: 1rem !important; + } + .p-lg-4 { + padding: 1.5rem !important; + } + .pt-lg-4, + .py-lg-4 { + padding-top: 1.5rem !important; + } + .pr-lg-4, + .px-lg-4 { + padding-right: 1.5rem !important; + } + .pb-lg-4, + .py-lg-4 { + padding-bottom: 1.5rem !important; + } + .pl-lg-4, + .px-lg-4 { + padding-left: 1.5rem !important; + } + .p-lg-5 { + padding: 3rem !important; + } + .pt-lg-5, + .py-lg-5 { + padding-top: 3rem !important; + } + .pr-lg-5, + .px-lg-5 { + padding-right: 3rem !important; + } + .pb-lg-5, + .py-lg-5 { + padding-bottom: 3rem !important; + } + .pl-lg-5, + .px-lg-5 { + padding-left: 3rem !important; + } + .m-lg-auto { + margin: auto !important; + } + .mt-lg-auto, + .my-lg-auto { + margin-top: auto !important; + } + .mr-lg-auto, + .mx-lg-auto { + margin-right: auto !important; + } + .mb-lg-auto, + .my-lg-auto { + margin-bottom: auto !important; + } + .ml-lg-auto, + .mx-lg-auto { + margin-left: auto !important; + } +} + +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important; + } + .mt-xl-0, + .my-xl-0 { + margin-top: 0 !important; + } + .mr-xl-0, + .mx-xl-0 { + margin-right: 0 !important; + } + .mb-xl-0, + .my-xl-0 { + margin-bottom: 0 !important; + } + .ml-xl-0, + .mx-xl-0 { + margin-left: 0 !important; + } + .m-xl-1 { + margin: 0.25rem !important; + } + .mt-xl-1, + .my-xl-1 { + margin-top: 0.25rem !important; + } + .mr-xl-1, + .mx-xl-1 { + margin-right: 0.25rem !important; + } + .mb-xl-1, + .my-xl-1 { + margin-bottom: 0.25rem !important; + } + .ml-xl-1, + .mx-xl-1 { + margin-left: 0.25rem !important; + } + .m-xl-2 { + margin: 0.5rem !important; + } + .mt-xl-2, + .my-xl-2 { + margin-top: 0.5rem !important; + } + .mr-xl-2, + .mx-xl-2 { + margin-right: 0.5rem !important; + } + .mb-xl-2, + .my-xl-2 { + margin-bottom: 0.5rem !important; + } + .ml-xl-2, + .mx-xl-2 { + margin-left: 0.5rem !important; + } + .m-xl-3 { + margin: 1rem !important; + } + .mt-xl-3, + .my-xl-3 { + margin-top: 1rem !important; + } + .mr-xl-3, + .mx-xl-3 { + margin-right: 1rem !important; + } + .mb-xl-3, + .my-xl-3 { + margin-bottom: 1rem !important; + } + .ml-xl-3, + .mx-xl-3 { + margin-left: 1rem !important; + } + .m-xl-4 { + margin: 1.5rem !important; + } + .mt-xl-4, + .my-xl-4 { + margin-top: 1.5rem !important; + } + .mr-xl-4, + .mx-xl-4 { + margin-right: 1.5rem !important; + } + .mb-xl-4, + .my-xl-4 { + margin-bottom: 1.5rem !important; + } + .ml-xl-4, + .mx-xl-4 { + margin-left: 1.5rem !important; + } + .m-xl-5 { + margin: 3rem !important; + } + .mt-xl-5, + .my-xl-5 { + margin-top: 3rem !important; + } + .mr-xl-5, + .mx-xl-5 { + margin-right: 3rem !important; + } + .mb-xl-5, + .my-xl-5 { + margin-bottom: 3rem !important; + } + .ml-xl-5, + .mx-xl-5 { + margin-left: 3rem !important; + } + .p-xl-0 { + padding: 0 !important; + } + .pt-xl-0, + .py-xl-0 { + padding-top: 0 !important; + } + .pr-xl-0, + .px-xl-0 { + padding-right: 0 !important; + } + .pb-xl-0, + .py-xl-0 { + padding-bottom: 0 !important; + } + .pl-xl-0, + .px-xl-0 { + padding-left: 0 !important; + } + .p-xl-1 { + padding: 0.25rem !important; + } + .pt-xl-1, + .py-xl-1 { + padding-top: 0.25rem !important; + } + .pr-xl-1, + .px-xl-1 { + padding-right: 0.25rem !important; + } + .pb-xl-1, + .py-xl-1 { + padding-bottom: 0.25rem !important; + } + .pl-xl-1, + .px-xl-1 { + padding-left: 0.25rem !important; + } + .p-xl-2 { + padding: 0.5rem !important; + } + .pt-xl-2, + .py-xl-2 { + padding-top: 0.5rem !important; + } + .pr-xl-2, + .px-xl-2 { + padding-right: 0.5rem !important; + } + .pb-xl-2, + .py-xl-2 { + padding-bottom: 0.5rem !important; + } + .pl-xl-2, + .px-xl-2 { + padding-left: 0.5rem !important; + } + .p-xl-3 { + padding: 1rem !important; + } + .pt-xl-3, + .py-xl-3 { + padding-top: 1rem !important; + } + .pr-xl-3, + .px-xl-3 { + padding-right: 1rem !important; + } + .pb-xl-3, + .py-xl-3 { + padding-bottom: 1rem !important; + } + .pl-xl-3, + .px-xl-3 { + padding-left: 1rem !important; + } + .p-xl-4 { + padding: 1.5rem !important; + } + .pt-xl-4, + .py-xl-4 { + padding-top: 1.5rem !important; + } + .pr-xl-4, + .px-xl-4 { + padding-right: 1.5rem !important; + } + .pb-xl-4, + .py-xl-4 { + padding-bottom: 1.5rem !important; + } + .pl-xl-4, + .px-xl-4 { + padding-left: 1.5rem !important; + } + .p-xl-5 { + padding: 3rem !important; + } + .pt-xl-5, + .py-xl-5 { + padding-top: 3rem !important; + } + .pr-xl-5, + .px-xl-5 { + padding-right: 3rem !important; + } + .pb-xl-5, + .py-xl-5 { + padding-bottom: 3rem !important; + } + .pl-xl-5, + .px-xl-5 { + padding-left: 3rem !important; + } + .m-xl-auto { + margin: auto !important; + } + .mt-xl-auto, + .my-xl-auto { + margin-top: auto !important; + } + .mr-xl-auto, + .mx-xl-auto { + margin-right: auto !important; + } + .mb-xl-auto, + .my-xl-auto { + margin-bottom: auto !important; + } + .ml-xl-auto, + .mx-xl-auto { + margin-left: auto !important; + } +} + +.text-justify { + text-align: justify !important; +} + +.text-nowrap { + white-space: nowrap !important; +} + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.text-center { + text-align: center !important; +} + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important; + } + .text-sm-right { + text-align: right !important; + } + .text-sm-center { + text-align: center !important; + } +} + +@media (min-width: 768px) { + .text-md-left { + text-align: left !important; + } + .text-md-right { + text-align: right !important; + } + .text-md-center { + text-align: center !important; + } +} + +@media (min-width: 992px) { + .text-lg-left { + text-align: left !important; + } + .text-lg-right { + text-align: right !important; + } + .text-lg-center { + text-align: center !important; + } +} + +@media (min-width: 1200px) { + .text-xl-left { + text-align: left !important; + } + .text-xl-right { + text-align: right !important; + } + .text-xl-center { + text-align: center !important; + } +} + +.text-lowercase { + text-transform: lowercase !important; +} + +.text-uppercase { + text-transform: uppercase !important; +} + +.text-capitalize { + text-transform: capitalize !important; +} + +.font-weight-light { + font-weight: 300 !important; +} + +.font-weight-normal { + font-weight: 400 !important; +} + +.font-weight-bold { + font-weight: 700 !important; +} + +.font-italic { + font-style: italic !important; +} + +.text-white { + color: #fff !important; +} + +.text-primary { + color: #007bff !important; +} + +a.text-primary:hover, a.text-primary:focus { + color: #0062cc !important; +} + +.text-secondary { + color: #6c757d !important; +} + +a.text-secondary:hover, a.text-secondary:focus { + color: #545b62 !important; +} + +.text-success { + color: #28a745 !important; +} + +a.text-success:hover, a.text-success:focus { + color: #1e7e34 !important; +} + +.text-info { + color: #17a2b8 !important; +} + +a.text-info:hover, a.text-info:focus { + color: #117a8b !important; +} + +.text-warning { + color: #ffc107 !important; +} + +a.text-warning:hover, a.text-warning:focus { + color: #d39e00 !important; +} + +.text-danger { + color: #dc3545 !important; +} + +a.text-danger:hover, a.text-danger:focus { + color: #bd2130 !important; +} + +.text-light { + color: #f8f9fa !important; +} + +a.text-light:hover, a.text-light:focus { + color: #dae0e5 !important; +} + +.text-dark { + color: #343a40 !important; +} + +a.text-dark:hover, a.text-dark:focus { + color: #1d2124 !important; +} + +.text-muted { + color: #6c757d !important; +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.visible { + visibility: visible !important; +} + +.invisible { + visibility: hidden !important; +} diff --git a/public/assets/css/load.css b/public/assets/css/load.css new file mode 100644 index 0000000..c5afa85 --- /dev/null +++ b/public/assets/css/load.css @@ -0,0 +1,25 @@ +.load__icon { + animation: linear load 2s infinite; + width: 32px; + height: 32px; +} + +.load__icon-wrap { + margin: auto; +} + +.load { + height: calc(100vh - 16px); + width: 100%; + display: flex; + align-items: center; +} + +@keyframes load { + from { + transform: rotate(0deg) scale(2); + } + to { + transform: rotate(360deg) scale(2); + } +} \ No newline at end of file diff --git a/public/assets/fav.ico b/public/assets/fav.ico new file mode 100644 index 0000000000000000000000000000000000000000..dc9cde60ac6bc5434f74c1c9693a676e82042c65 GIT binary patch literal 1150 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/public/assets/img/banner/shape/shape-1.png b/public/assets/img/banner/shape/shape-1.png new file mode 100644 index 0000000000000000000000000000000000000000..0adb5504182bb1c848b6ca29d41db611839049ad GIT binary patch literal 2439 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 literal 0 HcmV?d00001 diff --git a/public/assets/img/banner/shape/shape-4.png b/public/assets/img/banner/shape/shape-4.png new file mode 100644 index 0000000000000000000000000000000000000000..d63d8f091db829b2738268693dd666be258eccec GIT binary patch literal 4187 zcmd^?=QkUU!^O*&B8W|FqDJgZ&Boq>*fCp0QG3;BjaVhBrD`^ItlDbT7Q`%3o7##R zty#tMeV+6D{)*o@_nv#s=iPmAU))5Pkv1iO1#s`)JxX024byw~2%h|}N&oqma1GSa z{u_HR1Gwhh-QB*d#eah!46Q5wwl)edH8Q(LNJ>G&z|6%fC?+NMP)SuyQv<4^C@(81 zBJh9%L`Ow_pMaQ*mYrWhS;xRoPfHX?@(&ZYkh;Duz4Rk(YDx&J0v{uo4ZuPqBQDIz z123+axpxmZrK)wx!Dz#y;k_~;sx0%x)v!Hzwv@23Od(*1f zrSAVPZ0yL-lRtl>90k}CZ&{@brjrNhO0x_~q~;NwFyRcL8JjN!>NhB@hMB{Fu&hp9 z;qX<*4+gH$RzG)wYot z9+rSIvRj~HyO{B*=}VmdPz}jt`_M_ix5X2SFZHQj-zgNsP7DIq_G`(st$*}RmH4dX zEb6e)kG?Cw<-JAb+#=XcLEJTdN1+d2$4CkVonWNtGH=<0Jr73=H91cHAyCAY^WDjO z)t%`1R%n@~;9L2Ihzd13#lgCvO=u4yfDDE%@_bKZ4;Xnrmy;7CmpH9a+CQ_xrj$fd zGJL=+(k*li9*{LYCsL9E_VRbO*Y~uKnjT=c=!qQ9h;GT3wzv`uPmCr@NS|||@Yem< zPL=*&leCAWA&p$w8rro~`D8TJ&j^(_XowiZ$QrO72|_!J+$RC5@k6wq374?$3D2?D zV+b&{Llw?{{VB+F&L?~{W%`X~{_L>4Cs*nzYOp_ru=uUKzx*JL&eO=X=P0u`6K=R! zV|zBqq_)sD6v>-+65FM|)KhwmS2_pTQ0M*$^^JD6V%e9@t1TSJgM4ebAWrTs!bNN~ zJzRV`1rp*Z?t!pXXW)5y6haxed1e60VWgNw}WASFlCZQY8zJWKV{AEYG#m)HHAHA;1&q4O9+^}623c>|OhIm#^;mHQ^ zq5{*BnZM(Jw(LVacTO25w7`#`#*+`PX#K*K;0}$!AIZxVCs?~b!VYwHFxo`XR&Pm& z;L%9{*>1nP%-=^_vq}zuLFz%nJ2w!NQ`W)wxb(4R%EG1Bx`ePT@H8t=k^<@;j0iq4 zd>-+~0x+t27FH5zzjfh7rz%|;;Teh-!?64lGu|r5;Mju;=9bk_EKO8s`-L*ulGhy4ZdLiFA3ft@Gn&kxvS5&kS~dUf3Q+`gD&fCIiqiBaE?kD@;1 zP~Z~=wd1+wet;j?DT^@P3}RdvO9a&dB3Vu-iawV3^__93&CIcye%TOMeXj6DZ5P;F zzEpc(<}C*U`5~*-t8IJgdF5me!S}X3(Qt3f0!R>EK6l2=$XBO6kVH8&Ru8YZ`R*9C z)Z|3ebuEg4cP0^svB4lUz;gS1L9gICpJB-ig(!R*vij$X8aTua%J;BXEfWatX)V)Z zkW<*1zGVS#^ren7YI*6nEL7GuQQ`Wdx`7!d*o5>S0$}e4pCj=({-n97m{E65)n;Au z4nar%^s>uxxZ{q06*sQzu-9_)9F*2LRVpZSnRWQ)+2wTthUev1GOQ@WRm#wdLP5*R zM5fedRXn}tn4ucv?QcaOcr>(xrAMT*<{_GP>pr+TtANG(b>*e#68HMvKZ0w#$99Jh zb5K6duc?(n!S~ah%ZbQ1`i-Z}ADGe%N$&duV>AM}uWTg;s^?v&FLYZXOawQ&VTX(* zNjI=(`!@_j%+~@W)iB@%4w$@Ur1sFa{r71ZqXj?6_Bkopk|CYn{T-aWSgeXXr}%;< zscx=Wu>n6p(b=y*9(Q{LEbNHB$l?0NHX`mega$UZO6(}5&yT>$R%EG=?b#2q-FmOvW zuT4~EaHm!jZ62TS*NJ__`pA*y5ElHVifoepX~ZA_exG3OBY`e|=o^MR1cecos%yVVvKx<4i#D}G&zlj8kLu6)KuRf{9xjdoT3Z1T-1 zLs_qp^9=8w+bzhNe*F9laRs?>qHz-1f|?pPd~zaWbWawjyNNblN)h;q)O<^yC3l=? zkD@iV+DrqtM}+r628eObrmR1$nK9^j_JIAps`Kq3X@QQEJL%xmUlR#8wi>-C%f9Sp z8sEoOk2M`O=IJ$2PAM`;>4@+>akK{Ww;`A<0k|FaKZlm-5&{uio%j&+D(Xd}?nllluSR8OO_T2ng;85SOvPt1XB zLs!O^|H$pPl?7X@RD>U!b=ZUi{x!js4fP{WmH;mGmklegz_(txw#Nn+Zzcn;g+w>= zR1X7Fi9f$QaDw$Y$d^sh$5onM`@aX7J_YvKnS~K8dEqEL#IwVxk66%QCflsZ%|_)G zr23{i)c5r_A@C2#lNzON$1X@D|p zdSXrR`BA38nbY-cPRGHox27>f$@meIDx_HGDWr*nr`qJ8G*RpamB7}Fi1!5KB|})z z+ALW!wu_da0vvf=t7h0D4@|Bn(8qoYx|M z%v&4zV~w+GKR3qH8m>p6b6*n0BuC(`?84Q9Tb__(ouUA6ws37EV3ZxyyIc&ozI-oX z9#a4Ev5ur-fdrb+o?J^O1=IWKyOPOwK7xOw>*3rwWPsiWMhK1kb|}GD{X>}ytA;Nn z@7ylU7s>!V0Pvv_nzmJ?b8Nnp_R3bnCioGg2h$Nzk*2H>%WG7Q-6d9?WP_y`(51;N z7J}OgompBvNNBbWU}@*sjDtH6I{jJ3cJ=GnDh_rXi;G!-l(w_TQ z4qz-SAptAH@T-m59f*OoqI58>m?`GimGY@E1~59KM6Za6^x-0WcyX{-ITeFv>F0a6R$#=alsP{Z@R2LFi4kPnR?;321j6TvoO@o-kxR6Do7Hg< z^JZ&S$Uf{QY?Oo_n%YV^wXQ8taOd2d3!ras>oPM!4%~1Ox{96D0)q&RvxUrfQbXqm z9m;J{ymbk%^+rW)jg6ta4+BKz0-A<$+b56e)Rb3bq*K0juX;io@F-*DW(4L;!!V9j z#tpv=GNltE8(4C{4}^I^-{Ir5y;cgAMiLepYo>we{?j>Ld;XM^D2&OQ`6FogiU}WD z3(2%w3$6Cc6k)0;KJ>>|R#fjf)S&CLV+qeL^m4MRJvy{%Y+Y|}*VVcs=DQAI?S7nX z2yvpK>az40PG3`FTb?-dyI7SP3QfhZ^KiMo&!cZQmOXR+xN}lU3rPsupKn6)e2$5Y zj`r4zHzlOQCMy)3KWHuT4^CUuCmKudSO4hK4ef{B_@QT$CTfZEpzfV@7j>!;N;)%b z%Qk!8ecSHpf@M|50v9NtyK;|aTQ)mvcY}LNwo_@M=VM1aDGTIBeM7yt9zaq@o{d1Q zI2I8`WrndkLpzeV8Jbcm(SmTwHHGb|6Cic7zG&8YD}kX41(QL6HQSbBGSbJWMLOEG z`&3|10%e(e4kF*o712yCv#JWT@eX+YXqL2ZtEM)DNyW>eiW^X>zOdhja?{yyuXLc) z(3I0x{Blmio(K@xtl>8O)82@Z)50qe|GP-!Zm94iUl(mc;fuKQmSAeb8-1HXDa`=o zRJWL6_QEQ~f-BMM1lb)G^!zWOgw8>*Ve=Is-2)@Hs;`O$5~7m^wss#jeB$r_%13}) zvWwiegT)QKmC?F>6XIWcVzHzvwBql6+oPEUMLr1m&Obe6NJW6EgT@l}m86|IPGf0g zl`8uH(_UKXUH;^@rxy)r6ek_TIin~r)3##+@uK{-idIMJ zCT|}^{LJ2krs%gPU1a+**o-y8*~Is+w|v^sgRS-1-L&o&a^w`2L0`5V=h-;M;wkjk zM3x8r6cBl-nAI`sz&Mw4$z|qCysK54svDPVm{NXYxw`A`!nivcf8e0&V*Y}8OMURn zql2T?;F&K5D(EHg&sPB%j}GV>zU|qTbaRr|&xUOjzn*jOc@aF*l=pK<;QMUT@A=Wg zJ?NLjIl0ldmK$YfdBbBE_RGJsf6;kj=CY{O%5rt4I?im~z2$GX+rgE^{cR;PQSVH> z62DN>tRqi^3PS1Q>I%CoTfcLil`!M$f&T=}`O&)v8-00te*eDPd-rrTjWj-~IY$2v DU2X*| literal 0 HcmV?d00001 diff --git a/public/assets/img/banner/shape/shape-4_tmp19510 b/public/assets/img/banner/shape/shape-4_tmp19510 new file mode 100644 index 0000000000000000000000000000000000000000..513e4317bdf70f5a993bdb6ddbb5855e1fd55259 GIT binary patch literal 9792 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 W`yHuJ%l>_*OIOoCqgL$!=Klb6l#_h` literal 0 HcmV?d00001 diff --git a/public/assets/img/logo-white.png b/public/assets/img/logo-white.png new file mode 100644 index 0000000000000000000000000000000000000000..0acdcad61752d4f9824b88452a0c705799c3f415 GIT binary patch literal 1547 zcmV+m2K4!fP)H@@jfBSQA3lBh04qQjfr&A|3&(JiJ*I)-(!(o9%62%YqS(j^{T&`)hS@XLG3_pkrg{I4|g-u(1Ln z+fkd;4ztlIsyeQN$WVX0Ad79b5E~6VHnBD~+3FNqUG}rk-wExW%%fx9t)_>}Z8lDt zo8;fA#zsO8g|&kchW>G!0L;=z++nTROLf4~SqoD=Xq6!@5&{n^l^!ix7659~wPXMV z76gBiA5M{Rg}#g~>2++pO?})Rw&ytoA``OB$QE}40_d^M?OJW_hQA(&f5Oe+W59J~P* z?gtBB0oXBH{tQCDO-*}f$gnqtLz}UXtX2~nu_U!#!d!tsuAQmlG(H903Oc(6TUVj5 zcr4r=hlaQ`|K^^+vF}0X&*g{(Iere>U{{PiA(tRgt+C<;aenS{ zko3?EhO69g+fu_Z&>L%suMx`U@E3t@45h^#&u84eKrWT|yd2>hZq^?Z{TOpss^yc9 z*Su4hk(6orie$47?ncPbqc(PJ1UzF1g2XmqX7HG@l^Nnys#xpxt z?GKXv+Q~vlq5T>9*po)0t$ zV*qHTz*@AB-Fi(gk;lyp26(o(V7l?>;~wP9?VyZoo6je}_~XgZXUAkln^Gnue(sr# zCcC`9a^x6@k2C*_egs-8OPn-@@MPS~n%psF>9I^Gy%Hbl@qv(9rk!-`D)nSp$A%5J xz+%TdzEK?0IVCuf(S+7t=jl2<~so_#c3F8KirzG1&kB002ovPDHLkV1m-?>QMjy literal 0 HcmV?d00001 diff --git a/public/assets/img/logo.svg b/public/assets/img/logo.svg new file mode 100644 index 0000000..b2b30da --- /dev/null +++ b/public/assets/img/logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/assets/img/main-image.png b/public/assets/img/main-image.png new file mode 100644 index 0000000000000000000000000000000000000000..81b36beb00fc16b0aadf90f23dff0613d558c29f GIT binary patch literal 934566 zcmd@4hhNg|_Xm#Kuw~{bwKTP?)SQ)>TeCECXO>&2%-mXTM5Qv#ZI)(kbLJ%XVw5u% zhY_hq8OC@?m)QgT$j z-nHDhzA)c_Ssm!Q7M*^#(xr>lFFq`Cr8~swiewx8qXb94_nP;bXK>)fYWNv_&tNBv zNTOTC#ht`^c1iM?^7bJP1cVK6?lsN>=-Lg1zAXkH{?b$_bdv=@5BmU~6c{zJI6ou}U(TUbYZ@sPEXi z<8ki`Rie%s+>~bm-44K)V?RbLi`TP0!bwSfxzG@n&YF5Dd7OEGpEy3~ z0*Q(?@HF09p%E4|xsFxRHFGcXfT5aY3CH9LWOwKlIhP|)xhK${(3_`0<9IBPX^Xud z@fDKe@~3?aoP~zypyw}a{-mueXxaoI%K8AH#*jg^@uTu1^QDt>7>?6o!q4ho3@ZzUabS+oE`T2xl-0}nl=&q zX(dGG{{MrCjo3}qGrSuYKRy1;D{-^3t3}t;1JB#2RBvbcLw9o=_b~$#kzsVYI2(4S zhRCNZ9QVSja$fDNJ5vm#0lrwfJ}&`9N30MU%mB zcV;qdVe|)!fnkXS(MI8E4B9cAJ)&V3<`I_rQI~#)+-X$A--50HAv>VvxPZx>-Fj1P zseTWiMgb_d;79E?-gA6K+Pg)0n zsfec4Z_4Cv5o9RHY=j^0hJ|`qpE&#qNrKO!&`3Np z&Xu>>shJu>6Bq5%);OLWXa5mK8_Wwhpc*P9;re=9oLI9zb3p{S)!<$_t(Jb~$5qnv z^&xVWT^%wASNUO;kr~_tBG)6p$_|T+6AZSOf9#n zQzpLe<^Zs15VJIN>sQwW8duT`9;a0+-Y_tWL5u}?vO?XlF7Nq~e2Tz`3;Vg3DTqI< z%oSQT*vFU|)31|MO5XRt(sUYIarR8`4a0${vC|aVqe`-$ov}knJEK@QRPjsqJbtx3 z{NvSJN7DXG%_8;y_jn{op3=a`g|KXT7(B(xW25vK$nk5mB~mU6u}vn-dK4H#D_QAv z-S92u^3uVY(?61v`GD;m}Fk@FF#5s=Zh|h>i|{ zp^YF?*~~x{)2-d^;3rZULKHRFYLm4it5y}eaB;z6DlCUKj8{Y9XK;B`@DqcE?g^D` zFANebH6uRw#gUcgq?r(=MIvt9&b^RJo6cQKr!>*Saze+*4d_Y$ToX=p*^P)aAS|D#m?wblt-XVyWpjnCsxDR3UKztig;|LY3Ja&Ao z4yiD>M0?Fbi0^2rZD@lr2OzAu`2qtnO8!aQf`PHum$m>sav z%Nsnm6*jByn;fs*F_wN`1IG&Yu6y+DN$%F(r4xv}hMdP!LL-xIwYGWTC%=(dmu|>E zceV4%yzTWpI{eOfk*1T%pk>wQdRo5ocdwIcnvRo3EbhalXRdB(np3x%b0tG=_T0eU z*3R9%ns34T1Nl5O$E{WeaYq+hzdaIqchNNS$Tfvd1q|&OM`+cnbhseG#T`f*nfHq5 zAONsFysX;=D*^KeS(K2;*$U&s1v; zyIOVaFSIBq>G)Yay12=FPWmxj2Gr0$ET%lqpI0aTKAB0!oEM)0x?{z^vvOj0i1G!y z;RaGK$X=mdOybM2C8t)fNG?!1pH7@1uPFbeX|A27MR%zXJvm;0=R`-Q~s^vP_H z66gmHg6@&gkf3I-`_BRIe}UKAU9&r8wzT7(&zH_Z#dE!00QUhQxe)SpH1Ol^+=5-X zew;BhJq$958OP>+%12*J$LqgmqF>C15yR$T=-20Ulp;Zi`%_U^P-Zqb_a=hYhLmZX znWd#daCA~s5SB@AgE08h+;C}H0jF7hdAS;|q}qUKm^kR=aIg8k^Ba3RxpOh-SD^yT zR++s$@s1_D|BYEH`HUzsDWYSq3DW`b*>iE;5s^gD_;D{tVMa0N^e}XW(#qzGyKTSSJ?auSh)3qF_p zV8L$Rb`x9tzv_jdd0DorNU7;N1Y;bKk$W^2)d|mG(yt>DFNG+cIbfReexlj(lCb(g`swWu+zm zW|^j_3bnTHPVVb8_TV8bYANf2%F4uQ?m&SKYfza$3!m4}r0mhi14}^sAiXbKF@tPM zh56#~xT!_IWY@oV9Xn$k9~jvz-x$Xl(J`e21)twHfXDvTn$CABil_@?fZJFmw6!oJ zEIs@^f)?X(Mhv&xNYfEE5OPj)R4;7wfn*w7b~X=NRPV)3J%wV%K+wkIzL$Y+fjb>d zG2(ybV|j)1%R{s+2v93*L9V{vzi-W}-0=-#0Dje6i9kb^KDyX6Mtsxpmi&f4K=U!Pis zvQ(KN5h#zp+q<+1mge!3juC(~<^p$%4yolhT#RSoIr=m!{B#TR;|WLzWjOfN7BpxS zzg&i9K)J6%-m58Odo&B%bp|XX4sBh@tc;+%hoU;_k0Y$3lggX${&*>@MN9FgHMo zvgb>NfDs{KEQA_1j5hO22V+-bx&907&f0U_*4ny?jyGV4v0dwJD^L2F7X%q~VX@cE z#7QRkp(3hg9FY)3*!D4PB{2%zpOF4$l-BW-Rz(iN|6&^iCX_L>P_#{#uve$|wTKPk zTQl?@`0hNMB@g)3E_zFs45IA?sETs4zZw^ordPz@*om1VYiQ^6>CIK4ky1or zVMCELUGJ7t&V+fE8>#}m5E)sx5D!fwm$G3Z_C!XRQ1j;-^~bo^UXENDrf=E<>%-FC z%R1<{c$F?k<<(}4bJt}={fy6rKu_ir)=vojjvTa5+_Cl=Rz?s ze6v>iQs*>L)&6tq*dqQn%q-NDTz_b5e%TIv6hbg(iU# zZpY+7#}MLs7kB*z@I*i#6MZEY-AcBSW*%VvymW|DI;%d9hEcV;(DA#q>ky^~Q+CU=(GCcRG96ZVO7IPJ>xm_wS8_UFNZI>R&gU( z=YENbMk_Hcmlg?p2jcYKiounQCpfT%B$ckjca?&hGJ9f(F8-fHI@Mu zb{Uz+T_Ay#*>PFZo}t5aLjlX_Q@Jxq`>4{nFy`1A70yyIkq?2=ba4AIxj__iYI$@L z8LP8A&FKG1312|NTXPm`r>FgNj(8t9&b=n58|BNRHeIXgS*-&sZb28PYpXg2td?n| zcF4dCa{-NHF(7JAfK3h7t!dk3O!zwHDCEuzt&(2B(>9Giwnx+OrZ+GStYK(Y<2rbF zo$LrOZ$U#2L|`wFxrSY6x$v)w#^FBuWKmW?T8fP4!NDX5UaeR=o7JWH^7 zbt0^Jpx-aB*a6B z|7rK#+IZlZdgJ=f=M+*UkhZdYvR8ni#7dypsrSeqXLbXW59$&VKl1W$aLl$`f=-aH zzROi*CnkUz^C34m@-g!Zs~txNvpV$DzTH&_X?_EoTz&xqtc9%~xpJ4`dw4CB31tyk z6T1m2516Zu)VKS}7%n3+jUzYAI5;-mOULv5o8_7^jwpv#MCr6N<Fs+=A67Xu-u$Z{EACPrQo<(XI9o@KP_DiwBs~%oz5CDdB~mK)9F-4fQk7$@1tent zr+M4#)bf7eL94pQ^skj*jCTPJBv)Cv&P92t(ON6&xL$7asDL#OtvBi+px>1BW!(!vuCEY)m7SemWb{^1sT z$!T=L%V!6(jsVhMF^E9zz}o87aDp*1D;s`uFRt{-&vS(l$b7*|?H4&_?cPU&{45!mr0|S{Vt<$8ZI$%Q9)W&TQei;}ao^zY7)IKB;u=2kOjBnR@1z2i`w&h%o-m`gLjzqj#n7`GLwX> zT#VRwq4tmya^*w^n$P{u5ss>xyvv9FS$$6QS!W1BC~B_p3=fYr6NTH;@N%MU=BFu! zXy;*$%BIBq?)!Y^XyA))=cwu&m-xr_A_ME^XX3m<)@T~MpVN;rRE}$0EHqJl&fw^X!mZ?!IY?qnjh?+A`s z1c`R$a=aNuR4glMS^z!^EIENE5}?{VPR0iGb3N-XbaOFO{XHPPerfvA>*_2qBgVkp zXrN^D=066oWkcr6(`CFy>62BT#DoNSmH}%Jl5QDN8Eor>28uk#IuBndXyB4cq$?9W zyN+DY5c_OFR!~LBsU#lx(gxTnsuuST^~(zO%&7mOYi_k-kCTaOM0~P0<6{W?t3#~k z(wp;b8*xP}<=;fMQL`5cybLZgR}T)-u(m*;P?s*H<4-Ux9HfGon{lNS<3l{fyLW1A zRm`57hv6@lU>oHJ{p)3p)XI~gk~Z-w#_n5478?1LIceR8yyU#0GBSpnwc9-Uf7Wo! zKKT@R$R$7lj(Zs3nD!1jRkD#lAVoo9Fi$pTQoPf1$^Xld8CVtsn-|u5XjKo(xCH& zzt|aN2jc!*m2w~rwzq|VfivDT@1lrgy1p8Uq`Fm$- zpxIhp%XG9Zs~(sjxd}|~-*REC8NK$~Ex(d}@CW+ClKy{C_$NdgcAvT5&+44NV8}A6 zZ^>G?HHVlpR6GQA`13XHbE99(k05Y!+Uj&?z{cnLIfgWi=?YTvS?v_*e&2{LOH=;| zNPFe%_p)dtt_<*KA&14LdF94RFNRWu%zbgYF{MEBk`9PITMxke($HbO5q8LS53;1` zTgbh^u`vjhhe^;`FWdVGfNrM(l-xnNa~=g;g^jVsresV)(yc+mv@jI;msrWqq{h}& z*#vz?h~tNzh0p1gR`mlgM3ce~i|@<#Y^R&|-u++|A(={hFG!ftcdS!~IDQ%HJ&*s# zLXKaSdK0;L30;R7Eh?2C>w3+nknM; zLNf{_hcHwes^+ZjN=w*~Bu$BL^QqJy5Bx`cH36G=%nNwswlxrVo2VS!&=~hZ*A$zs zU>5sU@+4awF@iosIld7RL1RbqEjlj6&G|3(yrN)9^w$`Th$$%FoSvVE;a}F$P}(MX zyi3v6fX(TA?T5K>b7vNY!a2k8i265KTn16{ZE@AI~!m$ z(%u@Y$_UNMF*oR}<NnL0!E~Om}S$gx+N$ZDORZ@4T$9K=9 zTC>RMzcBSKb6&PB)|t9(vPW%ZBWUamqPOS>Ubj>46$MU3=8j{TcPpSOdIN!H8h5ID zEPSp8dNU(%-0KA+dR~7Ygzc1xAbqLj+uoQok+h3RDW-=b=Gb5THNZp31s}8_`qK;S_Nj*Bmh4v zE2rk;{YJ|itxKAqhJh8K9+h$=PiSmEfidd={KI&8ci#BFM9IPHY)RO%Mh4P%()@ci zw)JOj&R{~IvWf*a#05SjDUl3`=RP$YM-qOZRd8GTLGT@?eZ~aEmuT<^#qqp=OXA}@ z(NG+o>GR zVBkO3CwhP7nj9zeJTASuL!(6m;Svg#D;aANb8t_;edujvT=pYK>XrLl7nUSmoXw8g zCxrO)23EZ>2;4M6TyOnmd0Lp>ESX>7>pl6P&l+KR$iAPv(f?UkdphrldVsO%%s<`O zzw);3T8Qk~qm5tFOuPMcp6#nsAD^He2~lY}w{xr6j`elT;V<8zd&dt4AKFR#5ORya zh!^@j*0{i_zzz44lyO#aGmqT zR(WP-rCTXdY=w%7yr#dYN69Me>oDoonmiNSJRD4`W3>GIDQQv(5}Ys3aqMqv@e#^A zLkfv`-N#_;a8agDA_PmyrLO$KG(CXjKneO687SKonLEm1PL&c)8dLe0n7%)6bas{} zV`mfBl!9vWl`YQwezbp#^Pv;F|NmDmy-+{hnZTpHd$*#plHlvT#lOL}v`Ph02l!y~ zJm@|A6QiEoANaWA3@1R$+(5QDBuI0)E~I5Z^=#dzs_Y}mUjXQx6`|3Wf%`3cSBQh! zJ%ug6AAi)5d@0gUO}_k^ACuT}&Mnlf`i3|O_GZHGZujlMws((uL+vm}*_hhcJoM%q z%ymPIeSNp39b^}FyfCgazX{w}xHjf&=dkz#q`|hA|Az-T5+4nCxGVubyAiRW|dRV2E)^vuF$(XL?@&o z&JBK3uf2Krf5vm1Uby$+8tOb=cs~9OCM``S6>fobgoYEgCuy7^xKdt&DG>{-9?F17 z5w|9zL_2nN%*SG&Zr!?Y^qWUThunz7KoP;k(MJ>&*V`%J=jo(rx!5TsTU{TtC0<(5 z^&?vk%=7LEPK*Fde zlo(wV8RVu8C%R$(`uUGAoaj>XL9b|fyDjx;m2mlej6hlC=6Z&!7c|yfiKX7OO(`b* zQMle;8fp9gx2yNT>07K#tX6dxwb+K;kfdGCW&{W=>uV+st;VRX18RgI?B1yL%%0nB z#Y3OXktwg2=dO`gyDmj#{ zy_)Ol3fKM~OHT<`))9sKZ!b>pD{n6iV2;Mo?##gX{xYJEOeH&-w+K@#_EZ#4As}lV zL8z-37Y%FMiXgApB<6(A(UvP31H;{o%hEW~d>_~U;`ocBZ8i=r^1FZLGe2JEFJTP| z9ywr}TbWm7Fs63N?a`zbWKvaVUO2b`kIY%dvW7^t-krf|X6TA%mg{i)lUHs6xU-@F z8oXu2RxrV-$_?z_h1kUVKkmqcwET8ja(yO`K%F@bu)CRjimr6^6=_oqX6paNRdGC| z^p+62lzMP?m~Py9Pm^cId+5BA?Mk)PxQmP1fI{sn>Ga;$;pwW#E|{(=F>~q)AEKBw zw%+7EY%-u^rZ2;9|L!=l+?0NB~Q z4LTwPR63%_H~9xmCdc-GIp5Go2JB9&LL-*Tf_uKgFZ`JLnXI(7bX-=UndH`1M%8tn zd|U#zsC-ReZRPP2{e5X+1Yp>9`HzUIIwD`A%06YL|Le}?J^hXAo1o>LJCBu=jNfCK z#_m0GqIe#C-aJK({~_S*RYM_s*??dWy!q=`rJurigNQ0um(IYFm=hB~^2~!_u2wkk zrg=#@rggA>zUcLQt74m(JyJw<3V^I9S3QuKY4(7-+ym#o7T>{NteugFb6cdtQ1ZM;#<1^4Phq>>63|C!(_Zs-A* zr=kXZ;^c;>j*86W13=Kk;veSI^**kvR%3=fmTtFKn1s-#Mw*5Mqkw$0HOT&u^624% zi|jV_U%BzNy*;d21!O)gpW4yZHd!yUJnYZ?=FG9P5{i-K5TTL@Y9x*6%m`ST@OkEV zsu%QV;eow1wrJi-*rT`lS@ww*-2~eYJ{6gc4~TpvhduRBAEG=KqW6tD3#+8$vBNdK zHn(y1H$|#$FKuqdy3SlbW#?Gbw%+&>Uv8kgL%LwG@s_;~ha;azBCx96*u=a1;_PaD z;b!0s0*MCMl#;*MkjcF>6uIHwD$ShEwLgU!g9Y?VOgii(EG(IFW&S|{B+-DesqvLy z5)AQZyZ3kXICl2jwq#!xN)<2oCS7IfIsCbOexTOqqx{g$uhEwBea_W|YfCib=d(-v z7GDE@JUTCMnGccmfBxX$xH`M&u0uJib`$j4NM+!00h#z$2MfJZCyUH_cC z5wXR8p^~iVggjhQ1@+wzsX=int9r1fF6$TYxT8PpEC7x6F84ae67d91Yn_}Ix;anFOYD?iFJb#DYMMQipJOa` zd=v6pC^-ZDuS<49STRp4T*WIFds~$MIDakoebxNnN)0r?O3m3ov31S2H?b-u?b=k| zD=GWiT3TL`$-n%49d<1*K@v7V6d!`a&T#Lq&NbKZkLWpX4;tj_c_0_;?8O z%TIa1YZ9)WgfZcUsh42OGxmcf4S$M}V~2;J)-re6uoi|FTYujR_0u;eB4tf`%s6u# z*k_B(22kk!mz|ZlxAHZ&v4x(`%>y%Z-qYCaKHzBdr{m9VMCTpncS;0|ILx633&>h7jqQbo#%MoPXodEgC!g>8p zBIsIuCW;u=P?O=(nq|7yQhAx223jBpXxtHbT|(pR6=+pIf<>XuMDE??b?^W0i8%lE zmPm|8RJY;0`=V?MCV}{DrsW~%qsaq=1|I?<_Y#(vt^^{cN&}%pXR{P&OUS16?^2p- zf)*(8=u~@d_Hoo)9&PU8v5Oaw&(N8X!JRczxdL+qUp4fr3!?;Tk`IZ8Kb$#k|6ZGs zVm1k$zpWy7_}|3H9`K%Vi1CD4v2%kRUzv+9u(;{yesyIRjhsf%z+K*V2{P_NdHrM} z_`i8gyjn)BKaQiItX|P=CEdF-37mR{@iCe*$AVbYUsWG&^NY z-eGgV6j^JM;I1#Hz#Aat`yQKhZ9aNW>t2WE|B(N-@94V*dnx;2Uouwf_uMeiiVUxU z-B7kECEw8s%W6cp53QsRZLUfFj4K=M+g_aBNi0KI`3!GT!c~oO!1}NC4vGqDujChd z;M8;xFOGkC1^xW66Mt-`Jy$ zs_u{0z^bizSv`=NtHwp6&)zWXH)n!Gx{g0P?fRQ8V~Woy(!>r|I!eMbhhbkeNx zswKtP!Y@1DRYfj5_<#9p{K-z|eB!sJH}|nHkfoel1>v-(zbWW>>00H!0xTpr->Td& z+e+K6d!+kpK8`XwG}*0}d0kh#Wg#Hea-9D911z&}{9PX2@q;GUmiMHeSjXVTONj-X z?VY&b18i*iSiq9Ce<93!?^m$dSMABP@v`(cgFidpr3*CZ$1xE=nHn5P*UfV{0^l z2^~g!VsqdB$}i^!2Hi``%aUPD)w;5>dPHKJMb{2dDWWS^n>wK zeX8$=+mh?JJAbNoS%YBqa)bdK^O3m#+u=D z$fm1ChN}A|Ar#7c(0XXtH>ss`bo51V>qrXMOXY1&v#SRAzqc3Q3vTbQeK$&mF~sVG zMEp$cd7#B%EwXF##fiS@ti%ItcX4X#>3-KycW#8SHUZ48$HuSc6 zLlw;9<~>+?l0L``NG>iP6|PnVIz!)azr&zD=(-UrrO5>aN7$XCZW@rc3f%2PTK6ef zNMkIGl|Fh&nK`v3oWn+Aj`sx%0PVo1GPdaHm1)Ik)N0@i3-Wcv)Bj;s;^~-VZ;>2N zwp)ZReHMC)=g|&gkEkZEm6wWWp4;@hdwg3lfu{%t>-Q<>v6^|tag7%X_nk5oDpEUJ z_!m;;FV;h6o>CmP+}o|u)DwbNQn zv|Gj8e8FtyqK=i$dVlC~_Nh6k+V~y_d(yIJXL<}pd4#Xq{!ARCtvl3vWFw5!-(v-G z@OHtP4Lv*2#CgJ#ZNP`n+n?Tuz4l=lbv~IL);e&^2oYRJJ%Qxc?`Sk(1jDmBZpBg8 z9zDaiA^7I4Z9n0FXNfzvAvfUHUTu$P&5K>^mg0mIs+T9!_( zADZ8=(q@wzujUSwa9sKyk~n@Dq{sTYd~E7~q2RCf!PfgTU+w{}0GHyTzu}Wjil-OE zxT-Mkap2$eTs_x`1;Qbd*DcPz`MsOLwBH`a)JpqUZZtm$eQJLn%iR*L*{MA1^mQ9| z%m6KG(DqsA0&=ku3dH%UZKPGEU=Cx@xs$mkYAiPEx*|R=t3nWfN(y*y=?=EdMMN%lskrCO4ic1`dw3H!0)$_m-m z;kekOp)u;X`E%&S3#e)DN%hT}eTxQtY@UNWzQf5ck469}Uihzuw&*_lJCC(xZlHgz zS462z7S0_skBCmVrFHSJ3EuycrlUb7cwwpm9YopDOnYcK<{4SxC|Xh@g!yq4&lUGA z$#PT#{kxqUbCzfu4?Ss7*#gpPir=x|2%D)(PDsH^b2tJ427j#o@EmcKKHk2L+m)^u z7h|QSI`?QDSbM661YWIZ!5-*UorP#p24fr^or$bU`~+0OFFnQxVK+60j8c$KdEq2- zjCmurFiHEOVBLLxtQLPq;xWEoU~UeMT+V0+hl3BVE}N3djblddgCC7h{i)KZo00}O?hH78y+RF2E%6p-UTB*@Q%G2N=1rBqWyi=8N9kIe+|K<%71O3iEw0M;Y+<3=j7o z#w7^ra*Ew_p~upj6j(wE6bxrzM*StO;Wmv94$GzKx1Z^AZ=ei34Kv@03+molk-{uM zM%4vvf?P`83k!Nf3e%QS?vevbEitlq_80=RsZB6S!4ED^XH_YhtGKLGsLDx3Us+Bq z(2a^_??u1uKJ-GILy>0}c9`Rr%e-$L?B{x#cmXOWQM|gV>yA};dH`s($h=J_h^kj} zqv{$hmXHN#w4f#|XV_RpG^EMC5p0aLE4e2bcM+`^Ms2_nPq7A_r>#1BH3}`qgj~MP zKlbV1qx9V~Ktn(CEso+M8OqgnTUyn#XCAe+*5@`SoGMxF9qa$e_^9-`A=ShAqpP%v zRFXN$)aSp>o8y<%Y5lpCyHf>cc_xQCOsyU6s9Opp0_f48n#KCqYL2Y>$f@<}X87-Q`T@a$1Kfr^ue_SQI9t=2b%8F z*BRH!;}y>uv=h$|8@vx0kH z6`x~AKjHnEA(LAqp-qeIb1w%@Z5X9MTf15&bw`7*`=0`n*2%|w`8Hc1NSi8!<79d0 z(Y5C`%hdvlwxK|a1!t*0$i^2TOt2eKtr9y_R{E}Squkc=h!3xWaZY^g?-*+JCFn&S z>S~hkGAPs|k-6e(C%zz;%a513Kd-8E*j$<2PMl)SpWu_{{AJ7yiMssv2*7 zp4`Xg2KF6nZ8Hm$Pv3tN>T7K_%)MH~vz1h$F{S_e5XqBu%k&xw`tuLA;%dQC?{Q9FP)kg~H3llBfh;44<5dMqn6Xq|A5T1UYkSdinLD2_ zl&H$;xm>N{`#n)b*FQM8a_G!|BxsZN+S(egm-o;|u)A<~t@L9!h$F*L( zM1Q6x^Wp(OprHfiue|Kl3unrJmWxYYS(KlAd|%nl%1IgUQx)weH)g$`*qsmaE`Q?c z9A^79_5M4{(bgqdcbjb6&wYRtSZ)LMSohgfc5l=MWdf`!SYw85aTT1wfikMy?{m;6 zW)^S{`go>pwueV0jD1y|3D;>_Ir=`3G{|^msVH)VG@2~I4z)GMO`f;b$Ng&#alC!E zwY{7azVDFQg>~N@>-@CBRBT;;p6rv`M`K-*Vh$q3hXYwYjIrbS^$d~xeU#CkY_w^@ zu7Huv2z#^Rhi)9z^S-Pyu%yEJw&25(pLoKktBr5ZlBd>QQ|%GFL?%^D6reeNMW#TCFn+hx`E ztkflIYiqaEf<*yhL%3#1g^u6#RWDE@w{m~&%-Z;);k{RRoM6S+@NEO|`wYGN6OD^2 zD?eJs&LQOfd$_NjIQ;m|Lel{if_SF?;i;X?`Kt)y%TB)X;}F z33r$U9=)xudl~$AkC0eAj&X-GRez{YCaVqcc=-BmRxs}8f&im?XCMDOeRxy;OQl@y zv((mIoUMSvNi_5w-W=`0$7~;Wd#1k!nwi3x_()&QDQ@hD61GXnUFf1Uc4TGb>`lTZ zy&ot~7XWAvZ=2iCAH;k7!>2^Qej$2Pg9G5TVaqT~$$ZZI-}-KDHS(U+!a%oinV52gfk65T8z) z9@)O3A{Ztg_QF=)zP{U*;z@X#*$~bY>UuBzm?>oPL7Qt+Buz2m&bW6@BeA97YfW~^ z@ML(-@@c;(#niTV>8!#|>4epnA^%VL4*nLs=e17?3aS|Ru{E<@m@I!*mzp8u4%D`M z4)~^dsOs!{Lpv5Pvb;34TOvHvyzI!Bz4iOhg2B8<<$838JpY{9J4Ort%G3M)=j8t= zrs-R9_o)luZn-rBxHbU2@E_qY!aDMa-3UE;wM+Igvw{&b{tSI@PM+XCt5(HS-vl;waV8ssSM?R-Em;A(m4~S$Z4M|W4ZG{cPax(!G&E-DHf9+uph$} z(h0oAmlFBk{cegz5ylOEbs^?U$7hK%#pe6y}tDnoY*~-dM>9E3W zE2YMb+qb)T6OH&Vmy~)5op?0yEA9q?_G)CrH}H32tgqpf z3qiw!sO!ga&>nC4R97P0-4*iybo~W8b3xUjGdMYB)!6lYps5TT2x&D2l4n)-7G)&% zr!0$?C{jU7-KA#ZqB8>GIFCMi6>UD_<|$vpm{o_v-nQJSzLpOH=K2PnMQT8CbB-0f zaD&N5iJYL=)A+~>Lq#)V@PgWPO?2R9cgg4O5W5S5@G+}8Mpou5U_En{RqU17kUgbw zp}N!)FA5)eQb&f*IKAtA@SkhIK z0p(4jO60eumc)Rc#$h6>vk0d4)C|Nyp z8xhhYF@9?He@P8vzM9m4j%Ct(V#26nR4sr4Ewg#;jb^x+m`>{^d&sv zGe1cvI=itIXmM8Z2|}`GEHx;3^F8>^h{s@=x)Ijc5oz)uKB%ihL@PZ{7}38C6~nt$hKT>T2ujX za`jAH`q~GRdepXdx0${B#QANpi|svZkZQe?=HX~MrOD>Dpqvyx??=F^81w7RiSZr5 zQ^KrK^uXQl?_e2PCOpr7&HZ1|+NPcjT%lWN~^M5CQRoNxy$ zrEH5!4>BXJ_1vjNNpuG+s;c@n9(X-$`b+CXopd7qf3Usr_S2+SQ9UYIUI+2=%5uRF;Ye>qpL7u; z?x72-zr8N<2J_JHFz)0Aqm-U44Kxboft7E5fx*LtrHa=r5Y3mH zhgnvB4>C@Gl>IBEfs=e8O@?)j(`R{VfM2)1Sf-%PE$KI0Iiuuug388dTpzrF4bEG^ zY&srbF@7oV!t(SLRLn+1K7Ju1I4$>Mj9~qX^E>ao6|TfaDn`qDTkfn?ipX`TMC9}1 zC+xiMSbK_s_&d|DmCC<#zNlDEP-Rb8R=pm-_ZtWu+H3CJe%*oBIz;H8`+pO-=R9AL z{-6j%6+5?^WQoPm;6$OC_5O&Q>N`rHVWX7XL8@uXpAvna!Xk8a&{zIHFaJA(F)NdZ z$Q2S_jour5*T*=wb2`nc(J2J0>G2#JvpJ-CRyBa5@iad^b*D;z&A?lGSb%3tr7aNE zkCI1zjLTO_BVqhM4CjspGh6%5I#}miSti^n&x$ptZCUpoh|rECehRv}|t)ym)q6JYy?%U81eTFw50SWYt0X z*+tu*c&G7x-%O2KyJ+0&`UTr8+--b5;e6&qzx^A;IX&x{!kTOu0x~O^Hxl;3-afq61L@OkX;;GjzN9q1S4kmP^ik8G6(3JKQjEoQUu>4){{`QXd3vw~12W zqp}NkT3gQWhO)JubJ6v*p4iP*at;3~$fnwD&9UO3jCf)y)4h=F`dG2e|?}~pT_IRfef(C?fCZ{!{1kDk($ZkTCgiRjt#0MN_vL!z5ajdd;kg zsh^dznn$KS%QG5&Wlv09_{~iG@imnG#d@=LV zU&YD8y1bef>M8?XkojR{*fhdXRUCRz7d@f}M|q4rjT0_&$12Qu1=MM#ho7-1)HGLq z5@*_uaZupO_D=e!QLbJYCf9pbKNiO3{Vd5dGAH(T<<)SB9@F~SGM@6VXwxA$eWGzL zgywTy>V1xa=7|FUb1E>8RONeH8sEljo}SaN(Bz#f<=;y!_|Vl&c$lDoN=&fkWIyRo zc`2vZ6z(khKumDw#_$BKqz(zIa$L6Z4Hh9HjXHDw@==}le-!HN44AeQbp+ViZi|hv} zTfrC-n529Uuv6OMBQtr*RvINeyB`JLouzAyv1tt*NAvRwpl(qpl@4<;#leZeD)wTt zUeU{V=E3S*=^@m|%o)2&#~t68M4SGvA2*z?#plzKjBsNeEyYB6f47v%`KX(gKLnQ- zVv}nZh*#UH^-U=Yb6O=h5~EF z?yJpHAnA9BJZ!o8;=pJR+col2A3e;l*6X?RSrgOSs_im`Y78_s3ctzAkj||v+c&qC z4?j6ObN|7XrgT|Gv<70-<~i8=KJxma9A8#j@s5~KzL#wQtUfnAMC|Un+N&Q5i zU>u;WTqh|y7r(G@<8Jng{ZUc`?}^rHrz73B=7Dd^Y-C8*oSdYFj1&n&UZ|YsnWxSzbU_XneeRUoiWVra<;PKFR#ml zKNY>k$zWqg?U4|A+3^>->&|tf+QF4%A+S1lhN=a(>~6Qa?EKB3K*^f>@-YhW1kOjC zEU4#s?;Y%Hh5Xb`>b$?CIlEtj{S_NUWC|kB>5Sd^%GvWKF**L}fGv%zY3zZmJ5^cQw_@xizz9cje#Ey-;ff$NXSE z=z;DUNC%qCjaXvesF~PY57j|hX400Dz!r&1RQW~OIunfiu9jlRB<&or$YT+iyMj36 zki3Ip@#?(`D>(DKs8eL*Ugb}jFaMjapF0z`LnVhJaquH_w@MWv4+L9<%UcOxra1wd z{F~8{9l}&b)kn9F_%%yEV&fhxzMmOU2{z?zJp(UKCYinuuvB6J`zpjS2Hj=r<{V=}7qs_WRhw zi)*y@=zxN`3mW6jyq7(sjL>7remQ0wTTNC~QqUin*%zqgWSG;XPv>smAf!9As{IJ7 zMMdiE11vH8qmp}AEj>NYv3~V>!D&}u3zL4D&8JQE%=W{EU+I3fhCdP6UVf1eEh8Ki zOiJ^;pEfTJ0`# z^9pAE7$r$bG+!z8F|po4pRefFdT9TX$HreFS#??Ee{2(AfLVY6b~zdb1~~5}rT%mO zU_9TNG!ZcU!5ybnHiL9Ugxb~i$)p{0Hj{5i7%s4lpG4i_9>lQ{@5~v39`v2Nei&su zoHa9}|AQ-Ykfn05FVuH3GtIKU zD@v`_rss-0s|xaMqwq$1nvJQ1K*Nf6@8CcyRU=X(hU*keHBMMN*}mth`_L6A4otA= zV(wv?yzl`Cl_n}{d}j(Cg|;G05VxEbURqg~c{$iSEqAc%;Wy*CotVJ`TLph4oFE@G zf!YT%R&bR~+?M;(3x%MYYGZXJsc{7o2_#csINcZOh_C6>nkVXMNa!9rG^k{6BYW7M zw#z86&&x6>!J|mfir1ys!pL?0I>Wl!E+z)p|JZo7SR^ND+&%*}=QtYJ?VL)z5C5I| zE?j2XU;qrgFZKV27OO)_0_~A3aW|{V#a_ZX(a2;@nSndBTXV*w?blk+-z5dhurXh5 zij4Goz1803vVMBDC$ducdjKF|zmLmogHSk(%MWvb5?d($hMl})?`y9oiaZ_8LMZ*X zU_fj~SAJT4ilRLI<;6K%TNfOe41dkg1=?xlSVCFXY$v(FCYE%hvX^i_q3h_n6!60*?1|cg&S>>p$Wz6F=9ITf6QZF80 z0UTti`&ccO4V*XVHmnKfM|QY9S2_M$;)kk~T*TM3f_OI24=0?67$(GRinV3qQPNM* zIO3CiKl8oQ=pWcVOL19IcTg{swGF4($2Bo;bL4NZv4csc0PjpHTnAGpMBi4;;cNd% z8iL~_>aEr&SeIDn)Z~ltoweS4zVFDE0=ju389c-;=qbwK#k=l)T4Gf%o)cMZ5hs2a z@#B93{duTnAyme&pnh{}^W15`Y$<++C#^Tj(~ev&fAyi_X!}drwdx&j2#Y5&*q3*N z%#`>{1omMhReSp6k!LfW;t(j~oeL}ug%@w7u5T((doTGaHr*skf81_QH&F=qKqLgc z^QJU~l(9(q;?y(}am8XGUbvG14OauuYL9yJeraa|XcGiuqyfQU8|2-yeC)*0f>X+B z_h*Q5wbdxb<*S%lBu=xnuL=ilV#1d~3>sgp=T^QhSN_VYu#}4JH!$En;a^@p!fN1L z0YAj@d9oek=oajAD#R;(%b*ExektJv3+?3z}uLE zVJKqRM?g%u;HQn|a<(@De#)2j43vKCCL*SDjw08Z{U3JKkpA|Wymubs)`Vg9)U&#uy)8X)!jl<$`-*ix0vbvDo``i!GlHoJa{cNqty) zOo{|n+=+QmPfJX?t`mu0LW}K{m1ybeUogMx)0+^}4>8}-{Nfd_fhl`G@`SeE6SaBP zlbOcK_uzWIWz-Y(_xxHHitWWWJR3Bj66XDlc+K0(E3w*4_o(B9>J`kq#p|u!7YTNn zu%k&lR&EGaq}#6T7LBDW0*`wakXyC{AJyx~%8vyDH0x(%TXlb3oj^{|Tg>w$)f?l3j}MQY@ua2|NAYv?h8eO$Q2Dh(F2J zcoB`~hqTtj37OIJ^Ge$kTc4YS^APIXiGlxgwUlxkZnoI92?_onSq~@i6*nz4pnVXD z@@lGDCTPm>|E4`J=y83m06+e?tS>;kP&`(doXFFDfGl?jqK^qGqbh-4r>C|j5gLiY zrUo-WyL=xg3pc78AQ)aq!mGb(@>~SCC?K1RA0ETDFMMY6jm>c$h?3eya1uhO!@qqS zvAtXKSG5e)rl)%`0xTJpbZCVBPKxsh$k|BYrW~w)p-&TEXGnY2qw|^L(T4#_;RzAjqlS@#eKE(l1{A zdQMAWxc6gq8M?2|-9E@*XUiw&oj$^6CGpJp(`}WDYUAy9nnRkijYKsI81)>urM~dN zCP}VuR^=+;fd1ygCt^Sr{=ds;GcZ9Vv=FtfmP469e%!f~Y)NjQk7$bn1e*fiTZYY_l_C^C_Y>z$-bTU8ai z{tr`>9C#iUF~!IT4f4p*p50wtuivmPq>6Xv+u}QSA||gGQ;8hOg1AdK{t2+zlUROj z`V;8`_F{Kc1MBKSRgBmSb1vLU zTxdGSfX*2B^DH)&kABAqFwi5evH3@4b`x@9LIKP9B`I~&{+NW3+K|Cvt^?1UR%8rR z=LAo@^*U*F@)jr|e6_<-E(-=OyCeP;X}jC%dkpm=s|EJ&4zpFt7&V+(GM<49MXXwh z5W?;d`RitDTF#KxGKND>a~GO)EoXpHz<%(gF0D~k6b7FcP~29{ zd^ESpne$I_```6!6VEC=hQGH|IL8lPJwGPZ``%jScpF7wjUcB?uCAZmem0wiW~%pE zv(|dPvT?bGZw7%BGxkd2z0JW=y2Ib>e?)WSz%lXVpaAfMEj@DJ?nBOPM7^4YzSBvk z)>?8Uokn|M&((r{R+kBWy#yh9-|m;R?3wNBQ@)ub07)rjhCTm2U}!GVOy*xJ%9i;O z)`~F}kvP~7V#R|ii=@wp-)SVY-XYS`;kxT5$wGTZe`Pos6%-Z2BQn#=)ff_I1jB%C znHARduX|Wrl(9M;z<*bx7~(+o1do@GVHW#|Nr5|>adb%oz#jf z*f|6IkLoJf*Xd5kUO^=p%^=v`WY>o?{k-ce`4H_L;z?Vxk;zmSS0^wHI|(Cn_w)=e zqbD$*6+msz>rBoHQzo3R4Ks5T=93SMkAki=+ zy;N(PWnXKLYV}j*$C=Cwa?a|^j(B@G)t%rb>GjHiKVo7AR3nD(g`FiYHIrJdX^cn3 z4|5_R<1YUs+}0JuM#3>T;jYg&S&=iJqB{vs23DasfWOwfbe~WW${UGW(`k3zU_#?Z z;rwIP)+s`bCjg`7g3*}j$eNww4Ff<>SfyWvxPpqv&6gs1iP!hpJl zFG9Q(>~CS=;o)H`@TQ8>^ykD$iQIo$i*GDotd3$Vh!xgnvd=F2jXLLDTtr;)Y<=_r zEx)qcB;p2_y;***e&v)?$$X?;Q#?FJ$(I|oG|*M@>Bf7*Pi;vyJa@D4bm8{Gx9Ow_ z`eug5{C3-w`xA*oYx}Dr(USU%?PVfP1Bs32d)~?^e(U2di+yJs%#m)^!s>>wbCfe1 z=Sdx9Hj~?AOIkW#itDnQtUR}8t?@mov4r*#qX zm-;Ufut!#_2XCs~`6Ss@Zpf{WWu^xCCdJBsriiw869k&rdhh&uuK2{UheJ|-UtOB# z{ho=Al$t%jsQ8faduI4ZXHCpCC!VWBnf{C@nQ>@|nY7!ZBr!H^8>7>H3WNtFzLLf@9={z#F*`HBUr0X-`xVSX7+YG^QYDrk9 z&qmWZMO?G0cY4PgeVdrxP4^ijKOQUmm6`gs^)BCCC~Q?z*@lS_zPkEqB(Xs5CXcSW zm3IuvQYzs`e>(~A%v|@SqOF0r$h?3&jmE(`x^;abdd+}1Ft|)UTQ*NSnOqz29-~ zgsD1H1nEzAelAm#FtjgDSH>3fBO9+jbpD>=1X+LIZvqebgFU^Cr}q7=vRb-dX>)*Q zP-`fcyMo_1D&)Su2TVIt?2OZg+2 z>Qlc`*=m;Z9&oAoE{Ej{9cP|-w-7JRu{(c{`NG7(<;N&=+Rl_`YGg%(y|VgCa7=iW(Km9dS;g8hK_j+d(9>4a$Mrx0d=skefP zCjcSiZW;e|QeB9$v4)6wAV7&)GJl(r9Y;ki86falsC!ZUzzC}Pi2g6*yLLAl%omAjVUNk9&4+H+IMy%74H`XX>@;}< z^u@^@Plebw}epI2*5Y$g#o@SXb910>I0{H0Jo z5qZ4hQj=rRKyk90Puy5B9Tro)4Bc`P5J-fXi7@}JWfZc7P!nWiIY}d&K(}bS0Zo)) zzFAfbtW!M%^>U_vfkez%;81bMD=DFy3?UpK-v1zxXTM{zkGE*Av8`p8meHi6lAD*4 zbgC{bX2l)c-`@{JW9Io|T|mE1F18Ld0-v;hU+Ugmu-A$lMvD13h&4dwN$Ofp^dWrJfNyq{;t^ z1~u|}bK1Zq5*$TO+||tvXXmox%k~x~e-q6K4`q_Bs%e5rH|DG%EKH)+AZ9O)!D<52 zH;iShVz_cX%bMLWk9k?|fyjZD1c%W4T|Gu(EEN7vyA1H~vEH5Vp4v#%N-t1k{mRNC zdriaaFKFIUJyQHZpeRcUv&Vq^mtTBIQXhkbD{y3LI88H~2oJ24G7ca+{0x19(|PI^ zFc4m~W3Tgqo(W;JmAXT_>X1Amv{+hkxLTw=n`AcBog66%qsRG5+CPr;nd!|C-1h+EHzNkIRQSrMgzIltyCTMI= z1JRDKxd|-$Si}ibn3(WOzNtq{GxV?AFaz)~uF;=A0i~vKNA+{8BdV>~WI=M|V$_WK zw?ZyhY6K76-Rwr1aYoR-dlaYo6j%h;Pu zU<$(-UmzpG6U5y!jODW${>x~ji?c$s{RjTJ`Vy_xnsXBRl)k`xa+fw4_cTTCh_91B z88%b)s%kyO=jk%KAgphYQf?pJMcRZJ$F-I55H-O3>3rKAhkAB#tqTdsLgNbSKa=~B zHQt}elHP<`Jb_Q8dOH#KKIl|`NQ%(^k%BB@F)(>XE**%;WZ3NAK1!t~wBRFphMk$r z1z^OuwShjIal)MN&H&ysa&gIR`F4OxBe-O0v8wh@uT_kUrdIweWPyPDdS(q{#`R~%lLJRP!< zW*aj2Q3&X$tgPe(x#rs6si&EGC_j~$>_kzho58jEg4&n`1)Yq+BN^A_>RmQ|XizN1 z-C5FS^lAP(v-Xs%A8kRMc@;J|-zcBucl}x+LQ%1{!*At|E)B9nP7xqjNeoWoQ)q%;|C4Tzb>qiTXR>s%S$T^eECX{wA4m~@~md^Pv zD38q%?{LwB^gVZMwgY^Uef`)3+*Gc-*avkfhWqr0?||4GUDUt{+_%^Ro{y>RCsRY+ zDJfBJG12WMJy(($iw9BxUmVfls92hRHdX*y%doRzqf)li?A>i;m%_=C*)Ov_!71NL zjKnscwlePbCFawB1s8r=a_>UlC1^ZF#rT$5?+j&-7wry#B0l?$wzwV6R9yk>6VBJH9f!FxXsjxpWAnH{YR9Oi$ty;>65L34E zmK8Itcf!#D6AB}|O=T_aJg!eAz{ydrGver^Z)TvO$H$8krroU={SrwB`CT#)hvu-G z$_+hWIk3|Zpp>@>^ejHnsQ|rY?<1;*SYq<#-y{y44wJstv*9gIhlJgj`@az2#3`jF zeoqIhlBEw4dHs9{>~sRKAQh<@>)6}^sxt{eSK7jYW;~rvx0V81Nt+K5HjE=}%7#m3 z+_?fQY|>fP!)N2A!SGM>Sy_$=!wYysfytS|=W?g-llcHKN(Rk}hO=XB%5E<<{6s1XzB z4Ow2X_c4nJoZ{TAAdtU$5$pzWyq*bL@49hEj;t3$(+;ss*U=mk`m4*#a;4u2LDB!h zSFC=}kn1#hYpXckb$_XGpcBQfSwD1^dS=HZC8Dorq6B?*xUd6zV*pW}AhzJ_5qfD9 z4Hlh-JV<~-9ELzW{;ejuWO^iL_G*PyoL znSB55FR?M5hWfL52AklQ&)3OHbjB;6Sg4ewTqQb-0FW6{(cIBei;BwVZzUn=p<2Kd zt}sf6oNEQ@iiM$dYjPulp_Zu5#5g97Q8 zm{h$Q(dQ5J-z3Tp%rCBoFm&d$M5fZhGR9prZM+WVXdtOcqAH%O&&RgGk0iAKZ4Alh z-mCh}@N=)VI%h+?tj=A~A3xGT{fao59V^ZdxH4|l7TPe5(? zJY$nlb6m99Ga&QZxguev?0#2;rRw?-uq6m6z_32wi((l`a<|%v%6crKL%MGq@A>@M z%mZ@G<@#FBd0(p!)y8T}JSN)p_rV|Z)TE^$MJxB<~3C!3oO@D-2x7M z9FrWIx~xK#9+WF=22{mq8N4+C-WiWHKVtHl#MiGLR5ll9KImQ)Q6?UI_39*4#-lH9 zIO8uE?DhJ2Xwu6dh3Q5DA>`NnGtxATS0v0QRZ=QFK+m9XV+fB-&U#BJwH|oN z7Oa0(cTz-Q087e7E~l&|3N&&hKN(QDlzrir;}7gE=_M{=+8--#EGtFcFl(DP>l5H0 zo4;0Foc;m4Nm4mrk!sg5=|dC`z|)Cfi!XODmSXV9rtwol?ZHYn`l5F^MLk{z`OsQK z2J^9SVq=^CF&;HH%ySKYOGf<%8(Klc0K&vHr#`*Ci|FU}OcP!FOwJ?+? zTFq<0K3bS$FMYw9!o3zSE85!)98_XLsONZY_9YYhSZ<+_uqfyVM~)U#>3%^&?soGB zT{$|kjcUKN%Aw2u9=Z`~wVHu_veaxnOt??hI<%WVu8?XI*Xlpv!Sa`cv%J6SmKF`U zp1yWl?8_1HroTM8QSdkn6(vqgV!`mX+Xi3M!kMq*#8X_QzP=>gzY=SBfz|WRT8&Pb z;32AWXrXK%mp+Qyl8^25@y@f|0w#TM5$(LyeBX-RxeMrQ`v3Mjau14+_6FURP0B=gjeYB}lAT#>! zaQ&`Iki0wL(Z@itCfcAH}Bf-0Fv-FXyBI8zNIO`Hg0dX@Cw7I%zgA(_MsUdD+n6lDn-s3DG#Lo9;&vkT+ zK^>q{A34?!Wl2)W%v4{1;mGE?`#T%CE??)GSux<#+G;{(s44BjyJh)lD78WMUkzUS z+3UY?k-MUE%lJ3fdronu&|_QdgF#i&r#Odc$5-3k7yWTN23SF#&I8st7$)(FkYM4+ z70g}ppc~x(GEl$7rZTI3b<2zNwEdJuHA(oA>M4+x1Y> zLQ(vf2BbUiGk_W1v(Qh-3sF=bjC2c)!`?BYS1}Dp9hsbFh)v+$b_|;dX{+epSA|@I zyc#+d67G+bUM+dFs#j+G`Xf56?IF*vtqXApCPox5-_@|de{b#UCl*$fa$#a9By8mc zKTSQa*(f@5kozYe9C8p@s3gqJ5V!%dqt?%?=Q>)@uxewVQE%KTHbybCOU;5K>tpYW zKHL}oiT2_OeC5&F!mw%&1v!DKAO3tff;~O(vyZ{r;2FZoXL;-lQPjb|IYr4Bq$(Wq z{PUxGSU$@@TR?;OuC{AAmYLk>-g03jEf(aFkNSOJ2HaU#jxsQ+NOM_rvGbWXhwa(b zLwMKLT|{}UjGk=zow8?;Qh6_v+hZr>Hk)s=bj4$!}ocZr#eU~QLX zt57?}NsW|5bnza-ZvL0M=G>sl4?UK4X`r6fJo$m`YmADf*-MvcAuhyOJCF8Su^&9C zZ2SqdKTDt)iDw&UmVy>DaREAW!=WmPDabFMG8z+T;;45ds&f2dEnS28-4iFaApD_;*s&{wo z>Jg}-S2==h1cxcH+}zY$NTq<7Wz08B`!?2*5I_71P);C0w?*Z|=wZR(HuMQX^`3up zrh0ZcZ;Bx6t}qHy9Q1u_Iz=@jh{oUaIqg58EY>ynQ#+kY(cv+U(*St3G1zyBb2`1| zNagQbWd-30y4i2K48dH&4I4uR-f!#4X;`B!e6qsi$zcB>LVGTu@m1zH#9??h>`Fye z=XZ|WHHp=6$~tw62Y$=lbb0wDuq=r*Lu^be zsTvVAo1V%PnsjXD6$(^Kp zQOt5!tp0)J^ExaO2g_gmva*xa&N%Tl{~A@Mrjdc&r%5oAbn!8IUS_UdW4Y_kxsej5 zPLS|{*y2L&z%aQJRcs`>S=)W9Q{P9Toy>7;3wsGnFw7#WL*}(^3$|j09fYD}!q3d? z6i=6Z*=*0D-P_ZgA-&{dj?0DN<_1)m+25^H^lzraPJR&~E<7G?q}oeN%CzXLqj?$H z9o&}Je-aN?{)#y;?HEI4n#L#?^7&ed)F2BJ(f)z#?rCC4at7f{9E9&bb2$Ovlk)0c zS5jyY#`HDArmz-dq9-|Q@_sMzM|jp>d2 zKbrezSl;!T?R=+i<#!}tftb;-j({o69xNkIKhk|na*N?IepAt7yFZ6cm z^=`_b3ER<*WatGbgYRq0=*fY{uIB*j1Ea*2SRFz6@H2B`w5x&MZzThlFrcOO^Q>U9 z>sHjO7vI3W=);TRUh9TDx$CI~ePI6J+Q-c&l1fMiXiJ~r=P(4x5ZZ9&v_p0YCs~BBG z`&Du2eU%huotAbing}z`Hn>_TJ9e-izIOG)_koxu3V386f4aeMp~pN=jFT;rv#I1ZYzUB z!nfU)v=Q#*outIg&&ka69B}s#b73_E3$P>bJ(sTTc-KWv*4NkVml{fc?`?3@r6^h$ zwk+9cr;_L6@{98(R$ko#n4ytk6LpT{)y3dNqoB{#KqX<5B#lbkt{_9(p*=Vck#ZLW) zm#JcRy6wdEn8#iJjAVY$J(C?1cV_c*`)&;v`$cl`wG35olzqTeWLd;==WR zS*_(g$OGOZg+Q|csuJhy)Qn17c`yxNGpwJMUAHdG@5+6#8I@NhLdABPz}vHZ<)wtC z7o8&()kl2#Yw{PRyeITh_v@|nrpzPz2HV;Pq#JmQMAQk0Pez5SyjRISJfw*Jy{6E? zfgeO1O`|z2VjAIVTecqQ%=@aTcvaH6EQJp+gAD(bz0ZObSA3`+Ucyu`;$WQvvm@Vo zZUA~m9=S#ciDyXE zM?c~>yg=x1a}*S0M*N`HrZt)ClXbG1O`hDQ=|%q89jNyyIvD1JEnEl3et){N^E_9} z71hEpzyFG2AnIw|dNncg-~iG4Fw1kG;p8-Cw}$DEOT_=mFnzOI+KwgxJl+XA$<+zV z9f}rIYuF*xLT#6Yv?eoIy%*V-6FJP~VQ>GZds!1O*j+GT8!gMlA-YuW2pQ%}dWsW{ zdhTqq3JLLuNtT?Ao|vcQQ{%WPZa^bCsh{{{FA{c=Z3?%F51^IpNqZ*WykOMeqFLNcO%*CD_YQ8+=*4XtL$Cao4L#W+r=AIMa3mRB>f zx&fjHl!wMq#n}8P(P&tHQT}`2+|HlCXfZ z-Lf;iQDIvL-SF~O%3LOY?xDX#oTBd>D(7H$Kvj1?Ce=n0;$%h5ZK|qR*j==DcE#GP z|BNJm%Sg#ep!jNRYqIAk1OnjU1!ZF1da z-RArfu${82Sh$=h{5bwPXtr4kRCI2%Q9D<40Wy6WQ_Vg!>rwEl`7W&Rj`X1@Zkh(ngt8rgjcml5mOM^BuWWkGf8sFL-8IN?yRt@&>d6i-RSjVth zh(XAy_3!D1oSQ}znYOt~_$hlIhyw5#P(-F`Jjox>F50jkYGwzR{)jE1$rJ%!8kPn9 z{h;4;V57f}=(Cl>>8MqN7fDuK=|oDk5roqJR0BRFJ|M`JBnD0pfIv=Nz#JE$XA{OX zV%=XaM+{H(TqlKb(Go4?zXh5FN4`oP-msY#_M5MO55^c^$TvnDemIesw7AY)cbjj+ z3?l!r{j85*P_7Weir^bB8R ztPq{-X(bDgf)3<|W^$h_eAItI!p}!GXwRhrLC&LwWxy*T^#?uXXd=bo;^Rl5!zXV` zo$sI}!jX-_XGrbp93lj`rdpD5d-Iu7%6HW_erN3i$UniCOd6_;vHZWN@CQ?XUCcf= z{@U*P3x9!tR#U@eA@V+3kJn0>nAk(I*P;}bhk6@Y%+k(!Ve9J_gU_&Cc>v!T4uUun z+FO=G!1bP*#w*q>+Z6vu6&p*8>028q{jX&YcR?p-a$|MdFb%9To;@p#CB zRa3?0n|m>{ytUEc?H8gopc$qsfz{Y|+c_PwF^^J6yhv$Oy5gqL_Cn33R;4Ja^?M0& zZmRnF9rQqkUi3b$)Z&(DG>VZUSm^}peP$99iGC<5s5yBCZINZ_Ti$74beXi;vWY2; z4_96sTxt3`USqqcXTxMMfx_nK>ae92)1ZK?WLnK#;3e&-rJv#WyPakChC2Q8!Tvs+ zK!C+)y7f#i53v0%+6OlcYMw&!?Q%8)AZpQ9g)g{WeotAh%#VaMGw8^BzJKcHsr?3C z_9UyXO*0N}s-~)~BzH3feAN}Pa=f#|*mw(>lT#c7E!ekYd>|8s)zmpYz2V?SQt#yO zv3p>*;!JF|G-al{7(}9;^$$f(2eYTZe;Olv-U6*a>T7brdndAFTF=O$*?Nf)h{ob* zZ^1ut$Q`7x;scO>dP0>qy^#6csThMMFFw}Mu#^X~7*B4E4EF%Diwh|R%uYHm}!!2Wm?fZbzw$v3H( zRF>z4suQwSj)W3JLkmd7ZPFsEq2z{*0Yrn)fv`P~#JRu%&b!Oh8p!>BKxi39f(rp) z;V(QEq<=_hudJDYje_6o_b)I8-L_IFH*CE z(WAQz#76ERxVoAzg1h_y$@Ancea-VntA)t*$YS>RQE_6UM6om~3<)&{oPZt3gm7Cv zRpTq53}3(aXp6|QqGuc|Z&12o1Zk7)1#Y@n_S`Pm8}Y@VK$Zz}ARtOP`^{eR>f#>S z%BMHTO_NyG!h868o{LXl05i*K;>rYRQxZ`MAscZFOyjAfFNFH3R$-~+WE{$T zt3bVX#*`s8#Fr-4Qm=#dR}2xddzn@20_Yb$kh{_)e%v`j*&*PZaaFM#Mxh6LXs$F5J3r3+tNR%^k~iiDiEp zr?u<44G4YEO}5~p3&v^C{n|wMff-sZ_)u|BP$(cEJ z8svF#u}Da(i5eayz|-#wwRdFL6s+Ytj1$$~btXtu(#cSRhC`Ir23)* zGP~cb01bZqy?^qYk&{WoEM{V+(DC|OCozUA{9SK~itj(O(#eALiR5QVEZO_m^esZf zW?|`I0I&}uj-sPzHIUj1xax2?xBq6tfwjUN&NFgjhhib;$R^m@k9Z|~596NK(tw&{ z=+P8Vr5+W+^VG|9(Q>)N{km^DSod`@kOfxl*Xd!Wq?)}S;mV%h3$}N+IiuE^Z-KJZ zFT`st^rt;x}o`kZzsorl*sEQ*Dx0KWHK^FwsQF`n9XjsbIjs0;>a8 zs%UYE)1kxLF4Ef0=8z0QozE;t;ZS8hKBpZd;#VajKIJg~orIor&G<-ITozK%cgt`) zG?l)B8_8*4TyhZCp^19%v0O)I7ZowL?pNnlz_>nq_HOJ&y_?h38Q@xmW6~prKPi?( z#R&TXV5jBYDH-v5pGPC7DsmY%Girw1u(WqDldnSxaR#J9c)jSTo}4pNc}8kJ_4`hZ z=bHtJiTB=T;?I7bjQk@0JdOqSLg|gPgiB(KOj zBN0(1VXC_v%fa&f-`k%qjRv{YBVCt0-oRP^RNA{(Cq?@<*?5)rp*#%4lupgP^WjX6 z-y7qr!nNt+w4HnQCZc=ar#3|8RK-Q+etY`3e*1FvyRuj8_No6%d_AC0QC{6bwclnl zE-Yip2>bA9dz}7lL=Nc}^c^u@-0zl`@)S|!*W>G;fSYi^$&2M5X-J(L;)!E-(&M0; zLrD>L2yR!ngVho6C62v<1-C}Bj+BJQMstC1D0qMvT&R+if%O773sYL_1oDC2`Hx9ms zxMg*>%L*Pf!j#-_Hm686$fbb-%w?P6|FD;KXmD9o__^?o2ZpOl8$bm4o1vx-aV4B!1Ot-8`-anE|$5)-L zasi)!e%d8-ss>Og2o zTTcTkx)Do>N`%S4nFu%Zj#ciafwDG9xEfx)agegCrkQ2Vn4{eGYiV9Ugk%Xq*IQ zZNU~Qx9%?PVy(p<9|#@c--%Wqgp_*1vYz?CEfW5N(;U7|f;UzBK?m>Zs|6jtwX*f( zPb!C>d}!v36)p|1a?@d#!ms?#}Ccj+M! zj}|nF-q)+eWri?cy;v`O;oO#>darZ zKVMR6HDu<-2~WPTkVd{92Mfni9LlaXF`}KYJsUzEAy{!J5 zhxXTWF<~09Dx}E%()~r#^XQ@0=$ORdnsfKgh%<#Bf<=3ZJYOo48p&=d@oW-b0qM&J zd^Hp#E)44uR4lszFc1jbL}}X9#totk$1MFJ+|mz5ep$G4Sv=>8zX@2!1z1I%3_nHn zPLb$y@8_R1As0hqC(iBuQ9(!E!=qgDL5N-J|77_<;*xq4)?6_C(^b6j!(cncFaLeoIboX_ZL$?W%+4P>wDGHI(d(6cf?Yp z8Q;L#B>c2yguZoqq2g_B&*eVxz`)-6;f=kz($9GRA&ww_CXN6ZGnWg;7O>npTudVdN@`nQQgvQYI$)cO(?sTtGJ`mm#6e zL~+oY%TmV_f(^d;rgbio^PXjAJBFgFo0&cXUydb_mtG2(uPaZ!u$;O-_;P;M7lc06 z4+J-g*zjYm@b?6j;HP_ER_C}FG*#lY&z#hag`cn^s#$2|huKO2@=}s#sl|e~h6Dlc zoVm}1CChAUc55o9@#8Xv_6eS>DSZ^F6DguyRgH8VUv1x~lX^C()!!`@*BSnjNU`+XtzRxh zLMOPb6x{j5Jih!p6U}E}mG3ObUYHzxH`YWr&RiJn1MP<0(rcF6JoJ{F5{umUW4?hC6Q?SwuuY0R)!&KSlZ=^IYY!j-FbiD72QVfgb5N~wsSl6S?3FS_J~EI z1U^DM^j}eOAExpMU$Ka(J)z_)&&0EE4-!h`amm?4q!BJev%n@oJtmh@4DG{q2G}Ad zSP5+q4%_Wlj&wo>ZA;FqU)tCde3^YsnUQ^!gx|@IR_S5 zC)uEn0Z4cvAD|c&oJtAvmq)EjJvps<%OErtaP^vT(}gudj|2G^XzIXX0ZiWl`nG=z zl`mtkhaamvjR5#bsvw{({94iAE5j!K^XE#C3{_C3ehlw$+IF3g2#-ZwT5avH=7~tO z_(ES_-~l8=D^qQdDteTBVc4exFnoLm1BLV!hJIx_@|9$TZ8B3uu1`Sj^h0onrJUNGEb- zyX1!t_cWBv6(2^P=qCWC2%HPmnh$Qe#Q~z-a}_Y-Z~TnkU~d1oFbF`x(-dt;NA+eZ z4*-&9!@>1Y3Le%g5kN~*5A;9B}GLw&a;l#Z8^d6ExyI9Jm~#Vsi@ z8H0W@uORWZFt-Q16c>|t~ zK|U-UDRjU+-gk|@xbr|M?)V`(W5O-H^T4fOVl-6q2%mUMkvYm0I=0t>p_9iCVYp~- zvFr(onQliJg+_gb?T%nxj446>3nm_+G@XXV_ul?1SvTzw;Kdl@nf|FCS=Q`v{{M*j z%CIP-u4@!&X$6r|Is~Mo8%b#dX{EbsNJS;2rCS=LyFnTTn4!A_hK2!#8U}{%_Icj- zd-tFD0TG#!lrmZQj5d?YjqYPVeim{ z=j(5iNUWRRv~vf7kOK|%sGYW6q3|7j5tX2p@~hnMzJgtTHt%<-D7o$@@-pwgM}OR0 zZQuHUL`DZ#LJ%v4+15;b1gt^CN0z8>!HpQ7#|GTDE z%EmXV5$+rX&0Sr`7d&;~G*>=`Vi!WJ+BvPt(1%%>SwoR=4l_16+LU;JgbH3BzZu<& zzOT?ofA~5A?fy9tfX06~ncv61-X%R&96>ongd-muG-FoFIKOhs)mbf`Su<^9^1GU1 zNC8R~cb3$Gn%9BSl!+aR-GH_Tx0Sp|RUe{B+uR2wkZCQ`o=TRtkf0_?lE@X>J^uYM z!j5@>ems$E#~_uMkJ8S7t-Ar)oi1^)D*i>e-%L2&SP2}Os}7fDj>ZTF+pd5UAj8#E*C`jvM_5I zix@Gg2D31Qu_Pn!^xwaLd2F4dvhtgcq8Gww%(4(dXl?O(;VXz4ed+nXEAEJ8t<9NU z@vGoesY0I4yEh$Il1*<)y5rAO66ZL%bfZL^7`GQDt=&3V9A@=W^rBK^i`(HNWcLCf zd;qy%hO!>3Pe)v^nE$i3>8MDi1k28)xae9I$^zBc0=Lm$sm57x#W9B(w6_Qv&oiTM z0G_R4sC#v`S}L9qY(!FAv!euJ21C6Y`;!fwyf4)l1Z&=FYX|r!4P1pnUkuI7EQRzsE6T+ugQpl9Qin9^^o z_yn5tHTT0bGX_D2i5H=qCjdI}d6hfsN(+flt`Env9|9M`oTuTQJUAp<-BS-A-i=@a zG#^v{<;}%l$H9$&Gpb`i%rzap)#q#12jXlUr1lkrRgC=Mf-6&Ph3ag`q>^2U*~K5l zgsGV-LIqbdA1|j-a-v@KRi*VC{?ZR^AlngnzPdLX(mRaBEZ}sf&vJw;9wYC!yFz`7 zuKIvK*Y?6xH?qj{QJ~dC_?e%mxO5+x;dOIt$@$b9DQlnCML6msgQSSXg}4JIh(UG+$iz&txAa<}WVGe5%>=Lq zfMR`XIUt^l`)Y%=YQ%%%tB#4mhJ5zyT&bCZVHKG1{}RwwColGRISO|Rsl}2x^kl2w zK=sg*SiZ%MBCE^oCr4KjVPhMZuCwfU;tTqE$5Ns1Ceg-DNGVCR$kKNjt@W3it`BW^X2Gm$Mn4^?xLz+F<37vRH?PjX$;0Kzigl=BEJ>aPp`eI=EEG`+M zFiI>Raq7v*xIN<=l-+Fm**XEIl5`m9wU?yi)o5&N$9qs+;Gtdeg%!bA zbS0HQB}yIy$S?vl5EB1Np??l{58(|5#MyHgpCvsNFFPA$5JAzPtHjtx0q~Z!E+@k@ zWQU{5LU~xC2z-Cpaj(3_>aDjiI03U;@AOgw@&jlEEwUf~C=DTlzc*@Gn0CW%TgV2c zb;#Sl7Y}rH+7c3;(VataU^{Qs9SL6B))`k&hc`CBA2S4`(mGuU#+aLRc07PYMbUmmC zyj|4U4kvgs)hP6(m5pN4A;8Vjk3nFzeC^MLuB|R$(PQr*P@-*`Jc2&Pi9pb2DRE>f z9MQazTI*7;D^3npsShq1SSsQYV)nV2+kA3qtZ%Az9O&qGGEyhUE18A&A(65-|98cc z3cBJNse%b(RT^4a!?cY!{35-Y~v)nWw%Ouf8W-S@6>^dvqJ_cFix(e zw_38LnXq{9+pj8xe62B07AIXe+mM{9MOPAoSj^FvG8zfOM;(e~Zz)xtz+r17%cr-# z?Me0=6CEw9OcT#j0FHy!w(V6GZMw6G(ga;o>ETF0N_<4NhKXly$7u7Gz-8$M$afr% zCoAM=r1G1ug$}*9lVLz1q)iDqp_THf^2HKsu49cR^iAztY}V3d%jPZ*c<&!OY#`_LZ z7_zJof4RBq!oRVKgQj?T=Fd@*G4Z0*1u%NkcSHc>4vkN1knrzI1~t+OQESx?J0O_2;TvthibO0I^@B9hG!!OdJn7jDPuQ@cXp>Y>tN5pAP^nIJTL5Z6|KsGm(HB$2 zVg1q~k4yayr_F5qr)Zw%N%&kmDU5Ofg&K)AyxEMfxf$D)#xWS;iyq9#j=9}s@1p5L zlubH6i+yJsqqlcuC$^}B<`TPAry%!LnB4H*<>k^Ft2;bRmsQJ=gG>=uH=o`Lz zygngg9dW?v1u51iYV!D72f*ogxPB^W3~%xuWNUSXR{LS%( zdNxNE`!nT()z~>o@h;%$NBA$X(mybl=C}!ycqzYz$nT;El+Dy1fL-r>c|~Y8)Z&&m z9HG3Z)zjK8!N}%8XY*4>C1AWY?o-6~bD{9>sH_?)QqKYA29~^);VJ1yX>M7w=*x^R zuNT{gU)k7?#yV(aA6bw6eMalO(8#TTTDJuWjco+DB@WWcGL)`;P&^!$ha$Y9sH~eGZSMIJVelRz*m1ZU;;5Zoff!AN~+p_K>sTIH#Jd( zb>pp2O_#%c$rJ{Gwj_xynJ4%AQou&+cy*prczXO%A92~W#fWx)?K&;4IGO@^NGm8M zxk9u>1@!V#f~SrEn_*cxI-|i5^-pGrG=lUZ&zgOtu5P7><+T(iC`*S*j|i3#!S7B| z{RU+(TkVA@+EBTpd-azh|I==zBb3iNz~ zZJPm#%HPJ!h<6^HhbR`^W<(_OXlFW9?naNDpW3m-LS?BktuCx~*iUiTPRMB_MEY>mkBY&qs8m>KGCt4LcW)3ad5KA%= zp9Pj$T7$n8v`F-vD92@WF2UF++mp`!Yh?0{?j=qZft~hgn(4g-BRlV#NwTwLd5A|e z2$;0jU-$EMUYxW~V*>>erD=7PPwkOh-Q{p($sc5Are-Jt10JtmY#YHrZ59N>CiFdDmszsc4gjhZZ^cUcAzHKfO z5-dIRu`5m`v$CSDGr47E?wgthw9nLgv*ehkbBa8zTeKxBG^%v9FL??Wc97jxngRYs)W7`pFpP1*M3h{HPF&}m89euTInoJr*|cjBBE4{ z=x4x@3=gJ3{(0hNj~BAB-)D^E7*Drb6}q&R^dXPm>Frh{W7U~fAf{&=Q0P)bON+3( zX>na}LgS2-^&k-}!9-?iO2Els-%dHmo})&}`^|~3cn%p0xmoZRWR2~Qw@AG!hTpL^ z3@{6P@wGRm*o;Huz_8TD%~{qa>3+7ARUBlO!#J6Y{8T~8zW z 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 H;z%@@ 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�O1bimvc)4g3D8hb}_BYYf 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

b#p{>1(6?vPYzvY-1hZHBS+Ojv7 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�x$L&Gp~CNf)9QJ)qRpTrN2{GMieEW9 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

Lqm_cNPH&srELR7 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�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%0DynC5kaj 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 z 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

6$opV7za$H7)}YQS9=663ox5= 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

Gc3;V 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 literal 0 HcmV?d00001 diff --git a/public/assets/js/config.js b/public/assets/js/config.js new file mode 100644 index 0000000..90ae9b4 --- /dev/null +++ b/public/assets/js/config.js @@ -0,0 +1,93 @@ +"use strict"; +// Wiresphere config file +// Do not delete anything from this file! + +var WiresphereConfig = { + Background: { + SolidBg: { + Color: '#463760' // Solid background color + }, + + Picture: { + Enabled: true, // Background picture true/false + Opacity: '0.07' // Background picture opacity + }, + + Gradient: { + Enabled: true, // Background animated gradient true/false + StopAColor : '88,139,148', // Background animated gradient Stop A + StopBColor : '112,53,93', // Background animated gradient Stop B + Opacity : 0.4 // Background animated gradient opacity + }, + + + Sphere3D: { + lineColor: 0xffffff, // Color of sphere outline + lineOpacity: 0.5, // Opacity of front face of sphere + backlineOpacity: 0.05, // Opacity of backface of sphere + particleColor: 0xffffff, // Color of particles + particleOpacity: 0.5, // Opacity of particles + particleSize: 5, // Size of particles + particlesAmmount: 750, // Ammount of particles + moveSpeed: 0.07, // Speed of particles + cameraXMoveMax: 1.5, // Mouse movement max rotation + cameraYMoveMax: 0.75, // Mouse movement max rotation + cameraXMoveElastic: 0.02, // Mouse movement smoothness + cameraYMoveElastic: 0.02 // Mouse movement smoothness + }, + + Noise: { + Enabled: true, // Background noise true/false + Opacity: '0.05' // Background noise opacity + } + + }, + + Colors: { + JSOverride: true, // Set to false if you want to tweak CSS + + Tagline: { // Tagline + FirstLine: '#00e0ce', // Color of first line of tagline + SecondLine: '#ffffff' // Color of second line of tagline + }, + + Buttons: { // Buttons + Outline: '#00e0ce', // Color of buttons outline + Text: '#ffffff', // Color of buttons text + TextHover: '#ffffff' // Color of buttons text on hover + }, + + SideContent: { // Side content + Background: '#111111', // Background of side content + Text: '#ffffff', // Text color of side content + ContactIcons: '#333333', // Color of contact icons + } + + }, + + Map: { + Longtitude: -73.946, // X Map location + Latitude: 40.674, // X Map location + Style: [ // X Map style - refer to Google Maps for more details + { + "stylers": [ + { "saturation": -100 }, + { "lightness": -62 }, + { "gamma": 0.47 } + ] + },{ + "elementType": "labels.text", + "stylers": [ + { "color": "#ffffff" }, + { "visibility": "simplified" } + ] + },{ + "elementType": "labels.icon", + "stylers": [ + { "gamma": 2.17 } + ] + } + ] + } + +} \ No newline at end of file diff --git a/public/assets/js/jquery-2.2.4.min.js b/public/assets/js/jquery-2.2.4.min.js new file mode 100644 index 0000000..1b4ad81 --- /dev/null +++ b/public/assets/js/jquery-2.2.4.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.2.4 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="2.2.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){var b;if("object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype||{},"isPrototypeOf"))return!1;for(b in a);return void 0===b||k.call(a,b)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return h.call(b,a)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&f.parentNode&&(this.length=1,this[0]=f),this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?void 0!==c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?h.call(n(a),this[0]):h.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||n.uniqueSort(e),D.test(a)&&e.reverse()),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.removeEventListener("DOMContentLoaded",J),a.removeEventListener("load",J),n.ready()}n.ready.promise=function(b){return I||(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(n.ready):(d.addEventListener("DOMContentLoaded",J),a.addEventListener("load",J))),I.promise(b)},n.ready.promise();var K=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)K(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},L=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function M(){this.expando=n.expando+M.uid++}M.uid=1,M.prototype={register:function(a,b){var c=b||{};return a.nodeType?a[this.expando]=c:Object.defineProperty(a,this.expando,{value:c,writable:!0,configurable:!0}),a[this.expando]},cache:function(a){if(!L(a))return{};var b=a[this.expando];return b||(b={},L(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[b]=c;else for(d in b)e[d]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=a[this.expando];if(void 0!==f){if(void 0===b)this.register(a);else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in f?d=[b,e]:(d=e,d=d in f?[d]:d.match(G)||[])),c=d.length;while(c--)delete f[d[c]]}(void 0===b||n.isEmptyObject(f))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!n.isEmptyObject(b)}};var N=new M,O=new M,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/[A-Z]/g;function R(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?n.parseJSON(c):c; +}catch(e){}O.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|&#?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ja(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ha;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return"undefined"!=typeof n&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(G)||[""],j=b.length;while(j--)h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.hasData(a)&&N.get(a);if(r&&(i=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&N.remove(a,"handle events")}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(N.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,la=/\s*$/g;function pa(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function qa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function ra(a){var b=na.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function sa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(N.hasData(a)&&(f=N.access(a),g=N.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}O.hasData(a)&&(h=O.access(a),i=n.extend({},h),O.set(b,i))}}function ta(a,b){var c=b.nodeName.toLowerCase();"input"===c&&X.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function ua(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&ma.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),ua(f,b,c,d)});if(o&&(e=ca(b,a[0].ownerDocument,!1,a,d),g=e.firstChild,1===e.childNodes.length&&(e=g),g||d)){for(h=n.map(_(e,"script"),qa),i=h.length;o>m;m++)j=e,m!==p&&(j=n.clone(j,!0,!0),i&&n.merge(h,_(j,"script"))),c.call(a[m],j,m);if(i)for(k=h[h.length-1].ownerDocument,n.map(h,ra),m=0;i>m;m++)j=h[m],Z.test(j.type||"")&&!N.access(j,"globalEval")&&n.contains(k,j)&&(j.src?n._evalUrl&&n._evalUrl(j.src):n.globalEval(j.textContent.replace(oa,"")))}return a}function va(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(_(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&aa(_(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(ka,"<$1>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=_(h),f=_(a),d=0,e=f.length;e>d;d++)ta(f[d],g[d]);if(b)if(c)for(f=f||_(a),g=g||_(h),d=0,e=f.length;e>d;d++)sa(f[d],g[d]);else sa(a,h);return g=_(h,"script"),g.length>0&&aa(g,!i&&_(a,"script")),h},cleanData:function(a){for(var b,c,d,e=n.event.special,f=0;void 0!==(c=a[f]);f++)if(L(c)){if(b=c[N.expando]){if(b.events)for(d in b.events)e[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);c[N.expando]=void 0}c[O.expando]&&(c[O.expando]=void 0)}}}),n.fn.extend({domManip:ua,detach:function(a){return va(this,a,!0)},remove:function(a){return va(this,a)},text:function(a){return K(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.appendChild(a)}})},prepend:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(_(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return K(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!la.test(a)&&!$[(Y.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(_(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return ua(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(_(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),f=e.length-1,h=0;f>=h;h++)c=h===f?this:this.clone(!0),n(e[h])[b](c),g.apply(d,c.get());return this.pushStack(d)}});var wa,xa={HTML:"block",BODY:"block"};function ya(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function za(a){var b=d,c=xa[a];return c||(c=ya(a,b),"none"!==c&&c||(wa=(wa||n("

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">UTwwV-63iK&QG961%n8=ysNiQ3rrzvohfznD-l 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