Merge branch 'update' into 'develop'
chore: 🔖 release v4.1.0 Closes #41 See merge request ldc-web/projets/crm!33
This commit is contained in:
commit
af090bbef7
39 changed files with 708 additions and 320 deletions
|
|
@ -11,7 +11,6 @@ use App\Http\Requests\Api\V2\SingleOptinRequest;
|
|||
use App\Http\Requests\Api\V2\SingleOptinRemoveWithHashcodeRequest;
|
||||
use App\Models\CustomerInfo;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use App\Models\SingleOptin;
|
||||
|
||||
class OptinController extends Controller
|
||||
|
|
@ -26,24 +25,31 @@ class OptinController extends Controller
|
|||
* if this email already has a full account.
|
||||
* @throws \App\Exceptions\Api\V2\CrmException If the single optin could not be added or removed.
|
||||
*/
|
||||
public function optin(SingleOptinRequest $request, Service $service): JsonResponse
|
||||
public function optin(SingleOptinRequest $request, Service $service): \Illuminate\Http\JsonResponse
|
||||
{
|
||||
if (CustomerInfo::getFromEmail($service->brand, $request->email)->exists()) {
|
||||
$validatedData = $request->validated();
|
||||
if (CustomerInfo::getFromEmail($service->brand, $validatedData['email'])->exists()) {
|
||||
throw new ApiErrorException(ApiError::singleOptinCustomerExists);
|
||||
}
|
||||
|
||||
/** @var \App\Models\SingleOptin */
|
||||
$singleOptin = SingleOptin::query()->where('email', $request->email)
|
||||
$singleOptin = SingleOptin::query()
|
||||
->where('email', $validatedData['email'])
|
||||
->whereHas('brand', function ($q) use ($service) {
|
||||
$q->where('id', $service->brand->id);
|
||||
})->firstOrNew();
|
||||
})
|
||||
->firstOrNew();
|
||||
|
||||
if (!$this->changeOptin($service, $request, \boolval($request->opt_in), $singleOptin)) {
|
||||
if (!$this->changeOptin($service, $request, \boolval($validatedData['opt_in']), $singleOptin)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new CrmException('Api failed to update single optin.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if (isset($validatedData['answer_id'])) {
|
||||
$singleOptin->answers()->attach($validatedData['answer_id']);
|
||||
}
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
|
||||
|
|
@ -55,13 +61,17 @@ class OptinController extends Controller
|
|||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \App\Exceptions\Api\V2\CrmException If the single optin could not be removed.
|
||||
*/
|
||||
public function optoutWithHashcode(SingleOptinRemoveWithHashcodeRequest $request, Service $service): JsonResponse
|
||||
{
|
||||
public function optoutWithHashcode(
|
||||
SingleOptinRemoveWithHashcodeRequest $request,
|
||||
Service $service
|
||||
): \Illuminate\Http\JsonResponse {
|
||||
/** @var \App\Models\SingleOptin */
|
||||
$singleOptin = SingleOptin::query()->where('hashcode', $request->hashcode)
|
||||
$singleOptin = SingleOptin::query()
|
||||
->where('hashcode', $request->hashcode)
|
||||
->whereHas('brand', function ($q) use ($service) {
|
||||
$q->whereId($service->brand->id);
|
||||
})->firstOrFail();
|
||||
})
|
||||
->firstOrFail();
|
||||
|
||||
if ($singleOptin and !$this->changeOptin($service, $request, \boolval($request->opt_in), $singleOptin)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@ use App\Http\Requests\Api\V2\GetQuestionStatsRequest;
|
|||
use App\Http\Resources\Api\V2\BrandStatsResource;
|
||||
use App\Http\Resources\Api\V2\QuestionStatsResource;
|
||||
use App\Models\BrandStat;
|
||||
use App\Models\CustomerAnswer;
|
||||
use App\Models\ModelAnswer;
|
||||
use App\Models\Question;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
|
@ -24,8 +23,10 @@ class StatsController extends Controller
|
|||
* @param \App\Models\Service $service
|
||||
* @return \Illuminate\Http\Resources\Json\ResourceCollection
|
||||
*/
|
||||
public function brandStats(GetBrandStatsRequest $request, Service $service): ResourceCollection
|
||||
{
|
||||
public function brandStats(
|
||||
GetBrandStatsRequest $request,
|
||||
Service $service
|
||||
): \Illuminate\Http\Resources\Json\ResourceCollection {
|
||||
$stats = BrandStat::query()->whereDate('created_at', '>=', $request->start)
|
||||
->whereDate('created_at', '<=', $request->end)
|
||||
->whereBrandId($service->brand->id)->get();
|
||||
|
|
@ -39,8 +40,10 @@ class StatsController extends Controller
|
|||
* @param \App\Models\Question $question
|
||||
* @return \App\Http\Resources\Api\V2\QuestionStatsResource
|
||||
*/
|
||||
public function questionStats(GetQuestionStatsRequest $request, Question $question): QuestionStatsResource
|
||||
{
|
||||
public function questionStats(
|
||||
GetQuestionStatsRequest $request,
|
||||
Question $question
|
||||
): \App\Http\Resources\Api\V2\QuestionStatsResource {
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('questionBelongsToService', $question);
|
||||
|
||||
|
|
@ -62,18 +65,22 @@ class StatsController extends Controller
|
|||
* @param \Illuminate\Support\Carbon $end
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
|
||||
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): \Illuminate\Support\Collection
|
||||
{
|
||||
// * Totalize by date all answers.
|
||||
$answersByDays = CustomerAnswer::query()->whereDate('created_at', '>=', $start)
|
||||
$answersByDays = ModelAnswer::query()
|
||||
->whereDate('created_at', '>=', $start)
|
||||
->whereDate('created_at', '<=', $end)
|
||||
->whereIn('answer_id', $answers->pluck('id'))
|
||||
->get()->map(function ($custAnswer) {
|
||||
->get()
|
||||
->map(function ($custAnswer) {
|
||||
return [
|
||||
'answer_id' => $custAnswer->answer_id,
|
||||
'answer_id' => $custAnswer->answer_id,
|
||||
'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
|
||||
];
|
||||
})->groupBy(['created_at', 'answer_id'])->map(function ($day) {
|
||||
})
|
||||
->groupBy(['created_at', 'answer_id'])
|
||||
->map(function ($day) {
|
||||
return $day->map(function ($custAnswers) {
|
||||
return $custAnswers->count();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use App\Http\Requests\Api\V4\SingleOptinRequest;
|
|||
use App\Http\Requests\Api\V4\SingleOptinRemoveWithHashcodeRequest;
|
||||
use App\Models\CustomerInfo;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use App\Models\SingleOptin;
|
||||
|
||||
class OptinController extends Controller
|
||||
|
|
@ -26,24 +25,31 @@ class OptinController extends Controller
|
|||
* if this email already has a full account.
|
||||
* @throws \App\Exceptions\Api\V4\CrmException If the single optin could not be added or removed.
|
||||
*/
|
||||
public function optin(SingleOptinRequest $request, Service $service): JsonResponse
|
||||
public function optin(SingleOptinRequest $request, Service $service): \Illuminate\Http\JsonResponse
|
||||
{
|
||||
if (CustomerInfo::getFromEmail($service->brand, $request->email)->exists()) {
|
||||
$validatedData = $request->validated();
|
||||
if (CustomerInfo::getFromEmail($service->brand, $validatedData['email'])->exists()) {
|
||||
throw new ApiErrorException(ApiError::singleOptinCustomerExists);
|
||||
}
|
||||
|
||||
/** @var \App\Models\SingleOptin */
|
||||
$singleOptin = SingleOptin::query()->where('email', $request->email)
|
||||
$singleOptin = SingleOptin::query()
|
||||
->where('email', $validatedData['email'])
|
||||
->whereHas('brand', function ($q) use ($service) {
|
||||
$q->where('id', $service->brand->id);
|
||||
})->firstOrNew();
|
||||
})
|
||||
->firstOrNew();
|
||||
|
||||
if (!$this->changeOptin($service, $request, \boolval($request->opt_in), $singleOptin)) {
|
||||
if (!$this->changeOptin($service, $request, \boolval($validatedData['opt_in']), $singleOptin)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new CrmException('Api failed to update single optin.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if (isset($validatedData['answer_id'])) {
|
||||
$singleOptin->answers()->attach($validatedData['answer_id']);
|
||||
}
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
|
||||
|
|
@ -55,8 +61,10 @@ class OptinController extends Controller
|
|||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \App\Exceptions\Api\V4\CrmException If the single optin could not be removed.
|
||||
*/
|
||||
public function optoutWithHashcode(SingleOptinRemoveWithHashcodeRequest $request, Service $service): JsonResponse
|
||||
{
|
||||
public function optoutWithHashcode(
|
||||
SingleOptinRemoveWithHashcodeRequest $request,
|
||||
Service $service
|
||||
): \Illuminate\Http\JsonResponse {
|
||||
/** @var \App\Models\SingleOptin */
|
||||
$singleOptin = SingleOptin::query()->where('hashcode', $request->hashcode)
|
||||
->whereHas('brand', function ($q) use ($service) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ use App\Models\Answer;
|
|||
use App\Models\Customer;
|
||||
use App\Models\Question;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class QuestionController extends Controller
|
||||
{
|
||||
|
|
@ -19,7 +17,7 @@ class QuestionController extends Controller
|
|||
* @param \App\Models\Service $service
|
||||
* @return \Illuminate\Http\Resources\Json\ResourceCollection
|
||||
*/
|
||||
public function index(Service $service): ResourceCollection
|
||||
public function index(Service $service): \Illuminate\Http\Resources\Json\ResourceCollection
|
||||
{
|
||||
return new QuestionsResource($service->brand->questions);
|
||||
}
|
||||
|
|
@ -31,8 +29,10 @@ class QuestionController extends Controller
|
|||
* @param \App\Models\Customer $customer
|
||||
* @return \Illuminate\Http\Resources\Json\ResourceCollection
|
||||
*/
|
||||
public function indexWithResponse(Service $service, Customer $customer): ResourceCollection
|
||||
{
|
||||
public function indexWithResponse(
|
||||
Service $service,
|
||||
Customer $customer
|
||||
): \Illuminate\Http\Resources\Json\ResourceCollection {
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
|
||||
|
|
@ -47,18 +47,22 @@ class QuestionController extends Controller
|
|||
* @param \App\Models\Customer $customer
|
||||
* @return \Illuminate\Http\Resources\Json\JsonResource
|
||||
*/
|
||||
public function unAnsweredBy(Service $service, Customer $customer): JsonResource
|
||||
public function unAnsweredBy(Service $service, Customer $customer): \Illuminate\Http\Resources\Json\JsonResource
|
||||
{
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
|
||||
$questions = Question::query()->whereHas('brand', function ($q) use ($service) {
|
||||
$q->where('id', $service->brand->id);
|
||||
})->whereDoesntHave('answers', function ($q) use ($customer) {
|
||||
$q->whereIn('id', Answer::query()->whereHas('customers', function ($q) use ($customer) {
|
||||
$q->where('id', $customer->id);
|
||||
})->pluck('id'));
|
||||
})->with('answers')->get();
|
||||
$questions = Question::query()
|
||||
->whereHas('brand', function ($q) use ($service) {
|
||||
$q->where('id', $service->brand->id);
|
||||
})
|
||||
->whereDoesntHave('answers', function ($q) use ($customer) {
|
||||
$q->whereIn('id', Answer::query()->whereHas('customers', function ($q) use ($customer) {
|
||||
$q->where('id', $customer->id);
|
||||
})->pluck('id'));
|
||||
})
|
||||
->with('answers')
|
||||
->get();
|
||||
return new QuestionsResource($questions);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@ use App\Http\Requests\Api\V4\GetQuestionStatsRequest;
|
|||
use App\Http\Resources\Api\V4\BrandStatsResource;
|
||||
use App\Http\Resources\Api\V4\QuestionStatsResource;
|
||||
use App\Models\BrandStat;
|
||||
use App\Models\CustomerAnswer;
|
||||
use App\Models\ModelAnswer;
|
||||
use App\Models\Question;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
|
@ -24,8 +23,10 @@ class StatsController extends Controller
|
|||
* @param \App\Models\Service $service
|
||||
* @return \Illuminate\Http\Resources\Json\ResourceCollection
|
||||
*/
|
||||
public function brandStats(GetBrandStatsRequest $request, Service $service): ResourceCollection
|
||||
{
|
||||
public function brandStats(
|
||||
GetBrandStatsRequest $request,
|
||||
Service $service
|
||||
): \Illuminate\Http\Resources\Json\ResourceCollection {
|
||||
$stats = BrandStat::query()->whereDate('created_at', '>=', $request->start)
|
||||
->whereDate('created_at', '<=', $request->end)
|
||||
->whereBrandId($service->brand->id)->get();
|
||||
|
|
@ -39,8 +40,10 @@ class StatsController extends Controller
|
|||
* @param \App\Models\Question $question
|
||||
* @return \App\Http\Resources\Api\V4\QuestionStatsResource
|
||||
*/
|
||||
public function questionStats(GetQuestionStatsRequest $request, Question $question): QuestionStatsResource
|
||||
{
|
||||
public function questionStats(
|
||||
GetQuestionStatsRequest $request,
|
||||
Question $question
|
||||
): \App\Http\Resources\Api\V4\QuestionStatsResource {
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('questionBelongsToService', $question);
|
||||
|
||||
|
|
@ -62,18 +65,22 @@ class StatsController extends Controller
|
|||
* @param \Illuminate\Support\Carbon $end
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
|
||||
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): \Illuminate\Support\Collection
|
||||
{
|
||||
// * Totalize by date all answers.
|
||||
$answersByDays = CustomerAnswer::query()->whereDate('created_at', '>=', $start)
|
||||
$answersByDays = ModelAnswer::query()
|
||||
->whereDate('created_at', '>=', $start)
|
||||
->whereDate('created_at', '<=', $end)
|
||||
->whereIn('answer_id', $answers->pluck('id'))
|
||||
->get()->map(function ($custAnswer) {
|
||||
->get()
|
||||
->map(function ($custAnswer) {
|
||||
return [
|
||||
'answer_id' => $custAnswer->answer_id,
|
||||
'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
|
||||
];
|
||||
})->groupBy(['created_at', 'answer_id'])->map(function ($day) {
|
||||
})
|
||||
->groupBy(['created_at', 'answer_id'])
|
||||
->map(function ($day) {
|
||||
return $day->map(function ($custAnswers) {
|
||||
return $custAnswers->count();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use App\Http\Requests\Bo\QuestionExport;
|
|||
use App\Http\Requests\Bo\Questions\StoreQuestionRequest;
|
||||
use App\Http\Requests\Bo\Questions\UpdateQuestionRequest;
|
||||
use App\Models\Brand;
|
||||
use App\Models\Customer;
|
||||
use App\Models\Question;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -202,10 +203,12 @@ class QuestionController extends Controller
|
|||
foreach ($questions as $question) {
|
||||
$answersCounts = DB::query()
|
||||
->selectRaw('count(*) as total, answer_id')
|
||||
->from('customer_answer')
|
||||
->from('model_answer')
|
||||
->where('model_type', '=', Customer::class)
|
||||
->whereIn('answer_id', $question->answers->pluck('id'))
|
||||
->groupBy('answer_id')
|
||||
->get()->keyBy('answer_id');
|
||||
->get()
|
||||
->keyBy('answer_id');
|
||||
|
||||
$results[$question->id] = [
|
||||
'question' => $question->question,
|
||||
|
|
@ -218,7 +221,7 @@ class QuestionController extends Controller
|
|||
$answersCounts[$answer->id]->total ?? 0
|
||||
];
|
||||
}
|
||||
}
|
||||
} //end foreach
|
||||
|
||||
$w = Writer::createFromString('');
|
||||
$w->setDelimiter(';');
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Answer;
|
||||
use App\Models\Question;
|
||||
use App\Models\SingleOptin;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
|
|
@ -19,14 +20,17 @@ class SingleOptinController extends Controller
|
|||
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $query */
|
||||
$query = SingleOptin::query();
|
||||
$query = SingleOptin::query()
|
||||
->when(
|
||||
$request->filled('brand'),
|
||||
fn($q) => $q->whereHas('brand', fn($q) => $q->where('id', $request->brand))
|
||||
)
|
||||
->when(
|
||||
$request->filled('answer'),
|
||||
fn($q) => $q->whereHas('answers', fn($q) => $q->where('id', $request->answer))
|
||||
);
|
||||
|
||||
if ($request->brand) {
|
||||
$query = $query->whereHas('brand', function (Builder $q) use ($request) {
|
||||
$q->where('id', $request->brand);
|
||||
});
|
||||
}
|
||||
if ($request->search) {
|
||||
if ($request->filled('search')) {
|
||||
$this->searchQuery(
|
||||
$query,
|
||||
$request->search,
|
||||
|
|
@ -36,16 +40,28 @@ class SingleOptinController extends Controller
|
|||
}
|
||||
|
||||
$this->sortQuery($query);
|
||||
$searchFields = trans('E-mail');
|
||||
$query->with('brand');
|
||||
|
||||
$query->with(['brand']);
|
||||
|
||||
/** @var \Illuminate\Pagination\AbstractPaginator */
|
||||
$singleoptinModels = $this->paginate($query);
|
||||
$searchFields = trans('E-mail');
|
||||
|
||||
return view('bo.pages.singleoptins.index', compact('singleoptinModels', 'searchFields'));
|
||||
if ($request->filled('brand')) {
|
||||
$questionModels = Question::where('brand_id', $request->brand)->get();
|
||||
|
||||
if ($request->filled('question')) {
|
||||
$answerModels = Answer::where('question_id', $request->question)->get();
|
||||
}
|
||||
}
|
||||
|
||||
return view('bo.pages.singleoptins.index', [
|
||||
'singleoptinModels' => $singleoptinModels,
|
||||
'searchFields' => $searchFields,
|
||||
'questionModels' => $questionModels ?? null,
|
||||
'answerModels' => $answerModels ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ class SingleOptinRequest extends BaseApiRequest
|
|||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email:rfc,dns,spoof,filter',
|
||||
'opt_in' => 'required|boolean'
|
||||
'email' => 'required|email:rfc,dns,spoof,filter',
|
||||
'opt_in' => 'required|boolean',
|
||||
'answer_id' => 'sometimes|array|min:1',
|
||||
'answer_id.*' => 'sometimes|integer|exists:answers,id',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ class SingleOptinRequest extends BaseApiRequest
|
|||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email:rfc,dns,spoof,filter',
|
||||
'opt_in' => 'required|boolean'
|
||||
'email' => 'required|email:rfc,dns,spoof,filter',
|
||||
'opt_in' => 'required|boolean',
|
||||
'answer_id' => 'sometimes|array|min:1',
|
||||
'answer_id.*' => 'sometimes|integer|exists:answers,id',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,20 +11,20 @@ use Illuminate\Support\Collection;
|
|||
class QuestionsResource extends JsonResourceCollection
|
||||
{
|
||||
/** @var \Illuminate\Support\Collection<\App\Models\CustomerAnswer> */
|
||||
protected $userAnwsers = null;
|
||||
protected $userAnswers = null;
|
||||
|
||||
/**
|
||||
* Create a new resource instance.
|
||||
*
|
||||
* @param mixed $resource
|
||||
* @param \Illuminate\Support\Collection<\App\Models\CustomerAnswer> $userAnwsers
|
||||
* @param \Illuminate\Support\Collection<\App\Models\CustomerAnswer> $userAnswers
|
||||
* @return void
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
|
||||
*/
|
||||
public function __construct(mixed $resource, Collection $userAnwsers = null)
|
||||
public function __construct(mixed $resource, Collection $userAnswers = null)
|
||||
{
|
||||
//phpcs:enable
|
||||
$this->userAnwsers = $userAnwsers;
|
||||
$this->userAnswers = $userAnswers;
|
||||
parent::__construct($resource);
|
||||
}
|
||||
|
||||
|
|
@ -47,9 +47,9 @@ class QuestionsResource extends JsonResourceCollection
|
|||
'question' => $question->question,
|
||||
'answers' => new AnswersResource($answers)
|
||||
];
|
||||
if ($this->userAnwsers) {
|
||||
if ($this->userAnswers) {
|
||||
$question['customerAnswers'] = new AnswersResource($answers->filter(function (Answer $answer) {
|
||||
return $this->userAnwsers->keyBy('id')->has($answer->id);
|
||||
return $this->userAnswers->keyBy('id')->has($answer->id);
|
||||
}));
|
||||
}
|
||||
return $question;
|
||||
|
|
|
|||
|
|
@ -11,20 +11,20 @@ use Illuminate\Support\Collection;
|
|||
class QuestionsResource extends JsonResourceCollection
|
||||
{
|
||||
/** @var \Illuminate\Support\Collection<\App\Models\CustomerAnswer> */
|
||||
protected $userAnwsers = null;
|
||||
protected $userAnswers = null;
|
||||
|
||||
/**
|
||||
* Create a new resource instance.
|
||||
*
|
||||
* @param mixed $resource
|
||||
* @param \Illuminate\Support\Collection<\App\Models\CustomerAnswer> $userAnwsers
|
||||
* @param \Illuminate\Support\Collection<\App\Models\CustomerAnswer> $userAnswers
|
||||
* @return void
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
|
||||
*/
|
||||
public function __construct(mixed $resource, Collection $userAnwsers = null)
|
||||
public function __construct(mixed $resource, Collection $userAnswers = null)
|
||||
{
|
||||
//phpcs:enable
|
||||
$this->userAnwsers = $userAnwsers;
|
||||
$this->userAnswers = $userAnswers;
|
||||
parent::__construct($resource);
|
||||
}
|
||||
|
||||
|
|
@ -47,9 +47,9 @@ class QuestionsResource extends JsonResourceCollection
|
|||
'question' => $question->question,
|
||||
'answers' => new AnswersResource($answers)
|
||||
];
|
||||
if ($this->userAnwsers) {
|
||||
if ($this->userAnswers) {
|
||||
$question['customerAnswers'] = new AnswersResource($answers->filter(function (Answer $answer) {
|
||||
return $this->userAnwsers->keyBy('id')->has($answer->id);
|
||||
return $this->userAnswers->keyBy('id')->has($answer->id);
|
||||
}));
|
||||
}
|
||||
return $question;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
|
||||
*
|
||||
* @property-read \App\Models\Question $question Question BelongsTo relation.
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<App\Models\Customer>
|
||||
* $customers Customer BelongsToMany relation.
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<Illuminate\Database\Eloquent\Model> $model Morph to relation.
|
||||
*/
|
||||
class Answer extends Model
|
||||
{
|
||||
|
|
@ -42,12 +41,22 @@ class Answer extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* Customer belongsToMany relation.
|
||||
* Customer morph to relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function customers(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
public function customers(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->belongsToMany(Customer::class, 'customer_answer');
|
||||
return $this->morphedByMany(Customer::class, 'model', 'model_answer', 'answer_id', 'model_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer morph to relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function singleOptins(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->morphedByMany(SingleOptin::class, 'model', 'model_answer', 'answer_id', 'model_id');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
|
@ -24,7 +23,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||
* @property-read \Illuminate\Database\Eloquent\Collection<App\Models\Brand>
|
||||
* $brands Brand BelongsToMany relation.
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<App\Models\Answer>
|
||||
* $answers Answer BelongsToMany relation.
|
||||
* $answers Answer morph many relation.
|
||||
*/
|
||||
class Customer extends Model
|
||||
{
|
||||
|
|
@ -86,13 +85,13 @@ class Customer extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* Answers belongsToMany relation.
|
||||
* Answers morph to many relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function answers(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
public function answers(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->belongsToMany(Answer::class, 'customer_answer');
|
||||
return $this->morphToMany(Answer::class, 'model', 'model_answer', 'model_id', 'answer_id');
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
|
@ -217,14 +216,17 @@ class Customer extends Model
|
|||
* @param \App\Models\Service $service
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function getAnswers(Service $service): Collection
|
||||
public function getAnswers(Service $service): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return Answer::query()->whereHas('customers', function (Builder $q) {
|
||||
$q->where('id', $this->id);
|
||||
})->whereHas('question', function (Builder $q) use ($service) {
|
||||
$q->whereHas('brand', function (Builder $q) use ($service) {
|
||||
$q->where('id', $service->brand->id);
|
||||
});
|
||||
})->get();
|
||||
return Answer::query()
|
||||
->whereHas('customers', function (Builder $q) {
|
||||
$q->where('id', $this->id);
|
||||
})
|
||||
->whereHas('question', function (Builder $q) use ($service) {
|
||||
$q->whereHas('brand', function (Builder $q) use ($service) {
|
||||
$q->where('id', $service->brand->id);
|
||||
});
|
||||
})
|
||||
->get();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
/**
|
||||
* @property integer $answer_id Foreign key Answer.
|
||||
* @property integer $customer_id Foreign key Customer.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Created date.
|
||||
*
|
||||
* @property-read \App\Models\Customer $customer Customer BelongsTo relation.
|
||||
* @property-read \App\Models\Answer $answer Answer BelongsTo relation.
|
||||
*/
|
||||
class CustomerAnswer extends Pivot
|
||||
{
|
||||
/**
|
||||
* Inhibit update_at column as we only have
|
||||
* created_at.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getUpdatedAtColumn()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Customer belongsTo relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function customer(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Customer::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer belongsTo relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function answer(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Answer::class);
|
||||
}
|
||||
}
|
||||
66
app/Models/ModelAnswer.php
Normal file
66
app/Models/ModelAnswer.php
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $answer_id
|
||||
* @property integer $model_id
|
||||
* @property string $model_type
|
||||
* @property-read \Illuminate\Support\Carbon $created_at
|
||||
*
|
||||
* @property-read \App\Models\Answer $answer
|
||||
* @property-read \Illuminate\Database\Eloquent\Model $model
|
||||
*/
|
||||
class ModelAnswer extends Model
|
||||
{
|
||||
/**
|
||||
* Define table name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'model_answer';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'answer_id',
|
||||
'model_id',
|
||||
'model_type',
|
||||
];
|
||||
|
||||
/**
|
||||
* Inhibit update_at column as we only have
|
||||
* created_at.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getUpdatedAtColumn(): null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer belongsTo relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
*/
|
||||
public function model(): \Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Relation vers la réponse.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function answer()
|
||||
{
|
||||
return $this->belongsTo(Answer::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,14 @@ use Illuminate\Database\Eloquent\Model;
|
|||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property integer $hashcode Hashcode.
|
||||
* @property integer $brand_id Foreign key Brand.
|
||||
* @property string $email Email.
|
||||
* @property integer $hashcode Hashcode.
|
||||
* @property integer $brand_id Foreign key Brand.
|
||||
* @property string $email Email.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Created date.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
|
||||
|
||||
* @property-read \App\Models\Brand $brand Brand BelongsTo relation.
|
||||
* @property-read \App\Models\Brand $brand Brand BelongsTo relation.
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<App\Models\Answer> $answers Answer morph many relation.
|
||||
*/
|
||||
class SingleOptin extends Model
|
||||
{
|
||||
|
|
@ -72,4 +73,14 @@ class SingleOptin extends Model
|
|||
{
|
||||
return $this->belongsTo(Brand::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Answers morph to many relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function answers(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->morphToMany(Answer::class, 'model', 'model_answer', 'model_id', 'answer_id');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ldc/ldccrm",
|
||||
"version": "4.0.9",
|
||||
"version": "4.1.0",
|
||||
"type": "project",
|
||||
"description": "LDC CRM",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
32
database/factories/SingleOptinFactory.php
Normal file
32
database/factories/SingleOptinFactory.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Brand;
|
||||
use App\Models\SingleOptin;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SingleOptinFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = SingleOptin::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'hashcode' => Str::uuid(),
|
||||
'email' => $this->faker->email(),
|
||||
'brand_id' => $this->faker->randomElement(Brand::pluck('id')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('customer_answer', function (Blueprint $table) {
|
||||
$table->dropForeign('customer_answer_answer_id_foreign');
|
||||
$table->dropForeign('customer_answer_customer_id_foreign');
|
||||
$table->dropIndex('customer_answer_answer_id_foreign');
|
||||
$table->dropIndex('customer_answer_customer_id_foreign');
|
||||
});
|
||||
|
||||
Schema::rename("customer_answer", "model_answer");
|
||||
|
||||
Schema::table('model_answer', function (Blueprint $table) {
|
||||
$table->renameColumn('customer_id', 'model_id');
|
||||
|
||||
// Change to string because key of single optin is hashcode.
|
||||
$table->string('model_id')->change();
|
||||
$table->string('model_type')->after('model_id');
|
||||
});
|
||||
|
||||
DB::table('model_answer')->update(['model_type' => 'App\Models\Customer']);
|
||||
|
||||
Schema::table('model_answer', function (Blueprint $table) {
|
||||
$table->foreign('answer_id')
|
||||
->references('id')->on('answers')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('model_answer', function (Blueprint $table) {
|
||||
$table->dropForeign(['answer_id']);
|
||||
});
|
||||
|
||||
Schema::table('model_answer', function (Blueprint $table) {
|
||||
$table->dropColumn('model_type');
|
||||
$table->renameColumn('model_id', 'customer_id');
|
||||
});
|
||||
|
||||
Schema::rename("model_answer", "customer_answer");
|
||||
|
||||
Schema::table('customer_answer', function (Blueprint $table) {
|
||||
$table->foreign('answer_id')->references('id')->on('answers');
|
||||
$table->foreign('customer_id')->references('id')->on('customers');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Answer;
|
||||
use App\Models\Brand;
|
||||
use App\Models\Customer;
|
||||
use App\Models\CustomerInfo;
|
||||
|
|
@ -15,33 +16,33 @@ class CustomerSeeder extends Seeder
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
public function run(): void
|
||||
{
|
||||
$hasSetAtLeasOneActivated = false;
|
||||
Customer::factory(10)->create()->each(
|
||||
function ($customer) use (&$hasSetAtLeasOneActivated) {
|
||||
/** @var \App\Models\Brand */
|
||||
foreach (Brand::all() as $brand) {
|
||||
CustomerInfo::factory(1)->make()->each(
|
||||
function ($customerInfo) use ($customer, $brand) {
|
||||
$customerInfo->customer()->associate($customer);
|
||||
$customerInfo->brand()->associate($brand);
|
||||
$customerInfo->save();
|
||||
Customer::factory(10)->create()->each(function ($customer) use (&$hasSetAtLeasOneActivated) {
|
||||
/** @var \App\Models\Brand */
|
||||
foreach (Brand::all() as $brand) {
|
||||
CustomerInfo::factory(1)->make()->each(
|
||||
function ($customerInfo) use ($customer, $brand) {
|
||||
$customerInfo->customer()->associate($customer);
|
||||
$customerInfo->brand()->associate($brand);
|
||||
$customerInfo->save();
|
||||
}
|
||||
);
|
||||
CustomerLogin::factory(1)->make()->each(
|
||||
function ($customerLogin) use ($customer, $brand, &$hasSetAtLeasOneActivated) {
|
||||
$customerLogin->customer()->associate($customer);
|
||||
$customerLogin->brand()->associate($brand);
|
||||
if (!$hasSetAtLeasOneActivated) {
|
||||
$hasSetAtLeasOneActivated = true;
|
||||
$customerLogin->status = true;
|
||||
}
|
||||
);
|
||||
CustomerLogin::factory(1)->make()->each(
|
||||
function ($customerLogin) use ($customer, $brand, &$hasSetAtLeasOneActivated) {
|
||||
$customerLogin->customer()->associate($customer);
|
||||
$customerLogin->brand()->associate($brand);
|
||||
if (!$hasSetAtLeasOneActivated) {
|
||||
$hasSetAtLeasOneActivated = true;
|
||||
$customerLogin->status = true;
|
||||
}
|
||||
$customerLogin->save();
|
||||
}
|
||||
);
|
||||
}
|
||||
$customerLogin->save();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
$answerModels = Answer::inRandomOrder()->take(rand(1, 3))->pluck('id');
|
||||
$customer->answers()->attach($answerModels);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ class DatabaseSeeder extends Seeder
|
|||
BrandSeeder::class,
|
||||
CustomerSeeder::class,
|
||||
BrandStatSeeder::class,
|
||||
UserSeeder::class
|
||||
UserSeeder::class,
|
||||
SingleOptinSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
database/seeders/SingleOptinSeeder.php
Normal file
23
database/seeders/SingleOptinSeeder.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Answer;
|
||||
use App\Models\SingleOptin;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class SingleOptinSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
SingleOptin::factory(20)->create()->each(function (SingleOptin $singleOptin) {
|
||||
$answerModels = Answer::inRandomOrder()->take(rand(1, 3))->pluck('id');
|
||||
$singleOptin->answers()->attach($answerModels);
|
||||
});
|
||||
}
|
||||
}
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "srcs",
|
||||
"name": "crm",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import "./base/common";
|
|||
// CUSTOM
|
||||
import "./pages/customer.delete";
|
||||
import "./pages/data.delete";
|
||||
import "./pages/search-bar-brand";
|
||||
import "./pages/search-bar";
|
||||
import "./pages/service.index";
|
||||
import "./pages/stats";
|
||||
import "./pages/statsExport";
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
const brandSelect = document.getElementById("brandSelect") as HTMLSelectElement;
|
||||
if (brandSelect) {
|
||||
brandSelect.addEventListener("change", function () {
|
||||
if (brandSelect.value !== "0") {
|
||||
const url = new URL(location.href);
|
||||
url.searchParams.set("brand", brandSelect.value);
|
||||
url.searchParams.delete("rst");
|
||||
location.assign(url.search);
|
||||
}
|
||||
});
|
||||
}
|
||||
30
resources/ts/bo/pages/search-bar.ts
Normal file
30
resources/ts/bo/pages/search-bar.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
enum FilterKey {
|
||||
Brand = "brand",
|
||||
Question = "question",
|
||||
Answer = "answer",
|
||||
}
|
||||
|
||||
function setupFilterListener(selectId: string, paramKey: FilterKey): void {
|
||||
const select = document.getElementById(selectId) as HTMLSelectElement | null;
|
||||
if (!select) return;
|
||||
|
||||
select.addEventListener("change", () => {
|
||||
const value = select.value;
|
||||
if (value !== "0") {
|
||||
const url = new URL(location.href);
|
||||
url.searchParams.set(paramKey, value);
|
||||
url.searchParams.delete("rst");
|
||||
location.assign(url.search);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const filterMap: Record<string, FilterKey> = {
|
||||
brandSelect: FilterKey.Brand,
|
||||
questionSelect: FilterKey.Question,
|
||||
answerSelect: FilterKey.Answer,
|
||||
};
|
||||
|
||||
Object.entries(filterMap).forEach(([selectId, paramKey]) => {
|
||||
setupFilterListener(selectId, paramKey);
|
||||
});
|
||||
|
|
@ -1,26 +1,72 @@
|
|||
<form action="{{ request()->fullUrlWithQuery(['rst' => null]) }}">
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text" for="brandSelect">{{ __('Filter par Marque') }}</label>
|
||||
<select class="form-select" id="brandSelect">
|
||||
<option value="0" @if (!request()->brand) selected @endif>{{ __('Sélectionnez une marque..') }}</option>
|
||||
@foreach (\App\Models\Brand::all() as $brand)
|
||||
<option value="{{ $brand->id }}" @if (request()->brand == "$brand->id") selected @endif>{{ $brand->name }}</option>
|
||||
@php
|
||||
$selectedBrand = request()->brand;
|
||||
$selectedQuestion = request()->question;
|
||||
$selectedAnswer = request()->answer;
|
||||
$search = request()->search;
|
||||
$sortCol = request()->sort_col;
|
||||
$sortWay = request()->sort_way;
|
||||
@endphp
|
||||
|
||||
<form action="{{ request()->fullUrlWithQuery(['rst' => null]) }}" class="row gx-2">
|
||||
<div class="col-12 col-lg-6">
|
||||
{{-- BRAND SELECT --}}
|
||||
<select class="form-select mb-1" id="brandSelect">
|
||||
<option value="0" @selected(!$selectedBrand)>{{ __('Sélectionnez une marque...') }}</option>
|
||||
@foreach (\App\Models\Brand::all() as $brand)
|
||||
<option value="{{ $brand->getKey() }}" @selected($selectedBrand == $brand->getKey())>
|
||||
{{ $brand->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
{{-- QUESTION SELECT --}}
|
||||
@if (request()->routeIs('bo.single_optins.index') && $selectedBrand && $questionModels)
|
||||
<select class="form-select mb-1" id="questionSelect">
|
||||
<option value="0" @selected(!$selectedQuestion)>{{ __('Sélectionnez une question...') }}</option>
|
||||
@foreach ($questionModels as $question)
|
||||
<option value="{{ $question->getKey() }}" @selected($selectedQuestion == $question->getKey())>
|
||||
{{ $question->question }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@endif
|
||||
|
||||
{{-- ANSWER SELECT --}}
|
||||
@if ($selectedQuestion && $answerModels)
|
||||
<select class="form-select mb-1" id="answerSelect">
|
||||
<option value="0" @selected(!$selectedAnswer)>{{ __('Sélectionnez une réponse...') }}</option>
|
||||
@foreach ($answerModels as $answer)
|
||||
<option value="{{ $answer->getKey() }}" @selected($selectedAnswer == $answer->getKey())>
|
||||
{{ $answer->answer }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 d-flex justify-content-center align-items-start">
|
||||
<div class="input-group">
|
||||
{{-- SEARCH FIELD --}}
|
||||
<label class="input-group-text" for="searchField">{{ $searchFields }}</label>
|
||||
<input class="form-control" id="searchField" name="search" type="search" value="{{ $search }}"
|
||||
placeholder="Recherche.." />
|
||||
|
||||
{{-- HIDDEN INPUTS --}}
|
||||
<input type="hidden" name="sort_col" type="text" value="{{ $sortCol }}">
|
||||
<input type="hidden" name="sort_way" type="text" value="{{ $sortWay }}">
|
||||
@foreach (['brand' => $selectedBrand, 'question' => $selectedQuestion, 'answer' => $selectedAnswer] as $key => $val)
|
||||
@if ($val)
|
||||
<input type="hidden" name="{{ $key }}" type="text" value="{{ $val }}">
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="input-group-text bg-warning text-bg-warning">🔍🠚</div>
|
||||
<label class="input-group-text" for="searchField">{{ $searchFields }}</label>
|
||||
<input class="form-control" id="searchField" name="search" type="search" value="{{ request()->search }}"
|
||||
placeholder="Recherche.." />
|
||||
<input class="d-none" name="sort_col" type="text" value="{{ request()->sort_col }}" />
|
||||
<input class="d-none" name="sort_way" type="text" value="{{ request()->sort_way }}" />
|
||||
@if (request()->brand)
|
||||
<input class="d-none" name="brand" type="hidden" value="{{ request()->brand }}" />
|
||||
@endif
|
||||
<button class="btn btn-primary" data-bs-toggle="tooltip" type="submit" title="{{ __('Lancer la recherche') }}">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
<a class="btn btn-danger" data-bs-toggle="tooltip"
|
||||
href="{{ request()->fullUrlWithQuery(['rst' => 'true', 'search' => null, 'brand' => null]) }}"
|
||||
|
||||
{{-- ACTION BUTTONS --}}
|
||||
<button class="btn btn-primary" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('Lancer la recherche') }}">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
<a class="btn btn-danger ms-2" data-bs-toggle="tooltip"
|
||||
href="{{ request()->fullUrlWithQuery(['rst' => 'true', 'search' => null, 'brand' => null, 'question' => null, 'answer' => null]) }}"
|
||||
title="{{ __('Supprimer la recherche') }}">
|
||||
<i class="fas fa-backspace"></i>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<form id="searchForm" action="/{{ request()->path() }}">
|
||||
<div class="input-group pt-3">
|
||||
<div class="input-group">
|
||||
<label class="input-group-text" for="searchField">{{ $searchFields }}</label>
|
||||
@if(request()->sort_col)
|
||||
<input class="d-none" type="text" name="sort_col" value="{{ request()->sort_col }}">
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@
|
|||
<tr>
|
||||
<td class="text-center align-middle">{{ $answerModel->answer }}</td>
|
||||
<td class="text-center align-middle">
|
||||
<span
|
||||
class="badge bg-warning text-bg-warning text-wrap">{{ $answerModel->updated_at->formatLocalized('%d/%m/%Y à %H:%M') }}</span>
|
||||
<span class="badge bg-warning text-bg-warning text-wrap">
|
||||
{{ $answerModel->updated_at->isoFormat('LLLL') }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end align-middle">
|
||||
<form class="btn-group" id="dataDelete"
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Route::prefix('v2')->name('apiV2.')->group(function () {
|
|||
|
||||
// * CUSTOMERS
|
||||
Route::apiResource('customers', V2CustomerController::class, [
|
||||
'except' => 'index',
|
||||
'except' => 'index',
|
||||
'parameters' => ['customers' => 'serviceCustomer']
|
||||
]);
|
||||
Route::prefix('customers')->name('customers.')->group(function () {
|
||||
|
|
@ -96,7 +96,7 @@ Route::prefix('v4')->name('apiV4.')->group(function () {
|
|||
|
||||
// * CUSTOMERS
|
||||
Route::apiResource('customers', V4CustomerController::class, [
|
||||
'except' => 'index',
|
||||
'except' => 'index',
|
||||
'parameters' => ['customers' => 'serviceCustomer']
|
||||
]);
|
||||
Route::prefix('customers')->name('customers.')->group(function () {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class ApiBase extends BaseTestCase
|
|||
*
|
||||
* @param boolean $status The customer status for a brand.
|
||||
* @param boolean $random Randomize the customer to get.
|
||||
* @return array
|
||||
* @return array Data in array: customer, service, login, brand, info.
|
||||
*/
|
||||
protected function getCustomer(bool $status = true, bool $random = true): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ namespace Tests\CRM\ApiV2;
|
|||
|
||||
use App\Enums\ApiError;
|
||||
use App\Enums\ApiStatus;
|
||||
use App\Models\CustomerAnswer;
|
||||
use App\Models\Customer;
|
||||
use App\Models\ModelAnswer;
|
||||
use App\Models\Question;
|
||||
use App\Models\Service;
|
||||
|
||||
|
|
@ -80,16 +81,13 @@ class QuestionTest extends ApiBase
|
|||
$firstAnswer = $questions->first()->answers->first();
|
||||
$firstAnswer->load('question');
|
||||
|
||||
/** @var \App\Models\CustomerAnswer $custAnswer */
|
||||
$custAnswer = CustomerAnswer::whereHas('customer', function ($q) use ($customer) {
|
||||
$q->whereId($customer->id);
|
||||
})->whereHas('answer', function ($q) use ($firstAnswer) {
|
||||
$q->whereId($firstAnswer->id);
|
||||
})->firstOrNew();
|
||||
|
||||
$custAnswer->customer()->associate($customer);
|
||||
$custAnswer->answer()->associate($firstAnswer);
|
||||
$custAnswer->saveOrFail();
|
||||
/** @var \App\Models\ModelAnswer $modelAnswer */
|
||||
$modelAnswer = ModelAnswer::firstOrNew([
|
||||
'model_type' => Customer::class,
|
||||
'model_id' => $customer->getKey(),
|
||||
'answer_id' => $firstAnswer->getKey(),
|
||||
]);
|
||||
$modelAnswer->saveOrFail();
|
||||
|
||||
// * Call
|
||||
$response = $this->callApiWithToken($service, 'get', route(
|
||||
|
|
@ -176,16 +174,13 @@ class QuestionTest extends ApiBase
|
|||
$firstAnswer = $questions->first()->answers->first();
|
||||
$firstAnswer->load('question');
|
||||
|
||||
/** @var \App\Models\CustomerAnswer $custAnswer */
|
||||
$custAnswer = CustomerAnswer::whereHas('customer', function ($q) use ($customer) {
|
||||
$q->whereId($customer->id);
|
||||
})->whereHas('answer', function ($q) use ($firstAnswer) {
|
||||
$q->whereId($firstAnswer->id);
|
||||
})->firstOrNew();
|
||||
|
||||
$custAnswer->customer()->associate($customer);
|
||||
$custAnswer->answer()->associate($firstAnswer);
|
||||
$custAnswer->saveOrFail();
|
||||
/** @var \App\Models\ModelAnswer $modelAnswer */
|
||||
$modelAnswer = ModelAnswer::firstOrNew([
|
||||
'model_type' => Customer::class,
|
||||
'model_id' => $customer->getKey(),
|
||||
'answer_id' => $firstAnswer->getKey(),
|
||||
]);
|
||||
$modelAnswer->saveOrFail();
|
||||
|
||||
// * Call
|
||||
$response = $this->callApiWithToken($service, 'get', route(
|
||||
|
|
@ -280,11 +275,14 @@ class QuestionTest extends ApiBase
|
|||
$json = $response->decodeResponseJson();
|
||||
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
|
||||
|
||||
$this->assertTrue(CustomerAnswer::whereHas('customer', function ($q) use ($customer) {
|
||||
$q->whereId($customer->id);
|
||||
})->whereHas('answer', function ($q) use ($answer) {
|
||||
$q->whereId($answer->id);
|
||||
})->exists(), 'Question should have been answered.');
|
||||
$this->assertTrue(
|
||||
ModelAnswer::query()->where([
|
||||
['model_type', Customer::class],
|
||||
['model_id', $customer->getKey()],
|
||||
['answer_id', $answer->getKey()]
|
||||
])->exists(),
|
||||
'Question should have been answered.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ namespace Tests\CRM\ApiV2;
|
|||
use App\Enums\ApiError;
|
||||
use App\Enums\ApiStatus;
|
||||
use App\Enums\Country;
|
||||
use App\Models\ModelAnswer;
|
||||
use App\Models\Question;
|
||||
use App\Models\Service;
|
||||
use App\Models\SingleOptin;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
|
@ -37,6 +39,43 @@ class SingleOptinTest extends ApiBase
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test set single optin on with answer associated.
|
||||
* route: apiV2.optin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSetSingleOptinOnWithAnswerAssociated(): void
|
||||
{
|
||||
$service = Service::firstOrFail();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection $questions */
|
||||
$questions = Question::query()->whereHas('brand', function ($q) use ($service) {
|
||||
$q->whereId($service->brand->getKey());
|
||||
})->with('answers')->get()->keyBy('id');
|
||||
|
||||
// * Make sure a customer answer is set.
|
||||
$firstAnswer = $questions->first()->answers->first();
|
||||
|
||||
$response = $this->callApiWithToken($service, 'post', route('apiV2.optin'), [
|
||||
'email' => 'singleoptin@hotmail.fr',
|
||||
'opt_in' => 1,
|
||||
'answer_id' => [$firstAnswer->getkey()],
|
||||
]);
|
||||
|
||||
$singleOptin = SingleOptin::query()->where('email', 'singleoptin@hotmail.fr');
|
||||
$json = $response->decodeResponseJson();
|
||||
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
|
||||
$this->assertTrue(
|
||||
$singleOptin->exists() && ModelAnswer::query()->where([
|
||||
['answer_id', $firstAnswer->getkey()],
|
||||
['model_id', !is_null($singleOptin->first()) ? $singleOptin->first()->getKey() : null],
|
||||
['model_type', SingleOptin::class],
|
||||
])->exists(),
|
||||
'Single optin should have been set for email "singleoptin@hotmail.fr"'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test set single optin off.
|
||||
* route: apiV2.optin
|
||||
|
|
@ -87,6 +126,25 @@ class SingleOptinTest extends ApiBase
|
|||
$this->assertEquals(ApiError::singleOptinCustomerExists->label(), $json->json('data.message'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test set single optin on with anexisting answer associated.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSingleOptinShouldFailIfAnswerIdDoesntExist(): void
|
||||
{
|
||||
$service = Service::query()->firstOrFail();
|
||||
$response = $this->callApiWithToken($service, 'post', route('apiV2.optin'), [
|
||||
'email' => 'singleoptin@hotmail.fr',
|
||||
'opt_in' => 1,
|
||||
'answer_id' => [0],
|
||||
]);
|
||||
|
||||
$json = $response->decodeResponseJson();
|
||||
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
|
||||
$this->assertEquals(ApiError::validationFailed->value(), $json->json('data.code'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test set single optin off using hashcode.
|
||||
* route: apiV2.optout-hashcode
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ namespace Tests\CRM\ApiV2;
|
|||
use App\Enums\ApiError;
|
||||
use App\Enums\ApiStatus;
|
||||
use App\Models\Customer;
|
||||
use App\Models\CustomerAnswer;
|
||||
use App\Models\ModelAnswer;
|
||||
use App\Models\Question;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
|
@ -106,11 +106,11 @@ class StatsTest extends ApiBase
|
|||
$end = Carbon::now();
|
||||
|
||||
// * Make sure a customer answer is set.
|
||||
/** @var \App\Models\CustomerAnswer $custAnswer */
|
||||
$custAnswer = CustomerAnswer::whereHas('answer', function ($q) use ($answersIds) {
|
||||
$q->whereId($answersIds->first());
|
||||
})->firstOrNew();
|
||||
$custAnswer->customer()->associate($custAnswer->customer ?? Customer::query()->firstOrFail());
|
||||
/** @var \App\Models\ModelAnswer $custAnswer */
|
||||
$custAnswer = ModelAnswer::query()
|
||||
->where([['answer_id', $answersIds->first()], ['model_type', Customer::class]])
|
||||
->firstOrNew();
|
||||
$custAnswer->model()->associate($custAnswer->model_id ?? Customer::query()->firstOrFail());
|
||||
$custAnswer->answer()->associate($answersIds->first());
|
||||
$custAnswer->saveOrFail();
|
||||
|
||||
|
|
@ -122,11 +122,8 @@ class StatsTest extends ApiBase
|
|||
$datesIndexes = [];
|
||||
|
||||
do {
|
||||
$datesIndexes[$start->isoFormat('YYYY-MM-DD')] = $answersIds
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
->keys()->all();
|
||||
|
||||
$start = $start->addDay();
|
||||
$datesIndexes[$start->isoFormat('YYYY-MM-DD')] = $answersIds->keys()->all();
|
||||
$start = $start->addDay();
|
||||
} while ($start->lessThanOrEqualTo($end));
|
||||
|
||||
$json = $response->decodeResponseJson();
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class ApiBase extends BaseTestCase
|
|||
*
|
||||
* @param boolean $status The customer status for a brand.
|
||||
* @param boolean $random Randomize the customer to get.
|
||||
* @return array
|
||||
* @return array Data in array: customer, service, login, brand, info.
|
||||
*/
|
||||
protected function getCustomer(bool $status = true, bool $random = true): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ namespace Tests\CRM\ApiV4;
|
|||
|
||||
use App\Enums\ApiError;
|
||||
use App\Enums\ApiStatus;
|
||||
use App\Models\CustomerAnswer;
|
||||
use App\Models\Customer;
|
||||
use App\Models\ModelAnswer;
|
||||
use App\Models\Question;
|
||||
use App\Models\Service;
|
||||
|
||||
|
|
@ -80,16 +81,13 @@ class QuestionTest extends ApiBase
|
|||
$firstAnswer = $questions->first()->answers->first();
|
||||
$firstAnswer->load('question');
|
||||
|
||||
/** @var \App\Models\CustomerAnswer $custAnswer */
|
||||
$custAnswer = CustomerAnswer::whereHas('customer', function ($q) use ($customer) {
|
||||
$q->whereId($customer->id);
|
||||
})->whereHas('answer', function ($q) use ($firstAnswer) {
|
||||
$q->whereId($firstAnswer->id);
|
||||
})->firstOrNew();
|
||||
|
||||
$custAnswer->customer()->associate($customer);
|
||||
$custAnswer->answer()->associate($firstAnswer);
|
||||
$custAnswer->saveOrFail();
|
||||
/** @var \App\Models\ModelAnswer $modelAnswer */
|
||||
$modelAnswer = ModelAnswer::firstOrNew([
|
||||
'model_type' => Customer::class,
|
||||
'model_id' => $customer->getKey(),
|
||||
'answer_id' => $firstAnswer->getKey(),
|
||||
]);
|
||||
$modelAnswer->saveOrFail();
|
||||
|
||||
// * Call
|
||||
$response = $this->callApiWithToken($service, 'get', route(
|
||||
|
|
@ -176,16 +174,13 @@ class QuestionTest extends ApiBase
|
|||
$firstAnswer = $questions->first()->answers->first();
|
||||
$firstAnswer->load('question');
|
||||
|
||||
/** @var \App\Models\CustomerAnswer $custAnswer */
|
||||
$custAnswer = CustomerAnswer::whereHas('customer', function ($q) use ($customer) {
|
||||
$q->whereId($customer->id);
|
||||
})->whereHas('answer', function ($q) use ($firstAnswer) {
|
||||
$q->whereId($firstAnswer->id);
|
||||
})->firstOrNew();
|
||||
|
||||
$custAnswer->customer()->associate($customer);
|
||||
$custAnswer->answer()->associate($firstAnswer);
|
||||
$custAnswer->saveOrFail();
|
||||
/** @var \App\Models\ModelAnswer $modelAnswer */
|
||||
$modelAnswer = ModelAnswer::firstOrNew([
|
||||
'model_type' => Customer::class,
|
||||
'model_id' => $customer->getKey(),
|
||||
'answer_id' => $firstAnswer->getKey(),
|
||||
]);
|
||||
$modelAnswer->saveOrFail();
|
||||
|
||||
// * Call
|
||||
$response = $this->callApiWithToken($service, 'get', route(
|
||||
|
|
@ -280,11 +275,14 @@ class QuestionTest extends ApiBase
|
|||
$json = $response->decodeResponseJson();
|
||||
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
|
||||
|
||||
$this->assertTrue(CustomerAnswer::whereHas('customer', function ($q) use ($customer) {
|
||||
$q->whereId($customer->id);
|
||||
})->whereHas('answer', function ($q) use ($answer) {
|
||||
$q->whereId($answer->id);
|
||||
})->exists(), 'Question should have been answered.');
|
||||
$this->assertTrue(
|
||||
ModelAnswer::query()->where([
|
||||
['model_type', Customer::class],
|
||||
['model_id', $customer->getKey()],
|
||||
['answer_id', $answer->getKey()]
|
||||
])->exists(),
|
||||
'Question should have been answered.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ namespace Tests\CRM\ApiV4;
|
|||
use App\Enums\ApiError;
|
||||
use App\Enums\ApiStatus;
|
||||
use App\Enums\Country;
|
||||
use App\Models\ModelAnswer;
|
||||
use App\Models\Question;
|
||||
use App\Models\Service;
|
||||
use App\Models\SingleOptin;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
|
@ -37,6 +39,43 @@ class SingleOptinTest extends ApiBase
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test set single optin on with answer associated.
|
||||
* route: apiV2.optin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSetSingleOptinOnWithAnswerAssociated(): void
|
||||
{
|
||||
$service = Service::firstOrFail();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection $questions */
|
||||
$questions = Question::query()->whereHas('brand', function ($q) use ($service) {
|
||||
$q->whereId($service->brand->getKey());
|
||||
})->with('answers')->get()->keyBy('id');
|
||||
|
||||
// * Make sure a customer answer is set.
|
||||
$firstAnswer = $questions->first()->answers->first();
|
||||
|
||||
$response = $this->callApiWithToken($service, 'post', route('apiV2.optin'), [
|
||||
'email' => 'singleoptin@hotmail.fr',
|
||||
'opt_in' => 1,
|
||||
'answer_id' => [$firstAnswer->getkey()],
|
||||
]);
|
||||
|
||||
$singleOptin = SingleOptin::query()->where('email', 'singleoptin@hotmail.fr');
|
||||
$json = $response->decodeResponseJson();
|
||||
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
|
||||
$this->assertTrue(
|
||||
$singleOptin->exists() && ModelAnswer::query()->where([
|
||||
['answer_id', $firstAnswer->getkey()],
|
||||
['model_id', !is_null($singleOptin->first()) ? $singleOptin->first()->getKey() : null],
|
||||
['model_type', SingleOptin::class],
|
||||
])->exists(),
|
||||
'Single optin should have been set for email "singleoptin@hotmail.fr"'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test set single optin off.
|
||||
* route: apiV4.optin
|
||||
|
|
@ -87,6 +126,25 @@ class SingleOptinTest extends ApiBase
|
|||
$this->assertEquals(ApiError::singleOptinCustomerExists->label(), $json->json('data.message'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test set single optin on with anexisting answer associated.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSingleOptinShouldFailIfAnswerIdDoesntExist(): void
|
||||
{
|
||||
$service = Service::query()->firstOrFail();
|
||||
$response = $this->callApiWithToken($service, 'post', route('apiV2.optin'), [
|
||||
'email' => 'singleoptin@hotmail.fr',
|
||||
'opt_in' => 1,
|
||||
'answer_id' => [0],
|
||||
]);
|
||||
|
||||
$json = $response->decodeResponseJson();
|
||||
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
|
||||
$this->assertEquals(ApiError::validationFailed->value(), $json->json('data.code'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test set single optin off using hashcode.
|
||||
* route: apiV4.optout-hashcode
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ namespace Tests\CRM\ApiV4;
|
|||
use App\Enums\ApiError;
|
||||
use App\Enums\ApiStatus;
|
||||
use App\Models\Customer;
|
||||
use App\Models\CustomerAnswer;
|
||||
use App\Models\ModelAnswer;
|
||||
use App\Models\Question;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
|
@ -106,11 +106,11 @@ class StatsTest extends ApiBase
|
|||
$end = Carbon::now();
|
||||
|
||||
// * Make sure a customer answer is set.
|
||||
/** @var \App\Models\CustomerAnswer $custAnswer */
|
||||
$custAnswer = CustomerAnswer::whereHas('answer', function ($q) use ($answersIds) {
|
||||
$q->whereId($answersIds->first());
|
||||
})->firstOrNew();
|
||||
$custAnswer->customer()->associate($custAnswer->customer ?? Customer::query()->firstOrFail());
|
||||
/** @var \App\Models\ModelAnswer $custAnswer */
|
||||
$custAnswer = ModelAnswer::query()
|
||||
->where([['answer_id', $answersIds->first()], ['model_type', Customer::class]])
|
||||
->firstOrNew();
|
||||
$custAnswer->model()->associate($custAnswer->model_id ?? Customer::query()->firstOrFail());
|
||||
$custAnswer->answer()->associate($answersIds->first());
|
||||
$custAnswer->saveOrFail();
|
||||
|
||||
|
|
@ -122,11 +122,8 @@ class StatsTest extends ApiBase
|
|||
$datesIndexes = [];
|
||||
|
||||
do {
|
||||
$datesIndexes[$start->isoFormat('YYYY-MM-DD')] = $answersIds
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
->keys()->all();
|
||||
|
||||
$start = $start->addDay();
|
||||
$datesIndexes[$start->isoFormat('YYYY-MM-DD')] = $answersIds->keys()->all();
|
||||
$start = $start->addDay();
|
||||
} while ($start->lessThanOrEqualTo($end));
|
||||
|
||||
$json = $response->decodeResponseJson();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
namespace Tests\Client;
|
||||
|
||||
use App\Models\CustomerAnswer;
|
||||
use App\Models\Customer;
|
||||
use App\Models\ModelAnswer;
|
||||
use App\Models\Question as ModelsQuestion;
|
||||
use LDC\LDCCRM\CrmApiV4;
|
||||
use LDC\LDCCRM\Models\Answer;
|
||||
|
|
@ -78,16 +79,13 @@ class CrmClientQuestionTest extends CrmClientTester
|
|||
$firstAnswer = $answeredQuestion->answers->first();
|
||||
$firstAnswer->load('question');
|
||||
|
||||
/** @var \App\Models\CustomerAnswer $custAnswer */
|
||||
$custAnswer = CustomerAnswer::whereHas('customer', function ($q) {
|
||||
$q->whereId(static::$customer->id);
|
||||
})->whereHas('answer', function ($q) use ($firstAnswer) {
|
||||
$q->whereId($firstAnswer->id);
|
||||
})->firstOrNew();
|
||||
|
||||
$custAnswer->customer()->associate(static::$customer);
|
||||
$custAnswer->answer()->associate($firstAnswer);
|
||||
$custAnswer->saveOrFail();
|
||||
/** @var \App\Models\ModelAnswer $modelAnswer */
|
||||
$modelAnswer = ModelAnswer::firstOrNew([
|
||||
'model_type' => Customer::class,
|
||||
'model_id' => static::$customer->getKey(),
|
||||
'answer_id' => $firstAnswer->getKey(),
|
||||
]);
|
||||
$modelAnswer->saveOrFail();
|
||||
|
||||
$api = new CrmApiV4();
|
||||
$customer = $api->getCustomerOrNew(static::$info->hashcode);
|
||||
|
|
@ -145,16 +143,13 @@ class CrmClientQuestionTest extends CrmClientTester
|
|||
$firstAnswer = $answeredQuestion->answers->first();
|
||||
$firstAnswer->load('question');
|
||||
|
||||
/** @var \App\Models\CustomerAnswer $custAnswer */
|
||||
$custAnswer = CustomerAnswer::whereHas('customer', function ($q) {
|
||||
$q->whereId(static::$customer->id);
|
||||
})->whereHas('answer', function ($q) use ($firstAnswer) {
|
||||
$q->whereId($firstAnswer->id);
|
||||
})->firstOrNew();
|
||||
|
||||
$custAnswer->customer()->associate(static::$customer);
|
||||
$custAnswer->answer()->associate($firstAnswer);
|
||||
$custAnswer->saveOrFail();
|
||||
/** @var \App\Models\ModelAnswer $modelAnswer */
|
||||
$modelAnswer = ModelAnswer::firstOrNew([
|
||||
'model_type' => Customer::class,
|
||||
'model_id' => static::$customer->getKey(),
|
||||
'answer_id' => $firstAnswer->getKey(),
|
||||
]);
|
||||
$modelAnswer->saveOrFail();
|
||||
|
||||
$api = new CrmApiV4();
|
||||
$customer = $api->getCustomerOrNew(static::$info->hashcode);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue