File: /var/www/vhost/disk-apps/qas.sports-crowd.com/app/Core/Membership/Application/MembershipService.php
<?php
declare(strict_types=1);
namespace App\Core\Membership\Application;
use App\Address;
use App\CorporateIdentity;
use App\Core\Academy\Application\MembershipPaymentService;
use App\Core\Membership\Domain\ValueObjects\MembershipOrderStatus;
use App\Core\Membership\Domain\ValueObjects\MembershipStatus;
use App\Core\Parameter\Application\ParameterService;
use App\Core\Payment\Application\PaymentTransactionService;
use App\Core\Servientrega\Application\ServientregaService;
use App\Core\User\Application\UserService;
use App\Erp;
use App\ErpLog;
use App\ErpParameter;
use App\ErpCustomItem;
use App\Events\Memberships\SubscriptionRevoked;
use App\Events\Memberships\SubscriptionConfirmed;
use App\Http\Controllers\ShopifyController;
use App\Models\Membership\Membership;
use App\Models\Membership\MembershipSubscriber;
use App\PaymentTransaction;
use Carbon\Carbon;
use Exception;
// TODO: think in MembershipSubscriptionService
class MembershipService
{
private $ERPPrefix = 'M';
private $membershipPaymentService;
private $userService;
private $servientregaService;
private $paymentTransactionService;
private $parameterService;
public function __construct()
{
$this->membershipPaymentService = new MembershipPaymentService();
$this->userService = new UserService();
$this->servientregaService = new ServientregaService();
$this->paymentTransactionService = new PaymentTransactionService();
$this->parameterService = new ParameterService();
}
public function find(int $membershipSubscriberId)
{
return MembershipSubscriber::findOrFail($membershipSubscriberId);
}
public function createMembershipSubscription($membership, $user, $startDate = null, $deliveryPrice = null)
{
if (!$deliveryPrice && $membership->deliverable) {
$lastUsedAddress = $this->userService->getLastUsedAddress($user);
$deliveryPrice = $this->servientregaService->getDeliveryPrice($lastUsedAddress)->price;
}
$membershipSubscriber = new MembershipSubscriber();
$membershipSubscriber->membership_id = $membership->id;
$membershipSubscriber->user_id = $user->id;
$membershipSubscriber->status = MembershipStatus::PENDING;
$membershipSubscriber->price = $membership->price;
$membershipSubscriber->delivery = $deliveryPrice ?? 0;
$membershipSubscriber->start_date = $startDate ?? now();
$membershipSubscriber->end_date = $this->calculateEndDate(
$membershipSubscriber->start_date,
$membershipSubscriber->membership->billing_interval
);
$membershipSubscriber->auto_renew = false;
$membershipSubscriber->is_renewal = false;
$membershipSubscriber->save();
return $membershipSubscriber;
}
public function createFromMembershipSubscription($membershipSubscription)
{
$newMembershipSubscription = new MembershipSubscriber();
$newMembershipSubscription->membership_id = $membershipSubscription->membership_id;
$newMembershipSubscription->user_id = $membershipSubscription->user_id;
$newMembershipSubscription->status = MembershipStatus::PENDING;
$newMembershipSubscription->price = $membershipSubscription->membership->price;
$newMembershipSubscription->start_date = now();
$newMembershipSubscription->end_date = $this->calculateEndDate(
now(),
$membershipSubscription->membership->billing_interval
);
$newMembershipSubscription->is_renewal = true;
$newMembershipSubscription->save();
$paymentTransaction = $this->membershipPaymentService->getPaymentTransaction(
$newMembershipSubscription
);
$this->paymentTransactionService->setPaymentGatewayId(
$paymentTransaction,
$membershipSubscription->payment_transaction->gateway_payments_id,
);
return $newMembershipSubscription;
}
public function confirmMembershipOrder($membershipOrder)
{
if ($membershipOrder->status != MembershipOrderStatus::PENDING) {
throw new Exception("You can not confirm this membership", 1);
}
foreach ($membershipOrder->items as $item) {
$membershipSubscription = $this->createMembershipSubscription($item->membership, $item->subscriber);
$this->confirmMembership($membershipSubscription);
}
$membershipOrder->status = MembershipOrderStatus::CONFIRMED;
$membershipOrder->save();
}
public function confirmMembership($membershipSubscription)
{
if ($membershipSubscription->status != MembershipStatus::PENDING) {
throw new Exception("You can not confirm this membership", 1);
}
$previousMembership = $this->userService->hasMembership(
$membershipSubscription->user,
$membershipSubscription
);
if ($previousMembership) {
$startDate = max($previousMembership->end_date, $membershipSubscription->start_date);
$membershipSubscription->end_date = $this->calculateEndDate(
$startDate,
$membershipSubscription->membership->billing_interval
);
$membershipSubscription->is_renewal = true;
$previousMembership->status = MembershipStatus::EXPIRED;
$previousMembership->auto_renew = false;
$previousMembership->save();
}
$membershipSubscription->status = MembershipStatus::CONFIRMED;
$membershipSubscription->save();
SubscriptionConfirmed::dispatch($membershipSubscription);
}
public function sendConfimationEmail($membershipSubscription)
{
$tags = $membershipSubscription->membership->tags;
$appStoreUrl = $this->parameterService->appStoreUrl();
$playStoreUrl = $this->parameterService->playStoreUrl();
$corporateIdentity = CorporateIdentity::first();
$shopifyController = new ShopifyController();
$shopifyController->sendConfirmationEmail(
$membershipSubscription->user->toArray(),
$tags->first()->name,
[
'appStoreUrl' => $appStoreUrl,
'playStoreUrl' => $playStoreUrl,
'corporateIdentity' => $corporateIdentity
]
);
}
public function revokeMembership($membershipSubscription)
{
$membershipSubscription->update([
'status' => MembershipStatus::EXPIRED,
'auto_renew' => false
]);
SubscriptionRevoked::dispatch($membershipSubscription);
$this->syncERP($membershipSubscription);
}
public function setAsNoRenewable($membershipSubscription)
{
$membershipSubscription->auto_renew = false;
$membershipSubscription->save();
}
// TODO: Candidate to be a MembershipQueryService
public function queryDashboard($request)
{
$page = ($request->start / $request->length) + 1;
$request->merge(['page' => $page]);
$orderBy = $request->columns[$request->order[0]['column']]['name'];
$orderDirection = $request->order[0]['dir'];
if ($orderBy == 'users.name') {
$orderBy = 'users.first_name';
}
$response = MembershipSubscriber::select([
'membership_subscribers.*',
'memberships.*',
'users.*',
'payment_transactions.*',
'membership_subscribers.created_at',
])
->leftJoin('memberships', function ($join) {
$join->on('memberships.id', '=', 'membership_subscribers.membership_id');
})
->leftJoin('users', function ($join) {
$join->on('users.id', '=', 'membership_subscribers.user_id');
})
->leftJoin('payment_transactions', function ($join) {
$join->on('payment_transactions.id', '=', 'membership_subscribers.payment_transaction_id');
})
->when($request->search['value'], function ($query) use ($request) {
$query->where(function ($query) use ($request) {
$query->orWhere('state', 'like', '%' . $request->search['value'] . '%');
$query->orWhere('name', 'like', '%' . $request->search['value'] . '%');
$query->orWhere('first_name', 'like', '%' . $request->search['value'] . '%');
$query->orWhere('last_name', 'like', '%' . $request->search['value'] . '%');
$query->orWhere('document', 'like', '%' . $request->search['value'] . '%');
$query->orWhere('reference', 'like', '%' . $request->search['value'] . '%');
$query->orWhere('gateway_transaction_id', 'like', '%' . $request->search['value'] . '%');
});
})
->when(!is_null($request->states), function ($query) use ($request) {
$query->whereIn('membership_subscribers.status', $request->states);
})
->when(!is_null($request->is_renewal), function ($query) use ($request) {
$query->whereIn('membership_subscribers.is_renewal', $request->is_renewal);
})
->when(!is_null($request->start_date), function ($query) use ($request) {
$query->where('membership_subscribers.start_date', '>=', $request->start_date);
})
->when(!is_null($request->end_date), function ($query) use ($request) {
$query->where('membership_subscribers.end_date', '<=', $request->end_date);
})
->orderBy($orderBy, $orderDirection)
->paginate();
return response()->json($response);
}
public function queryDownloadReport($request)
{
$response = MembershipSubscriber::select([
'membership_subscribers.id',
'users.first_name',
'users.last_name',
'users.email',
'users.document',
'membership_subscribers.status as membership_status',
'payment_transactions.state as payment_status',
'membership_subscribers.start_date',
'membership_subscribers.end_date',
'membership_subscribers.is_renewal',
'membership_subscribers.auto_renew',
'membership_subscribers.tracking_code',
])
->leftJoin('users', function ($join) {
$join->on('users.id', '=', 'membership_subscribers.user_id');
})
->leftJoin('payment_transactions', function ($join) {
$join->on('payment_transactions.id', '=', 'membership_subscribers.payment_transaction_id');
})
->when(!is_null($request->states), function ($query) use ($request) {
$query->whereIn('membership_subscribers.status', $request->states);
})
->when(!is_null($request->is_renewal), function ($query) use ($request) {
$query->whereIn('membership_subscribers.is_renewal', $request->is_renewal);
})
->when(!is_null($request->start_date), function ($query) use ($request) {
$query->where('membership_subscribers.start_date', '>=', $request->start_date);
})
->when(!is_null($request->end_date), function ($query) use ($request) {
$query->where('membership_subscribers.end_date', '<=', $request->end_date);
})
->get();
return $response;
}
public function createConfirmedMembershipSubscription($params)
{
$previousMembershipSubscription = MembershipSubscriber::select('membership_subscribers.id')
->join('payment_transactions', 'payment_transactions.id', '=', 'membership_subscribers.payment_transaction_id')
->where('payment_transactions.reference', $params->transactionReference)
->first();
if ($previousMembershipSubscription) {
return;
}
$membershipSubscription = $this->createMembershipSubscription(
$params->membership,
$params->user,
$params->startDate,
$params->deliveryPrice
);
$paymentTransaction = PaymentTransaction::create([
'state' => 'CONFIRMED',
'origin_class' => get_class($this),
'gateway_transaction_id' => $params->transactionReference
]);
$membershipSubscription->payment_transaction_id = $paymentTransaction->id;
$membershipSubscription->sync_with_erp = true;
$membershipSubscription->update();
$this->confirmMembership($membershipSubscription);
}
public function syncERP($membershipSubscription, $action = null)
{
try {
$erpSync = ErpParameter::where('key', 'shop_sync')->first();
if (!$erpSync || $erpSync->value == 'false') {
return;
}
$erps = Erp::select('id', 'class')->where('active', true)->get();
foreach ($erps as $index => $erp) {
$classPath = $erp->class;
$ERPRepository = new $classPath($erp->id);
if ($membershipSubscription->status != MembershipStatus::EXPIRED) {
if (!$this->validateSyncWithERPPayment($membershipSubscription)) {
return;
}
$previousBill = $ERPRepository->getBill($this->ERPPrefix . $membershipSubscription->id);
if ($previousBill['r'] && count($previousBill['d']->detalle->Table)) {
return;
}
}
switch ($action) {
case 'createOrder':
$this->createOrderERP(
$membershipSubscription,
$ERPRepository
);
break;
case 'createRC':
$this->createRCERP(
$membershipSubscription,
$ERPRepository
);
break;
case 'createBill':
$this->createBillERP(
$membershipSubscription,
$ERPRepository
);
break;
default:
$this->createThirdClientERP(
$membershipSubscription,
$ERPRepository
);
break;
}
}
} catch (Exception $e) {
$error = [
'message' => $e->getMessage(),
'getFile' => $e->getFile(),
'getLine' => $e->getLine(),
];
$this->saveERPLog($membershipSubscription->id, json_encode($error), 'syncERP');
}
}
private function createThirdClientERP($membershipSubscription, $ERPRepository)
{
$addressObj = Address::with('city')
->where('user_id', $membershipSubscription->user->id)
->where('last_used', true)
->first();
if (!$addressObj) {
$address = 'Calle 90 #19-41';
$shortAddress = $address;
$country = 'Colombia';
$province = 'Bogotá';
$city = 'Bogotá, D.C.';
} else {
$address = $addressObj->direction . ' | ' . $addressObj->district;
$shortAddress = $addressObj->direction;
if ($addressObj->city) {
$country = $addressObj->city->state->country->name;
$province = $addressObj->city->state->name;
$city = $addressObj->city->name;
}
}
$user = $membershipSubscription->user;
$siesaThirdClient = $ERPRepository->thirdClientValidate($user->document);
$expired = $membershipSubscription->status == MembershipStatus::EXPIRED;
$params = new \stdClass();
$third = new \stdClass();
$client = new \stdClass();
$third->document = $user->document;
$third->firstName = $user->first_name;
$third->lastName = $user->last_name;
$third->contact = $user->last_name . ' ' . $user->first_name;
$third->phone = $user->phone;
$third->email = $user->email;
$third->isNew = count($siesaThirdClient) == 0;
$third->documentType = $ERPRepository->mapDocumentType($user->documentType->alias);
$third->birthDate = Carbon::parse($user->userInfo->dob)->format('Ymd');
$third->typeThird = 'NATURAL_PERSON';
$third->gender = $ERPRepository->mapGender($user->userInfo->sex);
$third->socialReason = $third->contact;
$third->establishmentName = $third->contact;
$third->ciiu = null;
$client->document = $user->document;
$client->contact = $user->last_name . ' ' . $user->first_name;
$client->phone = $user->phone;
$client->email = $user->email;
$params->third = $third;
$params->client = $client;
$params->address = $address;
$params->shortAddress = $shortAddress;
$params->country = $country ?? null;
$params->province = $province ?? null;
$params->city = $city ?? null;
$params->sucursal = '001';
$params->price_list = '001';
$params->typeClient = $expired ? 'CTDS' : 'CHED';
$params->hasTaxes = '1';
$params->createThirdPos = true;
$params->idCriteria = $expired ? '103' : '105';
$params->criteria = $expired ? '1032' : '1051';
$params->currency = 'COP';
$params->latitude = $addressObj ? $addressObj->lat : null;
$params->longitude = $addressObj ? $addressObj->long : null;
$response = $ERPRepository->createThirdClient($params);
if (!$response['r']) {
$this->saveERPLog($membershipSubscription->id, json_encode($response['d']), 'createThirdClient');
return;
}
if (!$expired) {
$this->createOrderERP($membershipSubscription, $ERPRepository);
}
}
private function createOrderERP($membershipSubscription, $ERPRepository)
{
$paymentTransaction = PaymentTransaction::where('id', $membershipSubscription->payment_transaction_id)->first();
$response = $ERPRepository->getOrder($this->ERPPrefix . $membershipSubscription->id);
if ($response['r']) {
$previousOrders = array_filter($response['d']->detalle->Table, function ($siesaOrder) use ($paymentTransaction) {
$match = str_contains($siesaOrder->Notas, $paymentTransaction->gateway_transaction_id) ||
str_contains($siesaOrder->Notas, $paymentTransaction->reference);
return $match;
}, ARRAY_FILTER_USE_BOTH);
if (count($previousOrders)) {
$this->createRCERP($membershipSubscription, $ERPRepository);
return;
}
}
$orderParams = new \stdClass();
$orderParams->operationCenter = '601';
$orderParams->document = $membershipSubscription->user->document;
$orderParams->sucursal = '001';
$orderParams->costCenter = '4001';
$orderParams->date = Carbon::parse($paymentTransaction->payment_date)->format('Ymd');
$orderParams->customerType = 'CHED';
$orderParams->note = $membershipSubscription->membership->name . '-' . $paymentTransaction->reference;
$orderParams->reference = $this->ERPPrefix . $membershipSubscription->id;
$orderParams->items = [
array(
'f431_id_co' => $orderParams->operationCenter,
'f431_consec_docto' => '1',
'f431_nro_registro' => '1',
'f431_referencia_item' => '', //$productVariant['sku'],
'f431_codigo_barras' => $membershipSubscription->is_renewal ? '3103144520224' : '1104424520023',
'f431_id_bodega' => 'BDHED',
'f431_id_motivo' => '60',
'f431_id_co_movto' => '601',
'f431_id_un_movto' => '104',
'f431_id_ccosto_movto' => '1201',
'f431_fecha_entrega' => $orderParams->date,
'f431_id_lista_precio' => '001',
'f431_id_unidad_medida' => 'UND',
'f431_cant_pedida_base' => 1,
'f431_precio_unitario' => $membershipSubscription->membership->price,
'f431_ind_precio' => '1',
'f431_ind_obsequio' => '0',
'f431_ind_impto_asumido' => '0'
)
];
if ($membershipSubscription->delivery > 0) {
$delivery = array(
'f431_id_co' => $orderParams->operationCenter,
'f431_consec_docto' => '1',
'f431_nro_registro' => '1',
'f431_referencia_item' => '110440022',
'f431_codigo_barras' => '',
'f431_id_bodega' => 'BDHED',
'f431_id_motivo' => '05',
'f431_id_co_movto' => $orderParams->operationCenter,
'f431_id_un_movto' => '102',
'f431_id_ccosto_movto' => '4001',
'f431_fecha_entrega' => $orderParams->date,
'f431_id_lista_precio' => '005',
'f431_id_unidad_medida' => 'UND',
'f431_cant_pedida_base' => '1',
'f431_precio_unitario' => (int) $membershipSubscription->delivery,
'f431_ind_precio' => '2',
'f431_ind_obsequio' => '0',
'f431_ind_impto_asumido' => '0'
);
array_push($orderParams->items, $delivery);
}
$erpCustomItems = ErpCustomItem::where('active', true)
->where('amount', '>', 0)
->where('modules', 'like', '%membership%')
->where(function ($query) {
$query->orWhereNull('labels');
$query->orWhere('labels', '');
})
->get();
foreach ($erpCustomItems as $erpCustomItem) {
$customItem = array(
'f431_id_co' => $orderParams->operationCenter,
'f431_consec_docto' => '1',
'f431_nro_registro' => count($orderParams->items) + 1,
'f431_referencia_item' => $erpCustomItem->reference ?? '',
'f431_codigo_barras' => $erpCustomItem->barcode ?? '',
'f431_id_bodega' => 'BDHED',
'f431_id_motivo' => $erpCustomItem->is_gift ? '08' : '60',
'f431_id_co_movto' => '601',
'f431_id_un_movto' => '104',
'f431_id_ccosto_movto' => '1201',
'f431_fecha_entrega' => $orderParams->date,
'f431_id_lista_precio' => $erpCustomItem->is_gift ? '015' : '001',
'f431_id_unidad_medida' => 'UND',
'f431_cant_pedida_base' => $erpCustomItem->amount,
'f431_precio_unitario' => 1,
'f431_ind_precio' => '1',
'f431_ind_obsequio' => $erpCustomItem->is_gift ? '1' : '0',
'f431_ind_impto_asumido' => $erpCustomItem->is_gift ? '1' : '0'
);
array_push($orderParams->items, $customItem);
}
$discounts = [];
$orderParams->discounts = $discounts;
$response = $ERPRepository->createOrder($orderParams);
if (!$response['r']) {
$this->saveERPLog($membershipSubscription->id, json_encode($response['d']), 'createOrder');
return;
}
$this->createRCERP($membershipSubscription, $ERPRepository);
}
private function createRCERP($membershipSubscription, $ERPRepository)
{
$paymentTransaction = PaymentTransaction::where('id', $membershipSubscription->payment_transaction_id)->first();
$response = $ERPRepository->getOrder($this->ERPPrefix . $membershipSubscription->id);
if (!$response['r']) {
$this->saveERPLog($membershipSubscription->id, json_encode($response['d']), 'createRC');
return;
}
$user = $membershipSubscription->user;
$price = $membershipSubscription->membership->price + $membershipSubscription->delivery;
foreach ($response['d']->detalle->Table as $index => $siesaOrder) {
$response = $ERPRepository->getRC($user->document, $siesaOrder->CentroDeOperacion);
if ($response['r']) {
$previousRC = array_filter($response['d']->detalle->Table, function ($item) use ($paymentTransaction, $price) {
return str_contains($item->Notas, $paymentTransaction->reference) && $item->TotalRecibo == $price;
}, ARRAY_FILTER_USE_BOTH);
if (count($previousRC)) {
$this->createBillERP($membershipSubscription, $ERPRepository);
continue;
}
}
$parameters = new \stdClass();
$parameters->paymentDate = Carbon::parse($paymentTransaction->payment_date)->format('Ymd');
$parameters->idThird = $user->document;
$parameters->currency = 'COP';
$parameters->siesaOrder = $siesaOrder;
$parameters->fe = '1104';
$parameters->note = $membershipSubscription->membership->name . '-' . $paymentTransaction->reference;
$parameters->assistant = '28050507';
$parameters->sucursal = '001';
$parameters->paymetMethod = 'WOM';
$parameters->UN = '104';
$parameters->price = $price;
$response = $ERPRepository->createRC($parameters);
if (!$response['r']) {
$this->saveERPLog($membershipSubscription->id, json_encode($response['d']), 'createRC');
return;
}
$this->createBillERP($membershipSubscription, $ERPRepository);
}
}
private function createBillERP($membershipSubscription, $ERPRepository)
{
$response = $ERPRepository->getOrder($this->ERPPrefix . $membershipSubscription->id);
if (!$response['r']) {
$this->saveERPLog($membershipSubscription->id, json_encode($response['d']), 'createBill');
return;
}
$paymentTransaction = PaymentTransaction::where('id', $membershipSubscription->payment_transaction_id)->first();
foreach ($response['d']->detalle->Table as $index => $siesaOrder) {
$parameters = new \stdClass();
$parameters->siesaOrder = $siesaOrder;
$parameters->thirdDocument = $membershipSubscription->user->document;
$parameters->note = $membershipSubscription->membership->name . '-' . $paymentTransaction->reference;
$parameters->price = $membershipSubscription->membership->price + $membershipSubscription->delivery;
$parameters->typeDocument = 'FE1';
$response = $ERPRepository->createBill($parameters);
if (!$response['r']) {
$this->saveERPLog($membershipSubscription->id, json_encode($response['d']), 'createBill');
return;
}
}
$this->confirmSyncERP($membershipSubscription);
}
private function validateSyncWithERPPayment($membershipSubscription)
{
return !MembershipSubscriber::select('sync_with_erp')
->where('id', $membershipSubscription->id)
->first()
->sync_with_erp;
}
public function saveERPLog($data, $message, $action)
{
$transactionId = $data;
ErpLog::updateOrCreate(
[
'origin' => 'membership',
'transaction_id' => $transactionId,
'resolved' => false
],
[
'origin' => 'membership',
'action' => $action,
'origin_class' => get_class($this),
'data' => $data,
'transaction_id' => $transactionId,
'message' => $message,
]
);
}
public function confirmSyncERP($membershipSubscription)
{
$membershipSubscription->sync_with_erp = true;
$membershipSubscription->update();
}
public function retrySyncERP($data, $action)
{
$membershipSubscription = MembershipSubscriber::find($data);
$this->syncERP($membershipSubscription, $action);
}
private function calculateEndDate($date, $interval)
{
switch ($interval) {
case 'yearly':
return $date->addYear();
break;
case 'monthly':
return $date->addMonth();
break;
case 'weekly':
return $date->addWeek();
break;
case 'daily':
return $date->addDay();
break;
default:
return $date->addDay();
break;
}
}
public function existBySegementation($segmentation)
{
$membership = Membership::select('id', 'active')
->join('membership_tags', 'membership_tags.membership_id', '=', 'memberships.id')
->whereIn('membership_tags.tag_id', $segmentation)
->first();
return $membership ? true : false;
}
public function memershipActiveBySegmentation($segmentation)
{
$membership = Membership::select('id')
->join('membership_tags', 'membership_tags.membership_id', '=', 'memberships.id')
->whereIn('membership_tags.tag_id', $segmentation)
->where('memberships.active', true)
->first();
return $membership ? true : false;
}
}