File: /var/www/vhost/disk-apps/teamdemo.sports-crowd.com/app/Services/ExperienceService.php
<?php
namespace App\Services;
use App\AcademyUser;
use App\Address;
use App\Core\Experience\Domain\ValueObjects\ExperiencePaymentStatusEnum;
use App\Core\Experience\Domain\ValueObjects\PlanSchemeEnum;
use App\Core\Payment\PaymentStatusEnum;
use App\Erp;
use App\ErpLog;
use App\ErpParameter;
use App\Http\Controllers\CollectionInvoiceController;
use App\Http\Controllers\Controller;
use App\Http\Controllers\ERPBaseController;
use App\Http\Controllers\NotificationsController;
use App\Http\Controllers\ServiceChargeController;
use App\Http\Controllers\UtilController;
use App\Mail\ExperienceConfirmed;
use App\Models\Experience\Experience;
use App\Models\Experience\ExperiencePayment;
use App\Models\Experience\ExperiencePlan;
use App\Models\Experience\ExperiencePlanPrice;
use App\Models\Experience\ExperienceUser;
use App\PaymentMethod;
use App\Services\AcademyService;
use Carbon\Carbon;
use DateInterval;
use DateTime;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use stdClass;
class ExperienceService
{
private $academyService;
private $util;
public function __construct()
{
$this->academyService = new AcademyService;
$this->util = new UtilController();
}
public function get(Request $request)
{
if (!$request->has('show_in')) {
$request->merge(['show_in' => ['all', 'app']]);
}
$experiences = $this->getExperienceQuery($request)->get();
$availableExperiences = [];
foreach ($experiences as $experience) {
$experiencePayments = $this->experiencePaymentUser($experience->id);
$experience->buy_amount = $experiencePayments ? $experiencePayments->buy_amount : 0;
$experience->available = $experiencePayments ? $experiencePayments->available > 0 : true;
$availableExperiences[] = $experience;
}
return $availableExperiences;
}
public function detail(Request $request)
{
$subQuery = '';
if (Auth::check()) {
$subQuery = ' - IFNULL((
SELECT SUM(ep.amount) / MAX(CASE
WHEN ep_plan.entries_per_purchase = 0
THEN 1
ELSE ep_plan.entries_per_purchase
END)
FROM experience_payments ep
JOIN experience_plan_prices epp ON epp.id = ep.experience_plan_price_id
JOIN experience_plans ep_plan ON ep_plan.id = epp.experience_plan_id
JOIN experience_users eu ON eu.id = ep.experience_user_id
WHERE ep.status != "EXPIRED"
AND epp.experience_plan_id = experience_plans.id
AND eu.user_id = ' . Auth::user()->id . '
), 0)';
}
$buyLimitRaw = '
CAST(
GREATEST(
experience_plans.buy_limit' . $subQuery . ',
0) AS UNSIGNED
) AS buy_limit
';
$query = Experience::select(
'experiences.id',
'experiences.name',
'experiences.icon',
'experiences.short_description',
'experiences.image',
'experiences.description',
'experiences.apply_to',
'experiences.includes',
'experiences.when',
'experiences.plan_scheme',
'experiences.total_capacity',
'experiences.available_slots',
'experiences.buy_limit',
'experiences.term_cons_url',
'experiences.event_place',
'experiences.start_datetime',
'experiences.experience_type',
'experiences.enable_raffle',
)
->with([
'plans' => function ($q) use ($buyLimitRaw) {
$q->select(
'experience_plans.id',
'experience_plans.name',
'experience_plans.description',
'experience_plans.type',
'experience_plans.experience_id',
'experience_plans.total_capacity',
'experience_plans.available_slots',
'experience_plans.entries_per_purchase',
'experience_plans.display_index',
DB::raw($buyLimitRaw),
)
->join('experiences', 'experiences.id', '=', 'experience_plans.experience_id');
},
'plans.prices',
'academy_plans',
'academy_plans.prices'
])
->groupBy('experiences.id');
$experience = $query->findorFail($request->id);
if ($experience) {
$experiencePayments = $this->experiencePaymentUser($experience->id);
$experience->buy_amount = $experiencePayments ? $experiencePayments->buy_amount : 0;
}
return $experience;
}
public function payments(Request $request)
{
$userId = Auth::user()->id;
$payments = ExperiencePayment::select(
'experiences.id AS experience_id',
'experience_payments.id',
'experiences.name',
'experiences.icon',
'experiences.image',
'experiences.available_slots',
'experiences.active AS experience_active',
DB::raw("CONCAT(experience_users.first_name, ' ', experience_users.last_name) AS student"),
'experience_users.document AS document',
'experience_plans.name AS plan',
DB::raw('IFNULL(payment_transactions.payment_date, "") AS payment_date'),
'experience_payments.amount',
'experience_payments.subtotal',
'experience_payments.service_charge',
'experience_payments.total',
'experience_payments.payment_due_date',
'experience_payments.status',
DB::raw('true AS enable_pay'),
DB::raw('false AS enable_renew'),
'experiences.start_datetime',
'experience_payments.raffle',
'experience_payments.updated_at',
)->with(['experience_user', 'buyer', 'entrances'])
->leftjoin('payment_transactions', 'payment_transactions.id', '=', 'experience_payments.payment_transaction_id')
->join('experience_users', 'experience_users.id', '=', 'experience_payments.experience_user_id')
->join('experience_plan_prices', 'experience_plan_prices.id', '=', 'experience_payments.experience_plan_price_id')
->join('experience_plans', 'experience_plans.id', '=', 'experience_plan_prices.experience_plan_id')
->join('experiences', 'experiences.id', '=', 'experience_plans.experience_id')
->where('experience_users.experience_type', $request->type)
->where('experience_users.user_id', $userId)
->where('experience_payments.status', '!=', ExperiencePaymentStatusEnum::EXPIRED)
->groupBy('experience_payments.id')
->orderBy('experiences.active', 'DESC')
->orderBy('experience_payments.id', 'DESC')
->get();
$validPayments = [];
foreach ($payments as $payment) {
$payment->confirmed = in_array($payment->status, [ExperiencePaymentStatusEnum::CONFIRMED, ExperiencePaymentStatusEnum::OVERSOLD]);
$payment->active = $payment->status == ExperiencePaymentStatusEnum::CONFIRMED && $payment->experience_active;
if (!$payment->confirmed) {
$experiencePayments = $this->experiencePaymentUser($payment->experience_id);
if ($payment->experience_active && $payment->available_slots && (!$experiencePayments || $experiencePayments->available)) {
$validPayments[] = $payment;
}
} else {
$validPayments[] = $payment;
}
}
return $validPayments;
}
public function enroll(Request $request)
{
$data = $this->getData($request);
$validation = $this->validateData($data);
if (!is_bool($validation)) {
return $validation;
}
$experienceUser = $this->createExperienceUser($data);
$payment = $this->createPayment($data, $experienceUser);
if ($payment->status == ExperiencePaymentStatusEnum::CONFIRMED) {
$this->confirmExperiencePayment($payment);
}
return response(array('r' => true, 'd' => $payment));
}
public function renew(Request $request)
{
$data = $this->getData($request);
$experiencePaymentId = $data['experiencePaymentId'];
$payment = $this->renewPayment($experiencePaymentId);
return response(array('r' => true, 'd' => $payment));
}
public function students(Request $request)
{
$students = [];
if ($request->type == 'academy') {
$students = $this->academyStudents();
} else {
$students = ExperienceUser::select(
'experience_users.first_name',
'experience_users.last_name',
'document_types.alias AS document_type',
'document',
)->join('document_types', 'document_types.id', '=', 'experience_users.document_type_id')
->where('user_id', Auth::user()->id)
->where('experience_type', $request->type)
->orderBy('name', 'ASC')
->get()->toArray();
array_unshift($students, [
'first_name' => Auth::user()->first_name,
'last_name' => Auth::user()->last_name,
'document_type' => Auth::user()->documentType ? Auth::user()->documentType->alias : '',
'document' => Auth::user()->document
]);
$students = collect($students)->unique('document')->values()->all();
$students[] = [
'first_name' => 'Nuevo',
'document' => 0
];
}
return $students;
}
public function guardians(Request $request)
{
$guardians = ExperienceUser::select(
'guardian_name',
'guardian_document',
'guardian_email',
'guardian_phone',
)->where('user_id', Auth::user()->id)
->where('experience_type', $request->type)
->whereNotNull('guardian_document')
->groupBy([
'guardian_name',
'guardian_document',
'guardian_email',
'guardian_phone'
])
->orderBy('guardian_name', 'ASC')
->get();
return $guardians;
}
public function finishPayment($paymentTransaction)
{
if ($paymentTransaction->state == PaymentStatusEnum::CONFIRMED) {
$experiencePayment = ExperiencePayment::with(
'experience_user',
'payment_transaction',
'experience_plan_price.plan.experience'
)->where('payment_transaction_id', $paymentTransaction->id)->first();
$experienceUser = $experiencePayment->experience_user;
$user = $experienceUser->user;
$this->confirmExperiencePayment($experiencePayment);
$message = trans('experiences.messages.confirmation_payment', [
'payment_transaction_reference' => $paymentTransaction->reference,
'experience_description' => $experiencePayment->description,
'experience_user_name' => ($experienceUser->first_name . ' ' . $experienceUser->last_name),
'experience_user_document' => $experienceUser->document
]);
$notificationsController = new NotificationsController;
$notificationsController->createSystemNotification($message, $user->id);
try {
$invoice = new CollectionInvoiceController();
$invoice->validateCreditCollectionInvoice($paymentTransaction->gateway_payments_id, $paymentTransaction->reference, $paymentTransaction->gateway_transaction_id, 'experience', $experiencePayment->total);
// $pay = new LealPayController();
// $pay->validateUserPointsAccumulation($order);
} catch (\Throwable $th) {
$data = [
'experiencePayment' => $experiencePayment,
'message' => $th->getMessage(),
'getFile' => $th->getFile(),
'getLine' => $th->getLine(),
];
$util = new UtilController;
$util->logFile(json_encode($data));
}
// Sincronizar pago al ERP
// $this->syncERP($academyTournamentPayment);
}
}
public function getUser($experiencePaymentId)
{
$experiencePayment = ExperiencePayment::find($experiencePaymentId);
if (!$experiencePayment) {
$user = new stdClass();
$user->id = 0;
$user->first_name = '';
$user->last_name = '';
$user->document = '';
return $user;
}
return $this->mapUserData($experiencePayment);
}
private function experiencePaymentUser($experienceId)
{
if (!Auth::user()) {
return null;
}
return ExperiencePayment::select(
DB::raw('CAST(IF(MAX(experience_payments.status) = "CONFIRMED", SUM(experience_payments.amount), 0) AS UNSIGNED) AS buy_amount'),
DB::raw('
CASE
WHEN experiences.plan_scheme = ' . PlanSchemeEnum::AVAILABILITY_BY_EXPERIENCE . ' THEN
IF(experiences.available_slots - IF(MAX(experience_payments.status) = "CONFIRMED", SUM(experience_payments.amount), 0) >= 0, true, false)
WHEN experiences.plan_scheme = ' . PlanSchemeEnum::AVAILABILITY_BY_PLAN . ' THEN
IF(SUM(experience_plans.available_slots) - IF(MAX(experience_payments.status) = "CONFIRMED", SUM(experience_payments.amount), 0) >= 0, true, false)
ELSE false
END AS available
')
)
->leftjoin('experience_plan_prices', 'experience_payments.experience_plan_price_id', '=', 'experience_plan_prices.id')
->leftjoin('experience_plans', 'experience_plan_prices.experience_plan_id', '=', 'experience_plans.id')
->leftjoin('experience_users', 'experience_payments.experience_user_id', '=', 'experience_users.id')
->leftjoin('experiences', 'experiences.id', '=', 'experience_plans.experience_id')
->where('experience_plans.experience_id', $experienceId)
->where('experience_users.user_id', Auth::user()->id)
->where('experience_payments.status', 'CONFIRMED')
->groupBy('experiences.id')
->first();
}
private function mapUserData($experiencePayment)
{
$user = new stdClass();
$user->id = $experiencePayment->id;
$user->first_name = $experiencePayment->experience_user->first_name;
$user->last_name = $experiencePayment->experience_user->last_name;
$user->document = $experiencePayment->experience_user->document;
$user->email = $experiencePayment->experience_user->email;
return $user;
}
public function confirmExperiencePayment($experiencePayment)
{
$this->handleRaffleForExperiencePayment($experiencePayment);
if ($experiencePayment->status == ExperiencePaymentStatusEnum::EXPIRED) {
$updated = Experience::where('id', $experiencePayment->experience_plan_price->plan->experience_id)
->where('available_slots', '>', 0)
->decrement('available_slots', $experiencePayment->amount);
if (!$updated) {
$experiencePayment->status = ExperiencePaymentStatusEnum::OVERSOLD;
$experiencePayment->save();
} else {
$experiencePayment->status = ExperiencePaymentStatusEnum::CONFIRMED;
}
} else {
$experiencePayment->status = ExperiencePaymentStatusEnum::CONFIRMED;
}
$experiencePayment->save();
$sendEmail = $experiencePayment->experience_plan_price->plan->experience->send_email;
if ($sendEmail && $experiencePayment->status == ExperiencePaymentStatusEnum::CONFIRMED) {
Mail::send(new ExperienceConfirmed($experiencePayment->id));
}
$this->syncERP($experiencePayment);
}
private function validateData($data)
{
// Agregar validaciones de información y si ya existe el estudiante
if (!isset($data['plan']) || ExperiencePlanPrice::find($data['plan']['plan_price_id']) == null) {
return response(array('r' => false, 'm' => __('experiences.messages.no_experience_plan_selected')));
}
return true;
}
private function getData($request)
{
return $request->all();
}
private function createExperienceUser($data)
{
if (ExperienceUser::where('document', $data['document'])->where('user_id', '=', Auth::user()->id)->exists()) {
return ExperienceUser::where('document', $data['document'])->where('user_id', '=', Auth::user()->id)->first();
} else if (isset($data['experience_type']) && $data['experience_type'] == 'academy') {
$data = $this->mapAcademyData($data);
} else if (!isset($data['email'])) {
$data = $this->mapExperienceUserData($data);
}
$experienceUser = new ExperienceUser;
$experienceUser->first_name = $data['first_name'] ? strtoupper($data['first_name']) : '';
$experienceUser->last_name = $data['last_name'] ? strtoupper($data['last_name']) : '';
$experienceUser->birthdate = $data['dob'] ?? $data['birthdate'] ?? null;
$experienceUser->document_type_id = $data['document_type_id'] ?? $data['document_type'];
$experienceUser->document = $data['document'];
$experienceUser->email = $data['email'];
$experienceUser->phone = is_array($data['phone']) ? $data['phone']['number'] : $data['phone'];
$experienceUser->dial_code = is_array($data['phone']) ? $data['phone']['dialCode'] : null;
$experienceUser->country_code = is_array($data['phone']) ? $data['phone']['countryCode'] : null;
// Enviar almacenar el base64 de la imagen
if (isset($data['fileIdentificationDocument']) && $data['fileIdentificationDocument']) {
$experienceUser->identification_file = $this->validateDataDocument($data['fileIdentificationDocument'], $data['document'] . '_identification_document');
}
if (isset($data['fileInsuranceDocument']) && $data['fileInsuranceDocument']) {
$experienceUser->insurance_file = $this->validateDataDocument($data['fileInsuranceDocument'], $data['document'] . '_insurance_document');
}
if ($this->isUnder18YearsOld($experienceUser->birthdate)) {
if (isset($data['guardianFirstName']) && $data['guardianFirstName'])
$experienceUser->guardian_first_name = strtoupper($data['guardianFirstName']);
if (isset($data['guardianLastName']) && $data['guardianLastName'])
$experienceUser->guardian_last_name = strtoupper($data['guardianLastName']);
if (isset($data['guardianDocument']) && $data['guardianDocument'])
$experienceUser->guardian_document = $data['guardianDocument'];
if (isset($data['guardianEmail']) && $data['guardianEmail'])
$experienceUser->guardian_email = $data['guardianEmail'];
if (isset($data['guardianPhone']) && $data['guardianPhone']) {
$experienceUser->guardian_phone = is_array($data['guardianPhone']) ? $data['guardianPhone']['number'] : $data['guardianPhone'];
$experienceUser->guardian_dial_code = is_array($data['guardianPhone']) ? $data['guardianPhone']['dialCode'] : null;
$experienceUser->guardian_country_code = is_array($data['guardianPhone']) ? $data['guardianPhone']['countryCode'] : null;
}
}
$experienceUser->user_id = Auth::user()->id;
$experienceUser->save();
return $experienceUser;
}
private function createPayment($data, $experienceUser)
{
$experiencePlanPrice = ExperiencePlanPrice::find($data['plan']['plan_price_id']);
if (!$this->experienceIsActive($experiencePlanPrice->plan->experience)) {
throw new Exception(__('experiences.messages.experience_is_not_active'), 403);
}
$availableSlots = $this->experienceAvailableSlots($experiencePlanPrice->plan);
if (!$availableSlots) {
throw new Exception(__('experiences.messages.no_available_slots'), 403);
}
$amount = ($data['amount'] ?? 1);
$entries = $this->calculatePurchaseEntries($experiencePlanPrice->plan, $amount);
$userExperienceTotalPurchases = ExperiencePayment::join('experience_users', 'experience_payments.experience_user_id', '=', 'experience_users.id')
->where([
['experience_plan_price_id', $data['plan']['plan_price_id']],
['experience_users.user_id', $experienceUser->user_id],
['experience_payments.status', '!=', ExperiencePaymentStatusEnum::EXPIRED]
])->sum('amount');
if (($userExperienceTotalPurchases + $entries) > ($experiencePlanPrice->plan->buy_limit * ($experiencePlanPrice->plan->entries_per_purchase > 0 ? $experiencePlanPrice->plan->entries_per_purchase : 1))) {
throw new Exception(__('experiences.messages.no_slots_available_for_user'), 403);
} else if ($availableSlots < $entries) {
throw new Exception(__('experiences.messages.slots_exceeds_limit'), 403);
}
$this->decreaseAvailableSlots($experiencePlanPrice->plan, $entries);
$controller = new ServiceChargeController;
$serviceCharge = 0;
$serviceChargeEnabled = $controller->validateServiceCharge();
if ($serviceChargeEnabled) {
$request = new Request([
'services' => 'experience',
]);
$serviceCharge = $controller->calculateServiceCharge($request);
}
$price = floatval($data['plan']['price']);
$price = $price * $amount;
if ($serviceCharge && str_contains($serviceCharge . '', '%')) {
$serviceCharge = (floatval(str_replace('%', '', $serviceCharge)) / 100) * $price;
}
$billingAddressId = $this->createBillingAddress($data);
$payment = ExperiencePayment::create(
[
'payment_identifier' => intval(Carbon::now()->getPreciseTimestamp(3)),
'description' => $experiencePlanPrice->description,
'status' => $experiencePlanPrice->price > 0 ? 'PENDING' : 'CONFIRMED',
'amount' => $entries,
'price' => $price,
'price_discount' => 0,
'discount' => 0,
'subtotal' => $price,
'service_charge' => $serviceCharge,
'total' => $price + $serviceCharge,
'payment_due_date' => $experiencePlanPrice->plan->experience->start_datetime ? Carbon::parse($experiencePlanPrice->plan->experience->start_datetime)->toDateString() : null,
'experience_plan_price_id' => $experiencePlanPrice->id,
'experience_user_id' => $experienceUser->id,
'billing_first_name' => $data['billing_first_name'] ?? null,
'billing_last_name' => $data['billing_last_name'] ?? null,
'billing_document_type_id' => $data['billing_document_type_id'] ?? null,
'billing_document' => $data['billing_document'] ?? null,
'billing_email' => $data['billing_email'] ?? null,
'billing_phone' => isset($data['billing_phone']) ? (is_array($data['billing_phone']) ? ($data['billing_phone']['number'] ?? null) : $data['billing_phone']) : null,
'billing_dial_code' => (isset($data['billing_phone']) && is_array($data['billing_phone'])) ? ($data['billing_phone']['dialCode'] ?? null) : null,
'billing_country_code' => (isset($data['billing_phone']) && is_array($data['billing_phone'])) ? ($data['billing_phone']['countryCode'] ?? null) : null,
'billing_address' => $billingAddressId ?? null,
'term_cons' => $data['term_cons'] ?? true,
'data_policy' => $data['data_policy'] ?? true,
'expires_at' => Carbon::now()->addMinutes(30), // TODO: Configurar tiempo de expiración
'sync_with_erp' => false,
'purchase_origin' => $data['origin'] ?? 'app',
'referral_code' => $data['referral_code'] ?? null,
]
);
$payment->companions()->attach([$experienceUser->id => ['buyer' => true]]);
return $payment;
}
private function calculatePurchaseEntries(ExperiencePlan $plan, $amount)
{
$experience = $plan->experience;
if ($this->experiencePlanScheme($experience) == PlanSchemeEnum::AVAILABILITY_BY_EXPERIENCE) {
return $plan->entries_per_purchase * $amount;
} else {
return $amount;
}
}
private function experienceIsActive($experience)
{
return $experience->active;
}
public function experiencePlanScheme(Experience $experience)
{
return $experience->plan_scheme;
}
private function experienceAvailableSlots(ExperiencePlan $plan)
{
$experience = $plan->experience;
if ($this->experiencePlanScheme($experience) == PlanSchemeEnum::AVAILABILITY_BY_EXPERIENCE) {
return $experience->available_slots;
} else {
return $plan->available_slots;
}
}
private function renewPayment($experiencePaymentId)
{
$experiencePayment = ExperiencePayment::find($experiencePaymentId);
$data = $experiencePayment->toArray();
$data['id'] = null;
$data['payment_identifier'] = intval(Carbon::now()->getPreciseTimestamp(3));
$data['payment_due_date'] = null;
$data['payment_transaction_id'] = null;
$data['sync_with_erp'] = false;
$newExperiencePayment = ExperiencePayment::create($data);
return $newExperiencePayment;
}
private function isUnder18YearsOld($birthdate)
{
$birthdateFormat = new DateTime($birthdate);
$now = new DateTime();
$minimumDate = $now->sub(new DateInterval('P18Y'));
return $birthdateFormat > $minimumDate;
}
private function validateDataDocument($fileBase64, $fileName)
{
if (!str_contains($fileBase64, 'data:image')) {
return $fileBase64;
}
return $this->storeObjectToS3($fileBase64, $fileName);
}
private function storeObjectToS3($fileBase64, $fileName)
{
$streamResource = $this->storeBase64ResourceToStreamResource($fileBase64);
$extension = $this->extensionFromBase64Resource($fileBase64);
$filenameToStore = $fileName . '.' . $extension;
Storage::disk('s3')->put(config('s3.default') . '/experience/' . $filenameToStore, $streamResource, 'public');
$url = config('filesystems.disks.s3.url') . '/experience/' . $filenameToStore;
return $url;
}
private function storeBase64ResourceToStreamResource($dataUrl)
{
$base64String = preg_replace('#^data:image/\w+;base64,#i', '', $dataUrl);
$binaryData = base64_decode($base64String);
$stream = fopen('php://memory', 'r+');
fwrite($stream, $binaryData);
rewind($stream);
return $stream;
}
private function extensionFromBase64Resource($dataUrl)
{
preg_match('#^data:image/(\w+);base64,#i', $dataUrl, $matches);
$extension = isset($matches[1]) ? $matches[1] : null;
return $extension;
}
private function validateAcademyStudents()
{
return $this->academyService->validateAcademyStudents();
}
private function academyStudents()
{
return $this->academyService->academyStudents();
}
private function mapAcademyData($data)
{
return $this->academyService->mapAcademyData($data);
}
private function mapExperienceUserData($data)
{
$user = Auth::user();
if ($user) {
$data['first_name'] = $user->first_name;
$data['last_name'] = $user->last_name;
$data['dob'] = $user->userInfo->dob;
$data['document_type_id'] = $user->document_type_id;
$data['document'] = $user->document;
$data['email'] = $user->email;
$data['phone'] = $user->phone;
$data['fileIdentificationDocument'] = 'pending';
$data['fileInsuranceDocument'] = 'pending';
$data['guardianFirstName'] = $user->first_name;
$data['guardianLastName'] = $user->last_name;
$data['guardianDocument'] = $user->document;
$data['guardianEmail'] = $user->email;
$data['guardianPhone'] = $user->phone;
}
return $data;
}
private function decreaseAvailableSlots(ExperiencePlan $plan, $amount)
{
$experience = $plan->experience;
if ($this->experiencePlanScheme($experience) == PlanSchemeEnum::AVAILABILITY_BY_EXPERIENCE) {
$updated = Experience::where('id', $experience->id)
->where('available_slots', '>=', $amount)
->decrement('available_slots', $amount);
} else {
$updated = ExperiencePlan::where('id', $plan->id)
->where('available_slots', '>=', $amount)
->decrement('available_slots', $amount);
}
if (!$updated) {
throw new Exception("No hay cupos disponibles");
}
}
private function createBillingAddress($data)
{
$billingAddressId = null;
if (isset($data['address'])) {
$address = Address::create([
'direction' => $data['address']['address'],
'user_id' => Auth::user()->id,
'active' => true,
'lat' => $data['address']['lat'],
'long' => $data['address']['lng'],
]);
$billingAddressId = $address->id;
}
return $billingAddressId;
}
public function experienceActive($experiencePaymentId)
{
$experience = $this->getExperienceByPaymentId($experiencePaymentId);
return $experience ? true : false;
}
public function getExperienceByPaymentId($experiencePaymentId)
{
$experience = ExperiencePayment::select(
'experiences.id',
'experiences.name',
'experience_plans.name as plan_name',
'experience_payments.amount',
'experience_payments.total'
)
->join('experience_plan_prices', 'experience_plan_prices.id', '=', 'experience_payments.experience_plan_price_id')
->join('experience_plans', 'experience_plans.id', '=', 'experience_plan_prices.experience_plan_id')
->join('experiences', 'experiences.id', '=', 'experience_plans.experience_id')
->where('experience_payments.id', $experiencePaymentId)
->where('experiences.active', 1)
->groupBy('experiences.id', 'experience_plans.name', 'experience_payments.amount', 'experience_payments.total')
->first();
return $experience;
}
public function getExperienceQuery($request)
{
$query = Experience::select(
'experiences.id',
'experiences.name',
'experiences.icon',
'experiences.short_description',
'experiences.image',
'experiences.apply_to',
'experiences.includes',
'experiences.when',
'experiences.plan_scheme',
'experiences.total_capacity',
'experiences.available_slots',
'experiences.buy_limit',
'experiences.term_cons_url',
'experiences.experience_type',
'experiences.event_place',
'experiences.is_sponsored',
'experiences.start_datetime',
'experiences.enable_raffle',
)
->with('plans', 'plans.prices')
->with('academy_plans', 'academy_plans.prices')
->leftjoin('experience_tags', 'experiences.id', '=', 'experience_tags.experience_id')
->leftJoin('tags', function ($join) {
$join->on('tags.id', '=', 'experience_tags.tag_id')
->where('tags.active', 1);
})
->leftjoin('user_tags', 'user_tags.tag_id', '=', 'experience_tags.tag_id')
->where(function ($q) {
if (Auth::check()) {
$userId = Auth::user()->id;
$q->where('user_tags.user_id', $userId)
->orWhereNull('experience_tags.tag_id')
->orWhereNull('tags.id');
} else {
$q->whereNull('experience_tags.tag_id')
->orWhereNull('tags.id');
}
})
->where('experiences.active', 1)
->whereIn('show_in', $request->show_in)
->where(function ($query) {
$query->whereHas('plans')->orwhereHas('academy_plans');
})
->groupBy('experiences.id')
->orderBy('experiences.id', 'ASC');
if ($request->type) {
$query->where('experiences.experience_type', $request->type);
}
return $query;
}
public function saveCompanion($experienceId, $companion)
{
$experienceUser = ExperienceUser::updateOrCreate([
'id' => $companion['id']
], [
'first_name' => $companion['first_name'],
'last_name' => $companion['last_name'],
'document_type_id' => $companion['document_type_id'],
'document' => $companion['document'],
'email' => $companion['email'],
'phone' => is_array($companion['phone']) ? $companion['phone']['number'] : $companion['phone'],
'user_id' => Auth::user()->id,
'dial_code' => is_array($companion['phone']) ? $companion['phone']['dialCode'] : null,
'country_code' => is_array($companion['phone']) ? $companion['phone']['countryCode'] : null,
]);
ExperiencePayment::find($experienceId)->companions()
->syncWithoutDetaching([$experienceUser->id => ['buyer' => false]]);
}
public function findExperiencePayment($paymentId)
{
return ExperiencePayment::find($paymentId);
}
public function findExperiencePlanPrice($planPriceId)
{
return ExperiencePlanPrice::find($planPriceId);
}
public function addCompanionsToExperiencePayment($payment, $data)
{
$groupedCompanions = [];
// Agrupar datos por companionN_
foreach ($data as $key => $value) {
if (preg_match('/^companion(\d+)_(.+)$/', $key, $matches)) {
$index = $matches[1]; // número de companion
$field = $matches[2]; // nombre del campo (first_name, last_name, etc.)
$groupedCompanions[$index][$field] = $value;
}
}
$payment = $this->findExperiencePayment($payment->id);
// Crear cada ExperienceUser y asociarlo al pago
foreach ($groupedCompanions as $companionData) {
$experienceUser = $this->createExperienceUser($companionData);
$payment->companions()->attach($experienceUser->id, ['buyer' => false]);
}
}
function handleRaffleForExperiencePayment($experiencePayment)
{
$experience = $experiencePayment->experience_plan_price->plan->experience;
// Verificar si la rifa está habilitada
if (!$experience->enable_raffle) {
$experiencePayment->raffle = null;
return;
}
$count = max((int) $experiencePayment->amount, 1);
// Obtener los códigos existentes para esta experiencia (como enteros)
$existingRaffles = DB::table('experience_payments')
->join('experience_plan_prices', 'experience_payments.experience_plan_price_id', '=', 'experience_plan_prices.id')
->join('experience_plans', 'experience_plan_prices.experience_plan_id', '=', 'experience_plans.id')
->where('experience_plans.experience_id', $experience->id)
->pluck('experience_payments.raffle')
->filter()
->flatMap(fn($r) => explode(',', $r))
->map(fn($r) => (int) trim($r))
->filter()
->unique()
->sort()
->values();
$nextNumber = $existingRaffles->last() ? $existingRaffles->last() + 1 : 1;
// Generar los nuevos códigos con formato 0001, 0002, etc.
$newRaffles = collect(range($nextNumber, $nextNumber + $count - 1))
->map(fn($num) => str_pad($num, 4, '0', STR_PAD_LEFT))
->toArray();
$experiencePayment->raffle = implode(',', $newRaffles);
}
public function syncERP($experiencePayment, $action = null)
{
try {
$erps = Erp::select('id', 'class')->where('active', 1)->get();
$erpSync = ErpParameter::where('key', 'experience_sync')->first();
if (!$erpSync || $erpSync->value == 'false') {
$this->saveERPLog($experiencePayment->id, 'Sinc ERP deshabilitada', 'syncERP');
return;
}
if (!$this->validateSyncWithERPPayment($experiencePayment)) {
return;
}
$erpService = new ErpService;
foreach ($erps as $index => $erp) {
$classPath = $erp->class;
$ERPRepository = new $classPath($erp->id);
$erpBaseController = new ERPBaseController;
$erpReference = $experiencePayment->erp_reference ?? 'E' . $experiencePayment->id;
if (
$experiencePayment->total < $erpBaseController->minPriceToSync($ERPRepository->erpId) ||
!$erpService->canSync($erpReference, $ERPRepository, 'experience_sync')
) {
return;
}
$previousBill = $ERPRepository->hasPreviousBill($erpReference);
if ($previousBill['r']) {
$experiencePayment->erp_response = $previousBill['d'];
$experiencePayment->update();
$this->confirmSyncERP($experiencePayment);
if (isset($previousBill['saveLog']) && $previousBill['saveLog']) {
$this->saveERPLog($experiencePayment->id, json_encode($previousBill['error']), 'createOrder');
}
continue;
}
$experiencePayment->erp_reference = $ERPRepository->getNextReference($erpReference);
$experiencePayment->update();
$erpService->synchronizing($erpReference);
switch ($action) {
case 'createOrder':
$this->createOrderERP($experiencePayment, $ERPRepository);
break;
case 'createRC':
$this->createRCERP($experiencePayment, $ERPRepository);
break;
case 'createBill':
$this->createBillERP($experiencePayment, $ERPRepository);
break;
default:
$this->createThirdClientERP($experiencePayment, $ERPRepository);
break;
}
}
} catch (Exception $e) {
$error = [
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->saveERPLog($experiencePayment->id, json_encode($error), 'syncERP');
}
}
public function saveERPLog($data, $message, $action)
{
$transactionId = $data;
ErpLog::updateOrCreate(
[
'origin' => 'experience',
'transaction_id' => $transactionId,
'resolved' => false
],
[
'origin' => 'experience',
'action' => $action,
'origin_class' => get_class($this),
'data' => $data,
'transaction_id' => $transactionId,
'message' => $message,
]
);
}
private function validateSyncWithERPPayment($experiencePayment)
{
return !ExperiencePayment::whereKey($experiencePayment->id)->value('sync_with_erp');
}
public function confirmSyncERP($experiencePayment)
{
$experiencePayment->sync_with_erp = true;
$experiencePayment->update();
}
private function createThirdClientERP($experiencePayment, $ERPRepository)
{
$experienceUser = $experiencePayment->experience_user;
$experiencePayment->billing_address = $academyUser->address_id ?? $ERPRepository->getDefaultAddress($experienceUser->user_id);
$experiencePayment->update();
$experienceType = $experiencePayment->experience_plan_price->plan->experience->experience_type;
$isAcademyExperience = $experienceType == 'academy';
$addressObj = Address::with('city')->find($experiencePayment->billing_address);
$address = $addressObj->direction . ' | ' . $addressObj->district;
$shortAddress = $addressObj->direction;
$country = $addressObj->city ? $addressObj->city->state->country->name : null;
$province = $addressObj->city ? $addressObj->city->state->name : null;
$city = $addressObj->city ? $addressObj->city->name : null;
$juricalPerson = false;
$thirdDocument = $experiencePayment->billing_document;
$params = new \stdClass();
$third = new \stdClass();
$client = new \stdClass();
$third->document = $thirdDocument;
$third->firstName = $experiencePayment->billing_first_name;
$third->lastName = $experiencePayment->billing_last_name;
$third->contact = $experiencePayment->billing_last_name . ' ' . $experiencePayment->billing_first_name;
$third->phone = $experiencePayment->billing_phone;
$third->email = $experiencePayment->billing_email;
$third->documentType = $ERPRepository->mapDocumentType($juricalPerson ? 'nit' : $experiencePayment->billing_document_type->alias);
$third->birthDate = Carbon::parse($experiencePayment->created_at)->format('Ymd');
$third->typeThird = 'NATURAL_PERSON';
$third->gender = '0';
$third->socialReason = $third->contact;
$third->establishmentName = $third->contact;
$third->ciiu = null;
if ($isAcademyExperience) {
$client->document = $experienceUser->document;
$client->contact = $experienceUser->last_name . ' ' . $experienceUser->first_name;
$client->phone = $experienceUser->phone;
$client->email = $experienceUser->email;
} else {
$client = $third;
}
$params->third = $third;
$params->client = $client;
$params->address = str_replace('#', 'N', $address);
$params->shortAddress = str_replace('#', 'N', $shortAddress);
$params->country = $country;
$params->province = $province;
$params->city = $city;
$params->sucursal = $isAcademyExperience ? $this->getSucursalForERP($experienceUser) : '001';
$params->price_list = '001';
$params->typeClient = $isAcademyExperience ? 'CACD' : 'CHED';
$params->postal = '';
$params->hasTaxes = '1';
$params->createThirdPos = true;
$params->idCriteria = $isAcademyExperience ? '101' : '105';
$params->criteria = $isAcademyExperience ? '1012' : '1054';
$params->currency = 'COP';
$params->latitude = $addressObj ? $addressObj->lat : null;
$params->longitude = $addressObj ? $addressObj->long : null;
$response = $ERPRepository->createThirdClient($params);
if (!$response['r']) {
$this->saveERPLog($experiencePayment->id, json_encode($response['d']), 'createThirdClient');
return;
}
$this->createOrderERP($experiencePayment, $ERPRepository);
}
private function createOrderERP($experiencePayment, $ERPRepository)
{
$hasPreviousOrder = $ERPRepository->hasPreviousOrder(
$experiencePayment->erp_reference,
$experiencePayment->payment_transaction->reference,
$experiencePayment->payment_transaction->gateway_transaction_id
);
if ($hasPreviousOrder) {
$this->createRCERP($experiencePayment, $ERPRepository);
return;
}
$experience = $experiencePayment->experience_plan_price->plan->experience;
$serviceItemCode = $experience->experience_erp->service_item_code;
$operationCenter = $experience->experience_erp->operation_center;
if (!$serviceItemCode) {
$this->saveERPLog(
$experiencePayment->id,
'La experiencia (' . $experience->name . ') no tiene código de servicio configurado',
'createOrder'
);
return;
}
if (!$operationCenter) {
$this->saveERPLog(
$experiencePayment->id,
'La experiencia (' . $experience->name . ') no tiene centro de operacion configurado',
'createOrder'
);
return;
}
$addressObj = Address::with('city')->find($experiencePayment->billing_address);
$experienceUser = $experiencePayment->experience_user;
$experienceType = $experiencePayment->experience_plan_price->plan->experience->experience_type;
$isAcademyExperience = $experienceType == 'academy';
$third = new \stdClass();
$third->firstName = $experiencePayment->billing_first_name;
$third->lastName = $experiencePayment->billing_last_name;
$third->contact = $experiencePayment->billing_last_name . ' ' . $experiencePayment->billing_first_name;
$third->phone = $experiencePayment->billing_phone;
$third->email = $experiencePayment->billing_email;
$third->documentType = $ERPRepository->mapDocumentTypeOrder($experiencePayment->billing_document_type->alias);
$third->document = $experiencePayment->billing_document;
$orderParams = new \stdClass();
$orderParams->operationCenter = $operationCenter;
$orderParams->document = $third->document;
$orderParams->sucursal = $isAcademyExperience ? $this->getSucursalForERP($experienceUser) : '001';
$orderParams->costCenter = $isAcademyExperience ? '3002' : '1201';
$orderParams->date = Carbon::now()->format('Ymd');
$orderParams->customerType = $isAcademyExperience ? 'CACD' : 'CHED';
$orderParams->note = $experiencePayment->payment_transaction->reference . ' - ' . $experience->name;
$orderParams->reference = $experiencePayment->erp_reference;
$orderParams->numeroPedido = $experiencePayment->id;
$orderParams->third = $third;
$orderParams->transaction = $experiencePayment->payment_transaction;
$orderParams->address = $addressObj;
$itemParams = new \stdClass();
$itemParams->operation_center = $operationCenter;
$itemParams->serviceCode = $serviceItemCode;
$itemParams->warehouse = $isAcademyExperience ? 'AC017' : 'BDHED';
$itemParams->cost_center = $isAcademyExperience ? '3002' : '1201';
$itemParams->date = $orderParams->date;
$itemParams->price_list = '022';
$itemParams->price = $experiencePayment->total; //Pago total incluido descuentos
$itemParams->fullPrice = $experiencePayment->price + $experiencePayment->service_charge;
$itemParams->discount = 0;
$itemParams->reason = $isAcademyExperience ? '06' : '60';
$itemParams->un = $isAcademyExperience ? '100' : '104';
$discounts = [];
if ($experiencePayment->discount > 0) {
$controller = new Controller();
$discount = array(
'f430_id_co' => $operationCenter,
'f430_consec_docto' => '1',
'f431_nro_registro' => '1',
'f432_tasa' => $experiencePayment->discount,
'f432_vlr_uni' => 0,
);
array_push($discounts, $discount);
$itemParams->discount += $controller->formatNumberForCurrency(($itemParams->price * $experiencePayment->discount) / 100);
}
if ($experiencePayment->price_discount > 0) {
$discount = array(
'f430_id_co' => $operationCenter,
'f430_consec_docto' => '1',
'f431_nro_registro' => '1',
'f432_tasa' => 0,
'f432_vlr_uni' => $experiencePayment->price_discount,
);
array_push($discounts, $discount);
$itemParams->discount += $experiencePayment->price_discount;
}
$orderParams->discounts = $discounts;
$orderParams->items = [$ERPRepository->generateOrderItem($itemParams)];
$response = $ERPRepository->createOrder($orderParams);
if (!$response['r']) {
$this->saveERPLog($experiencePayment->id, json_encode($response['d']), 'createOrder');
return;
}
$experiencePayment->erp_response = is_string($response['d']) ? $response['d'] : null;
$experiencePayment->update();
if (isset($response['saveLog']) && $response['saveLog']) {
$this->saveERPLog(
$experiencePayment->id,
json_encode($response['error']),
'createOrder'
);
}
$this->createRCERP($experiencePayment, $ERPRepository);
}
private function createRCERP($experiencePayment, $ERPRepository)
{
$erpBaseController = new ERPBaseController;
if (!$erpBaseController->syncRcEnabled($ERPRepository->erpId)) {
$this->createBillERP($experiencePayment, $ERPRepository);
return;
}
$response = $ERPRepository->getOrder($experiencePayment->erp_reference);
if (!$response['r']) {
$this->saveERPLog($experiencePayment->id, json_encode($response['d']), 'createRC');
return;
}
foreach ($response['d']->detalle->Table as $index => $siesaOrder) {
$response = $ERPRepository->getRC($experiencePayment->billing_document, $siesaOrder->CentroDeOperacion);
if ($response['r']) {
$previousRC = array_filter($response['d']->detalle->Table, function ($item) use ($experiencePayment) {
$fullPrice = $experiencePayment->price + $experiencePayment->service_charge;
return str_contains($item->Notas, $experiencePayment->payment_transaction->reference) && $item->TotalRecibo == $fullPrice;
}, ARRAY_FILTER_USE_BOTH);
if (count($previousRC)) {
$this->createBillERP($experiencePayment, $ERPRepository);
continue;
}
}
$paymentTransaction = $experiencePayment->payment_transaction;
$experienceUser = $experiencePayment->experience_user;
$experienceType = $experiencePayment->experience_plan_price->plan->experience->experience_type;
$isAcademyExperience = $experienceType == 'academy';
$paymentMethod = PaymentMethod::select('code')->where('id', $paymentTransaction->payment_method_id)->first();
$parameters = new \stdClass();
$parameters->paymentDate = Carbon::parse($paymentTransaction->payment_date)->format('Ymd');
$parameters->idThird = $experiencePayment->billing_document;
$parameters->currency = 'COP';
$parameters->siesaOrder = $siesaOrder;
$parameters->fe = $isAcademyExperience ? '1105' : '1104';
$parameters->note = $paymentTransaction->reference;
$parameters->assistant = $isAcademyExperience ? '28050502' : '28050507';
$parameters->sucursal = $isAcademyExperience ? $this->getSucursalForERP($experienceUser) : '001';
$parameters->paymetMethod = $paymentMethod ? $paymentMethod->code : 'WOM';
$parameters->UN = $isAcademyExperience ? '100' : '104';
$parameters->price = $experiencePayment->total;
$response = $ERPRepository->createRC($parameters);
if (!$response['r']) {
$this->saveERPLog($experiencePayment->id, json_encode($response['d']), 'createRC');
return;
}
$this->createBillERP($experiencePayment, $ERPRepository);
}
}
private function createBillERP($experiencePayment, $ERPRepository)
{
$erpBaseController = new ERPBaseController;
if (!$erpBaseController->syncBillEnabled($ERPRepository->erpId)) {
$this->confirmSyncERP($experiencePayment);
return;
}
$response = $ERPRepository->getOrder($experiencePayment->erp_reference);
if (!$response['r']) {
$this->saveERPLog($experiencePayment->id, json_encode($response['d']), 'createBill');
return;
}
$paymentTransaction = $experiencePayment->payment_transaction;
foreach ($response['d']->detalle->Table as $index => $siesaOrder) {
$parameters = new \stdClass();
$parameters->siesaOrder = $siesaOrder;
$parameters->thirdDocument = $experiencePayment->billing_document;
$parameters->note = $paymentTransaction->reference;
$parameters->price = $experiencePayment->total;
$parameters->typeDocument = 'FE1';
$response = $ERPRepository->createBill($parameters);
if (!$response['r']) {
$this->saveERPLog($experiencePayment->id, json_encode($response['d']), 'createBill');
return;
}
}
$this->confirmSyncERP($experiencePayment);
}
public function retrySyncERP($data, $action)
{
$academyPurchase = ExperiencePayment::find($data);
$this->syncERP($academyPurchase, $action);
}
public function getSucursalForERP($experienceUser)
{
$sucursal = 2;
$users = AcademyUser::where('user_id', $experienceUser->user_id)->pluck('identification')->toArray();
$sucursal += array_search($experienceUser->document, $users);
if ($sucursal == 5)
$sucursal += 1;
return $this->util->completeLeadingZeros($sucursal, 3);
}
}