diff --git a/app/Classes/General/Eloquent/Filters/CreatedAfterOrEqual.php b/app/Classes/General/Eloquent/Filters/CreatedAfterOrEqual.php new file mode 100644 index 00000000..9df98f40 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/CreatedAfterOrEqual.php @@ -0,0 +1,23 @@ +getModel()->getTable(); + $startDate = Carbon::createFromFormat('d-m-Y', $value)->startOfDay(); + return $builder->where("{$table}.created_at", '>=', $startDate); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/CreatedBeforeOrEqual.php b/app/Classes/General/Eloquent/Filters/CreatedBeforeOrEqual.php new file mode 100644 index 00000000..5b349540 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/CreatedBeforeOrEqual.php @@ -0,0 +1,22 @@ +getModel()->getTable(); + $endDate = Carbon::createFromFormat('d-m-Y', $value)->endOfDay(); + return $builder->where("{$table}.created_at", '<=', $endDate); + } +} diff --git a/app/Classes/General/Eloquent/Filters/OwnerId.php b/app/Classes/General/Eloquent/Filters/OwnerId.php index eac7e32d..c2dfa685 100644 --- a/app/Classes/General/Eloquent/Filters/OwnerId.php +++ b/app/Classes/General/Eloquent/Filters/OwnerId.php @@ -14,7 +14,8 @@ class OwnerId implements Filter */ public static function apply(Builder $builder, $value) { - return $builder->where('owner_id', $value); + $table = $builder->getModel()->getTable(); + return $builder->where("{$table}.owner_id", $value); } } \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/OwnerType.php b/app/Classes/General/Eloquent/Filters/OwnerType.php new file mode 100644 index 00000000..3feedccd --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/OwnerType.php @@ -0,0 +1,21 @@ +getModel()->getTable(); + return $builder->where("{$table}.owner_type", $value); + } + +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/StatusIn.php b/app/Classes/General/Eloquent/Filters/StatusIn.php index cbfdfbd0..85425802 100644 --- a/app/Classes/General/Eloquent/Filters/StatusIn.php +++ b/app/Classes/General/Eloquent/Filters/StatusIn.php @@ -14,7 +14,8 @@ class StatusIn implements Filter */ public static function apply(Builder $builder, $value) { - return $builder->whereIn('status', $value); + $table = $builder->getModel()->getTable(); + return $builder->whereIn("{$table}.status", $value); } } \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/WithAgingColumn.php b/app/Classes/General/Eloquent/Filters/WithAgingColumn.php new file mode 100644 index 00000000..e3908ebc --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/WithAgingColumn.php @@ -0,0 +1,30 @@ +select('packing_lists.*') + ->addSelect(DB::raw("DATEDIFF('$today', transactions.updated_at) as days_over_duedate")) + ->addSelect(DB::raw("CASE + WHEN DATEDIFF('$today', transactions.updated_at) <= 0 THEN 0 + WHEN DATEDIFF('$today', transactions.updated_at) > 0 AND DATEDIFF('$today', transactions.updated_at) <= 30 THEN 1 + WHEN DATEDIFF('$today', transactions.updated_at) > 30 AND DATEDIFF('$today', transactions.updated_at) <= 60 THEN 2 + WHEN DATEDIFF('$today', transactions.updated_at) > 60 AND DATEDIFF('$today', transactions.updated_at) <= 90 THEN 3 + ELSE 4 + END AS due_date_number")); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/WithOrderReferenceLike.php b/app/Classes/General/Eloquent/Filters/WithOrderReferenceLike.php new file mode 100644 index 00000000..acc9f161 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/WithOrderReferenceLike.php @@ -0,0 +1,27 @@ +join('transactions as t2', 't2.payment_reference', '=', 'transactions.bill_no') + ->join('transactions as t3', 't3.id', '=', 't2.owner_id') + ->join('packing_lists', 'packing_lists.id', '=', 't3.owner_id') + ->join('orders', function ($join) use ($value) { + $join->on('orders.id', '=', 'packing_lists.owner_id') + ->where('orders.reference', 'LIKE', '%'.$value.'%'); + }) + ->addSelect(['transactions.*', 't2.id as paymentTransactionId', 't3.id as invoiceTransactionId', 'packing_lists.id as packingListId', 'orders.reference as orderReference']); + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Exports/Services/ExportsAgingList.php b/app/Classes/Modules/Exports/Services/ExportsAgingList.php new file mode 100644 index 00000000..111eb20a --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsAgingList.php @@ -0,0 +1,98 @@ +filters = [ + "has_invoice_status_in" => [2], + "packing_list_ordered_by_invoice_date" => true, + "with_aging_column" => true + ]; + } + + public function headings(): array + { + return [ + 'Company Name', + 'Customer Marking', + 'Order Number', + 'Invoice No', + 'Invoice Date', + 'Days', + 'Amount', + ]; + } + + public function query() + { + return (new ApplyFiltersToQuery())->execute(PackingList::query(), $this->filters); + } + + public function map($list): array + { + $marking = null; + $orderNo = null; + $name = null; + $invDate = 'n/a'; + $invNo = 'n/a'; + $days = 'n/a'; + + if ($list->owner instanceof Order) { + $inviterPivotInviteeReference = $list->owner->companyModule->inviters()->withPivot('invitee_reference')->first(); + + if ($inviterPivotInviteeReference) { + $marking = $inviterPivotInviteeReference->pivot->invitee_reference; + $orderNo = $list->owner->reference; + } + $name = $list->owner->companyModule->company->name; + } + + $transaction = $list->transactions()->whereIn('status', [ApprovalStatus::APPROVED])->first(); + if ($transaction) { + + $invoiceDate = $transaction->created_at; + $invDate = date_format($invoiceDate, 'd-m-Y'); + $invNo = $transaction->bill_no; + $amt = number_format($transaction->amount, 2); + + $currentDate = new DateTime(); + $interval = $currentDate->diff($invoiceDate); + $days = $interval->format('%a'); + } + + return [ + $name, + $marking, + $orderNo, + $invNo, + $invDate, + $days, + $amt, + ]; + } + + private function dueDateColumn($colNum, $dueDateNumber, $amt) + { + if ($colNum == $dueDateNumber) return $amt; + return null; + } +} diff --git a/app/Classes/Modules/Exports/Services/ExportsCustomersWalletTransactionHistory.php b/app/Classes/Modules/Exports/Services/ExportsCustomersWalletTransactionHistory.php new file mode 100644 index 00000000..69571b1f --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsCustomersWalletTransactionHistory.php @@ -0,0 +1,108 @@ +request = $request; + } + + public function headings(): array + { + return [ + 'Date', + 'Description', + 'Incoming', + 'Outgoing', + 'Balance', + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + $wallet = Wallet::find($this->request->route('wallet_id')); + $transactions = $wallet->transactions()->whereIn('transactions.status', [2, 3])->orderBy('id'); + + return $transactions; + } + + /** + * @param Transaction $transaction + * + * @return array + */ + public function map($transaction): array + { + $decimals = $this->request->route('is_precise') == 'true' ? 5 : 2; + + $description = ''; + switch ((int) $transaction->type) { + case TransactionType::TOP_UP: + $description = (float) $transaction->amount . ' Credit Top up'; + break; + case TransactionType::GROUP_PAYMENT: + $description = (float) $transaction->amount . ' Credit Top up'; + break; + case TransactionType::CREDIT_NOTE: + $description = 'Credit Voucher for ' . $transaction->payment_reference; + break; + case TransactionType::PAYMENT: + $booking = Transaction::where('payment_reference', $transaction->bill_no)->first()->owner; + + if (!$booking) { + $description = 'Payment for unknown booking, please contact tech support.'; + break; + } + + $marking = $booking->marking; + $description = 'Payment For booking refs' . $marking; + break; + case TransactionType::DEBIT_NOTE: + $description = 'Debit Voucher for ' . $transaction->payment_reference; + break; + } + + $incoming = $outgoing = ''; + + if (in_array($transaction->type, [TransactionType::TOP_UP, TransactionType::CREDIT_NOTE, TransactionType::GROUP_PAYMENT])) { + $incoming = number_format($transaction->amount, $decimals, '.', ','); + $this->runningBalance += $transaction->amount; + } + + if (in_array($transaction->type, [TransactionType::PAYMENT, TransactionType::DEBIT_NOTE])) { + $outgoing = number_format($transaction->amount, $decimals, '.', ','); + $this->runningBalance -= $transaction->amount; + } + + return [ + Carbon::parse($transaction->created_at)->format('d-m-Y h:i:s A'), + $description, + $incoming, + $outgoing, + number_format($this->runningBalance, $decimals, '.', ',') + ]; + } +} diff --git a/app/Classes/Modules/Exports/Services/ExportsFeedback.php b/app/Classes/Modules/Exports/Services/ExportsFeedback.php index 7bc26265..e83930d4 100644 --- a/app/Classes/Modules/Exports/Services/ExportsFeedback.php +++ b/app/Classes/Modules/Exports/Services/ExportsFeedback.php @@ -20,9 +20,11 @@ class ExportsFeedback implements FromQuery, WithHeadings, WithHeadingRow, WithMa public function headings(): array { return [ + 'Version', 'Question Set', 'Question Text', - 'Answer', + 'Answer Text', + 'Answer Value', 'Source System', 'Source Marking', 'Source Email', @@ -38,7 +40,7 @@ class ExportsFeedback implements FromQuery, WithHeadings, WithHeadingRow, WithMa return QAUserAnswerSelected::whereHas('question', function ($query) { $query->whereHas('questionnaire', function ($innerQuery) { $innerQuery->where('group', 'feedback'); - })->where('created_at', '>', Carbon::now()->subMonths(1)); + }); //->where('created_at', '>', Carbon::now()->subMonths(1)); })->orderBy('created_at', 'desc'); } @@ -56,11 +58,14 @@ class ExportsFeedback implements FromQuery, WithHeadings, WithHeadingRow, WithMa $companyModule = $user->companyModule()->first(); $user_marking = $companyModule ? $companyModule->inviters()->withPivot('invitee_reference')->first()->pivot->invitee_reference : ""; } + $answer = $userAnswer->answer; return [ + $userAnswer->question->questionnaire->version, $userAnswer->question->questionnaire->description, $userAnswer->question->question_text, - $userAnswer->free_text_answer, + $answer->display_text, + $answer->value, $user ? QASystemSourceType::getText(QASystemSourceType::IZYIM) : QASystemSourceType::getText($source->system), $user ? $user_marking : $source->marking, $user ? $user->email : $source->email, diff --git a/app/Classes/Modules/HelpMenu/ControllersLogic/ListQuestionsQALogic.php b/app/Classes/Modules/HelpMenu/ControllersLogic/ListQuestionsQALogic.php index ca25eeb7..dd4e8984 100644 --- a/app/Classes/Modules/HelpMenu/ControllersLogic/ListQuestionsQALogic.php +++ b/app/Classes/Modules/HelpMenu/ControllersLogic/ListQuestionsQALogic.php @@ -59,7 +59,7 @@ class ListQuestionsQALogic extends AbstractControllerLogic $delimiter = "|"; $parts = explode($delimiter, $decriptedToken); $questionSet = $parts[3]; - $query = $this->listsHelpMenuQuestions->execute(['questionnaire_set_id' => $questionSet]); + $query = $this->listsHelpMenuQuestions->execute(['questionnaire_set_id' => $questionSet, 'order_by' => (object)['column' => 'order','DESC' => false]]); return $this->collectionResponse(HelpMenuQuestionResource::collection($query)); } diff --git a/app/Classes/Modules/Orders/Processors/UpdateDoFromYDPortalProcessor.php b/app/Classes/Modules/Orders/Processors/UpdateDoFromYDPortalProcessor.php index efcfd697..2273e10b 100644 --- a/app/Classes/Modules/Orders/Processors/UpdateDoFromYDPortalProcessor.php +++ b/app/Classes/Modules/Orders/Processors/UpdateDoFromYDPortalProcessor.php @@ -72,6 +72,7 @@ class UpdateDoFromYDPortalProcessor } catch (\Exception $exception){ + log::debug($exception); throw new InternalServerErrorException('failed to approve address due to an error related to YD portal'); } } diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreateGroupsLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateGroupsLogic.php index fb21f138..49a59f05 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/CreateGroupsLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateGroupsLogic.php @@ -84,6 +84,25 @@ class CreateGroupsLogic extends AbstractControllerLogic // $invoices = $this->fetchesTransaction->execute(['id_in' => $invoice_ids]); $invoices = Transaction::whereIn('id', $invoice_ids)->get(); + foreach ($invoices as $invoice) { + $order = null; + if ($invoice->owner instanceof Transaction) { + if ($invoice->owner) { + if ($invoice->owner->owner) { + $order = $invoice->owner->owner->owner; + } + } + } else if (!($invoice->owner instanceof Transaction) && !($invoice->owner instanceof Wallet)) { + if ($invoice->owner) { + $order = $invoice->owner->owner; + } + } + + if (!$order) { + throw new MalformedRequestException("There is an error while paying for invoice {$invoice->bill_no}"); + } + } + if ($payment_method == PaymentMethodType::WALLET) { $companyModuleId = $invoices->first()->receiver; diff --git a/app/Classes/Modules/Transactions/ControllersLogic/ListWalletTransactionsLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/ListWalletTransactionsLogic.php new file mode 100644 index 00000000..af51b2e9 --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/ListWalletTransactionsLogic.php @@ -0,0 +1,79 @@ +listsTransactions = $listsTransactions; + } + + /** + * @return array + */ + protected function notification():array { + return [ + 'title' => 'Retrieved Wallet Transactions', + 'message' => 'You have successfully retrieved a list of transactions' + ]; + } + + /** @var ListsTransactions */ + private $listsTransactions; + + public function logic(Request $request) : JsonResponse + { + $query = $this->listsTransactions->execute($this->listsTransactions->deserializeFilters($request->input('filters'))); + + if (str_contains($request->input('filters'), "owner_id") && $query->count() > 0) { + $wallet_total_incoming = Transaction::where('owner_type', $query->first()->owner_type) + ->where('owner_id', $query->first()->owner_id) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereIn('type', [TransactionType::TOP_UP, TransactionType::CREDIT_NOTE, TransactionType::GROUP_PAYMENT]) + ->sum('amount'); + + $wallet_total_outgoing = Transaction::where('owner_type', $query->first()->owner_type) + ->where('owner_id', $query->first()->owner_id) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereIn('type', [TransactionType::PAYMENT, TransactionType::DEBIT_NOTE]) + ->sum('amount'); + + $currentWalletBalance = $wallet_total_incoming - $wallet_total_outgoing; + $incoming = Transaction::where('owner_type', $query->first()->owner_type) + ->where('owner_id', $query->first()->owner_id) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereIn('type', [TransactionType::TOP_UP, TransactionType::CREDIT_NOTE, TransactionType::GROUP_PAYMENT]) + ->where('id', '>', $query->first()->id) + ->sum('amount'); + $outgoing = Transaction::where('owner_type', $query->first()->owner_type) + ->where('owner_id', $query->first()->owner_id) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereIn('type', [TransactionType::PAYMENT, TransactionType::DEBIT_NOTE]) + ->where('id', '>', $query->first()->id) + ->sum('amount'); + $runningBalanceInReverse = $currentWalletBalance - $incoming + $outgoing; + $request['running_balance'] = $runningBalanceInReverse; + } + + return $this->collectionResponse(WalletTransactionResource::collection($query)); + + } + + + +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/RegenerateSingleShippingInvoiceTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/RegenerateSingleShippingInvoiceTransactionLogic.php new file mode 100644 index 00000000..20dba2a0 --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/RegenerateSingleShippingInvoiceTransactionLogic.php @@ -0,0 +1,76 @@ + 'Regenerate Shipping Invoice', + 'message' => 'You have successfully regenerated shipping invoice' + ]; + } + + /** @var CreatesDocument */ + private $createsDocument; + + /** @var CreatesFiles */ + private $createsFiles; + + /** @var FetchesTransaction */ + private $fetchesTransaction; + + /** + * @param CreatesDocument $createsDocument + */ + public function __construct(CreatesDocument $createsDocument, CreatesFiles $createsFiles, FetchesTransaction $fetchesTransaction) + { + $this->createsDocument = $createsDocument; + $this->createsFiles = $createsFiles; + $this->fetchesTransaction = $fetchesTransaction; + } + + public function logic(Request $request) : JsonResponse + { + $invoice = $this->fetchesTransaction->execute(['id' => $request->route('invoice_id')]); + + $invoice->documents()->delete(); + + $transaction_invoice_pdf = LaravelMpdf::loadView('pages.pdfs.shipping_invoice', ['invoice_transaction' => $invoice]); + + $document_object = new DocumentObject( + DocumentType::SHIPPING_INVOICE, + [chunk_split('data:application/pdf;base64,'.base64_encode($transaction_invoice_pdf->output()))], + '', + ApprovalStatus::COMPLETED, + 'shipping_invoice' + ); + + $document =$this->createsDocument->execute($invoice, $document_object); + + $this->createsFiles->execute($document, $document_object); + + // dump($document); + + return $this->response([]); + } + +} diff --git a/app/Http/Controllers/Exports/ExportArrivedParcelController.php b/app/Http/Controllers/Exports/ExportArrivedParcelController.php index 8b396490..f62ea308 100644 --- a/app/Http/Controllers/Exports/ExportArrivedParcelController.php +++ b/app/Http/Controllers/Exports/ExportArrivedParcelController.php @@ -9,6 +9,7 @@ use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Maatwebsite\Excel\Excel; +use App\Classes\Modules\Exports\Services\ExportsAgingList; class ExportArrivedParcelController { @@ -46,4 +47,11 @@ class ExportArrivedParcelController ob_end_clean(); return $response; } + + public function aging(Request $request) { + $data = new ExportsAgingList(); + $response = $data->download('aging_report.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } } diff --git a/app/Http/Controllers/Exports/ExportCustomersWalletTransactionToExcelController.php b/app/Http/Controllers/Exports/ExportCustomersWalletTransactionToExcelController.php new file mode 100644 index 00000000..742c4be1 --- /dev/null +++ b/app/Http/Controllers/Exports/ExportCustomersWalletTransactionToExcelController.php @@ -0,0 +1,36 @@ +headers->set('Authorization', 'Bearer ' . $token); + } + + public function export(Request $request) + { + $exportsTransactions = new ExportsCustomersWalletTransactionHistory($request); + $wallet = Wallet::find($request->route('wallet_id')); + $company_marking = $wallet->owner->connections->first()->invitee_reference; + + $filename = $company_marking . '-wallet-' . ($request->route('is_precise') == 'true' ? 'precise-' : '') . 'transaction-history.xls'; + $response = $exportsTransactions->download($filename, Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } +} diff --git a/app/Http/Controllers/Transactions/ListWalletTransactionsController.php b/app/Http/Controllers/Transactions/ListWalletTransactionsController.php new file mode 100644 index 00000000..e83d7b58 --- /dev/null +++ b/app/Http/Controllers/Transactions/ListWalletTransactionsController.php @@ -0,0 +1,21 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Transactions/RegenerateSingleShippingInvoiceTransactionController.php b/app/Http/Controllers/Transactions/RegenerateSingleShippingInvoiceTransactionController.php new file mode 100644 index 00000000..1607b327 --- /dev/null +++ b/app/Http/Controllers/Transactions/RegenerateSingleShippingInvoiceTransactionController.php @@ -0,0 +1,21 @@ +execute($request); + } +} diff --git a/app/Http/Resources/HelpMenuQuestionResource.php b/app/Http/Resources/HelpMenuQuestionResource.php index e41aeb53..516c3d54 100644 --- a/app/Http/Resources/HelpMenuQuestionResource.php +++ b/app/Http/Resources/HelpMenuQuestionResource.php @@ -34,7 +34,7 @@ class HelpMenuQuestionResource extends JsonResource 'question_number' => $q->question_number, 'question_text' => $q->question_text, 'question_type' => $q->question_type, - 'question_answers' => HelpMenuAnswerOptionsResource::collection(QAAnswerOptions::where('question_number', $q->question_number)->where('questionnaire_set_id', $q->questionnaire_set_id)->get()), + 'question_answers' => HelpMenuAnswerOptionsResource::collection(QAAnswerOptions::where('question_number', $q->question_number)->where('questionnaire_set_id', $q->questionnaire_set_id)->orderBy('order', 'ASC')->get()), 'questionnaire_set_id' => $q->questionnaire_set_id, 'next_nested_question' => $q->next_nested_question, 'next_main_question' => $q->next_main_question, diff --git a/app/Http/Resources/HelpMenuQuestionnaireSetsResource.php b/app/Http/Resources/HelpMenuQuestionnaireSetsResource.php index 7c629a1d..d7143b99 100644 --- a/app/Http/Resources/HelpMenuQuestionnaireSetsResource.php +++ b/app/Http/Resources/HelpMenuQuestionnaireSetsResource.php @@ -18,6 +18,7 @@ class HelpMenuQuestionnaireSetsResource extends JsonResource 'name' => $this->name, 'description' => $this->description, 'group' => $this->group, + 'version' => $this->version, ]; } } diff --git a/app/Http/Resources/HelpMenuQuestionsAnswersResource.php b/app/Http/Resources/HelpMenuQuestionsAnswersResource.php index 98314a16..0d74319a 100644 --- a/app/Http/Resources/HelpMenuQuestionsAnswersResource.php +++ b/app/Http/Resources/HelpMenuQuestionsAnswersResource.php @@ -4,6 +4,7 @@ namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; use App\Classes\ValueObjects\Constants\QASystemSourceType; +use App\Models\QAAnswerOptions; use App\Models\QAQuestions; use Carbon\Carbon; @@ -20,6 +21,7 @@ class HelpMenuQuestionsAnswersResource extends JsonResource $question = QAQuestions::where('id', $this->question_id)->first(); $source = new HelpMenuUserSourceResource($this->userSource); $user = $this->source_id === 0 ? new UserResource($this->user) : null; + $answerOption = QAAnswerOptions::where('id', $this->answer_option_id)->first(); $user_marking = ''; if($user){ @@ -31,7 +33,8 @@ class HelpMenuQuestionsAnswersResource extends JsonResource 'question_id' => $this->question_id, 'questionnaire' => new HelpMenuQuestionnaireSetsResource($this->question->questionnaire), 'question_text' => $question ? $question->question_text : null, - 'free_text_answer' => $this->free_text_answer, + 'free_text_answer' => $answerOption ? $answerOption->display_text : null, + 'answer_value' => $answerOption ? $answerOption->value : null, 'source_system' => $user ? QASystemSourceType::getText(QASystemSourceType::IZYIM) : QASystemSourceType::getText($source->system), 'source_marking' => $user ? $user_marking : $source->marking, 'source_email' => $user ? $user->email : $source->email, diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php index 0da7bccb..df105455 100644 --- a/app/Http/Resources/TransactionResource.php +++ b/app/Http/Resources/TransactionResource.php @@ -27,11 +27,15 @@ class TransactionResource extends JsonResource $packingListReference = null; if ($this->owner instanceof Transaction) { - $order = new OrderResource($this->owner->owner->owner); - $packingListReference = $this->owner->owner->reference; + if ($this->owner) { + if ($this->owner->owner) { + $order = new OrderResource($this->owner->owner->owner); + } + } } else if (!($this->owner instanceof Transaction) && !($this->owner instanceof Wallet)) { - $order = new OrderResource($this->owner->owner); - $packingListReference = $this->owner->reference; + if ($this->owner) { + $order = new OrderResource($this->owner->owner); + } } else { $group = Group::where('reference', $this->payment_reference)->first(); if ($group) { diff --git a/app/Http/Resources/WalletTransactionResource.php b/app/Http/Resources/WalletTransactionResource.php index 0d643d1d..141da749 100644 --- a/app/Http/Resources/WalletTransactionResource.php +++ b/app/Http/Resources/WalletTransactionResource.php @@ -20,28 +20,41 @@ class WalletTransactionResource extends JsonResource public function toArray($request) { $description = ''; + $current_running_balance = $request['running_balance']; switch((int) $this->type){ case TransactionType::TOP_UP: $description = (double) $this->amount.' Credit Top up'; + $request['running_balance'] = bcsub($request['running_balance'], $this->amount, 5); break; case TransactionType::CREDIT_NOTE: $description = 'Credit Voucher for '.$this->payment_reference; + $request['running_balance'] = bcsub($request['running_balance'], $this->amount, 5); break; case TransactionType::PAYMENT: - $order = Transaction::where('payment_reference', $this->bill_no)->first()->owner->owner->owner; + $invoice = Transaction::where('payment_reference', $this->bill_no)->first(); + if(!$invoice) { + Log::channel('paymentUnknownOrderLog')->info('ID: ' . $this->id); + $description = 'Payment for unknown invoice, please contact tech support.'; + break; + } + $order = $invoice->owner->owner->owner; if(!$order) { + Log::channel('paymentUnknownOrderLog')->info('ID: ' . $this->id); $description = 'Payment for unknown order, please contact tech support.'; break; } + $request['running_balance'] = bcadd($request['running_balance'], $this->amount, 5); $marking = $order->reference; $description = 'Payment For order refs.'.''.$marking.''; break; case 11: + $request['running_balance'] = bcadd($request['running_balance'], $this->amount, 5); $description = 'Debit Voucher for '.$this->payment_reference; break; case 15: + $request['running_balance'] = bcsub($request['running_balance'], $this->amount, 5); $description = (double) $this->amount.' Credit Top up'; break; @@ -56,6 +69,7 @@ class WalletTransactionResource extends JsonResource 'payment_method' => (float) $this->payment_method, // 'issuer_name' => $this->issuerCompany->name, 'amount' => (double) $this->amount, + 'running_balance' => (double) $current_running_balance, 'service_charge' => (double) $this->service_charge, 'tax' => (double) $this->tax, 'status' => (int) $this->status, diff --git a/app/Models/QAUserAnswerSelected.php b/app/Models/QAUserAnswerSelected.php index e85296aa..5b4cf2a1 100644 --- a/app/Models/QAUserAnswerSelected.php +++ b/app/Models/QAUserAnswerSelected.php @@ -46,4 +46,13 @@ class QAUserAnswerSelected extends AbstractModel implements Documentable return $this->BelongsTo(QAQuestions::class, 'question_id', 'id'); } + + /** + * @return BelongsTo + */ + public function answer(): BelongsTo + { + return $this->BelongsTo(QAAnswerOptions::class, 'answer_option_id', 'id'); + } + } diff --git a/config/logging.php b/config/logging.php index 1aa06aa3..5868ffb5 100644 --- a/config/logging.php +++ b/config/logging.php @@ -100,6 +100,12 @@ return [ 'emergency' => [ 'path' => storage_path('logs/laravel.log'), ], + + 'paymentUnknownOrderLog' => [ + 'driver' => 'single', + 'path' => storage_path('logs/paymentUnknownOrderLog.log'), + 'level' => 'info', + ], ], ]; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index ed1872de..76038b92 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -26,7 +26,12 @@ class DatabaseSeeder extends Seeder // $this->call(QAQuestionsDemoSeeder::class); //DEMO POC // $this->call(QAAnswerOptionsDemoSeeder::class); //DEMO POC + // 20230928 Set 1 to Set 3 // $this->call(QAQuestionsSeeder::class); // $this->call(QAAnswerOptionsSeeder::class); + + // 20231121 Set 4 to Set 6 + // $this->call(QAQuestions2Seeder::class); + // $this->call(QAAnswerOptions2Seeder::class); } } diff --git a/database/seeders/QAAnswerOptions2Seeder.php b/database/seeders/QAAnswerOptions2Seeder.php new file mode 100644 index 00000000..f9aafe42 --- /dev/null +++ b/database/seeders/QAAnswerOptions2Seeder.php @@ -0,0 +1,395 @@ +display_text = "Very Dissatisfied"; + $answerOption->value = "1"; + $answerOption->order = 1; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 2 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Dissatisfied"; + $answerOption->value = "2"; + $answerOption->order = 2; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 3 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Neutral"; + $answerOption->value = "3"; + $answerOption->order = 3; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 4 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Satisfied"; + $answerOption->value = "4"; + $answerOption->order = 4; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 5 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Very Satisfied"; + $answerOption->value = "5"; + $answerOption->order = 5; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + + + // Set 1 + // Option 1 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Yes"; + $answerOption->value = "3"; + $answerOption->order = 3; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 2 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Partially"; + $answerOption->value = "2"; + $answerOption->order = 2; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 3 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "No"; + $answerOption->value = "1"; + $answerOption->order = 1; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + + + // Set 1 + // Option 1 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Very Clear"; + $answerOption->value = "5"; + $answerOption->order = 5; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 2 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Clear"; + $answerOption->value = "4"; + $answerOption->order = 4; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 3 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Neutral"; + $answerOption->value = "3"; + $answerOption->order = 3; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 4 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Unclear"; + $answerOption->value = "2"; + $answerOption->order = 2; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + // Option 5 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Very Unclear"; + $answerOption->value = "1"; + $answerOption->order = 1; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 4; + $answerOption->save(); + + + + + + // Set 2 + // Option 1 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Yes"; + $answerOption->value = "3"; + $answerOption->order = 3; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + // Option 2 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Somewhat"; + $answerOption->value = "2"; + $answerOption->order = 2; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + // Option 3 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "No"; + $answerOption->value = "1"; + $answerOption->order = 1; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + + + // Set 2 + // Option 1 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Exceeded Expectations"; + $answerOption->value = "3"; + $answerOption->order = 3; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + // Option 2 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Met Expectations"; + $answerOption->value = "2"; + $answerOption->order = 2; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + // Option 3 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Below Expectations"; + $answerOption->value = "1"; + $answerOption->order = 1; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + + + // Set 2 + // Option 1 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Very Dissatisfied"; + $answerOption->value = "1"; + $answerOption->order = 1; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + // Option 2 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Dissatisfied"; + $answerOption->value = "2"; + $answerOption->order = 2; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + // Option 3 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Neutral"; + $answerOption->value = "3"; + $answerOption->order = 3; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + // Option 4 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Satisfied"; + $answerOption->value = "4"; + $answerOption->order = 4; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + // Option 5 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Very Satisfied"; + $answerOption->value = "5"; + $answerOption->order = 5; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 5; + $answerOption->save(); + + + + + + // Set 3 + // Option 1 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Immediately"; + $answerOption->value = "5"; + $answerOption->order = 5; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 2 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Within a few hours"; + $answerOption->value = "4"; + $answerOption->order = 4; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 3 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Within a day"; + $answerOption->value = "3"; + $answerOption->order = 3; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 4 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "More than a day"; + $answerOption->value = "2"; + $answerOption->order = 2; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 5 for Question 10 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Never received a response"; + $answerOption->value = "1"; + $answerOption->order = 1; + $answerOption->question_number = 10; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + + + // Set 3 + // Option 1 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Very Clear"; + $answerOption->value = "5"; + $answerOption->order = 5; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 2 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Clear"; + $answerOption->value = "4"; + $answerOption->order = 4; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 3 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Neutral"; + $answerOption->value = "3"; + $answerOption->order = 3; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 4 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Unclear"; + $answerOption->value = "2"; + $answerOption->order = 2; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 5 for Question 20 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Very Unclear"; + $answerOption->value = "1"; + $answerOption->order = 1; + $answerOption->question_number = 20; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + + + // Set 3 + // Option 1 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Very Likely"; + $answerOption->value = "5"; + $answerOption->order = 5; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 2 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Likely"; + $answerOption->value = "4"; + $answerOption->order = 4; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 3 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Neutral"; + $answerOption->value = "3"; + $answerOption->order = 3; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 4 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Unlikely"; + $answerOption->value = "2"; + $answerOption->order = 2; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + // Option 5 for Question 30 + $answerOption = new QAAnswerOptions; + $answerOption->display_text = "Very Unlikely"; + $answerOption->value = "1"; + $answerOption->order = 1; + $answerOption->question_number = 30; + $answerOption->questionnaire_set_id = 6; + $answerOption->save(); + + + + } +} diff --git a/database/seeders/QAQuestions2Seeder.php b/database/seeders/QAQuestions2Seeder.php new file mode 100644 index 00000000..452eb4d5 --- /dev/null +++ b/database/seeders/QAQuestions2Seeder.php @@ -0,0 +1,148 @@ + 'Set 1', + 'description' => 'Customer Support Satisfaction Survey', + 'group' => 'feedback', + 'version' => 2 + ], + [ + 'name' => 'Set 2', + 'description' => 'First Order Experience Feedback', + 'group' => 'feedback', + 'version' => 2 + ], + [ + 'name' => 'Set 3', + 'description' => 'Sales Inquiry Experience', + 'group' => 'feedback', + 'version' => 2 + ], + ]; + + foreach ($questionnaireSets as $set) { + $questionnaireSet = QAQuestionnaireSet::create([ + 'name' => $set['name'], + 'description' => $set['description'], + 'group' => $set['group'], + 'version' => $set['version'], + ]); + + $questions = []; + + switch ($set['name']) { + case 'Set 1': + $questions = [ + [ + 'question_number' => 10, + 'question_text' => 'How satisfied are you with the time it took to receive a response?', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => true, + 'is_end' => false, + ], + [ + 'question_number' => 20, + 'question_text' => 'Was your issue resolved during this interaction?', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => false, + 'is_end' => false, + ], + [ + 'question_number' => 30, + 'question_text' => 'How clear and understandable was the communication from the support team?', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => false, + 'is_end' => true, + ] + ]; + break; + case 'Set 2': + $questions = [ + [ + 'question_number' => 10, + 'question_text' => 'Did you find what you were looking for without any issues?', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => true, + 'is_end' => false, + ], + [ + 'question_number' => 20, + 'question_text' => 'Did the service meet your expectations?', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => false, + 'is_end' => false, + ], + [ + 'question_number' => 30, + 'question_text' => 'How satisfied are you with the delivery time?', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => false, + 'is_end' => true, + ] + ]; + break; + case 'Set 3': + $questions = [ + [ + 'question_number' => 10, + 'question_text' => 'How quickly did our sales team respond to your inquiry?', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => true, + 'is_end' => false, + ], + [ + 'question_number' => 20, + 'question_text' => 'Was the information provided by our sales team clear and easy to understand?', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => false, + 'is_end' => false, + ], + [ + 'question_number' => 30, + 'question_text' => 'After interacting with our sales team, how likely are you to use our service?', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => false, + 'is_end' => true, + ] + ]; + break; + } + + foreach ($questions as $key => $questionData) { + $question = new QAQuestions; + $question->question_number = $questionData['question_number']; + $question->question_text = $questionData['question_text']; + $question->question_type = $questionData['question_type']; + $question->questionnaire_set_id = $questionnaireSet->id; + + if (isset($questionData['next_nested_question'])) { + $question->next_nested_question = $questionData['next_nested_question']; + } + if (isset($questionData['next_main_question'])) { + $question->next_main_question = $questionData['next_main_question']; + } + + $question->is_start = $questionData['is_start']; + $question->is_end = $questionData['is_end']; + if (isset($questionData['end_text'])) { + $question->end_text = $questionData['end_text']; + } + + $question->order = $key + 1; + $question->save(); + } + } + } +} diff --git a/resources/assets/vue/components/companies/sections/CustomerPaymentBillingSectionComponent.vue b/resources/assets/vue/components/companies/sections/CustomerPaymentBillingSectionComponent.vue index fb69cee3..8e069bcf 100644 --- a/resources/assets/vue/components/companies/sections/CustomerPaymentBillingSectionComponent.vue +++ b/resources/assets/vue/components/companies/sections/CustomerPaymentBillingSectionComponent.vue @@ -1,7 +1,7 @@ \ No newline at end of file + diff --git a/resources/assets/vue/components/feedback/elements/QuestionAnswerComponent.vue b/resources/assets/vue/components/feedback/elements/QuestionAnswerComponent.vue index 1ad87455..252df086 100644 --- a/resources/assets/vue/components/feedback/elements/QuestionAnswerComponent.vue +++ b/resources/assets/vue/components/feedback/elements/QuestionAnswerComponent.vue @@ -1,8 +1,10 @@