From d9d3c48e6dffb3646b111b9f62f57249e714c96d Mon Sep 17 00:00:00 2001 From: Steve Ng Date: Fri, 6 Oct 2023 10:59:36 +0800 Subject: [PATCH] code for statement import invoice mapping --- .../ExportsImportedInvoiceMappeds.php | 78 ++++++++++++ .../ExportCustomersToExcelController.php | 8 ++ .../ImportStatementInvoiceController.php | 120 ++++++++++++++---- app/Models/StatementTransactionOwner.php | 5 + app/Models/Transaction.php | 19 +++ app/Models/TransactionMappingLog.php | 24 ++++ ..._create_transaction_mapping_logs_table.php | 35 +++++ .../ImportedInvoiceMappedComponent.vue | 116 +++++++++++++++++ .../sections/TransactionsMappingComponent.vue | 17 ++- routes/web.php | 1 + 10 files changed, 392 insertions(+), 31 deletions(-) create mode 100644 app/Classes/Modules/Exports/Services/ExportsImportedInvoiceMappeds.php create mode 100644 app/Models/TransactionMappingLog.php create mode 100644 database/migrations/2023_10_04_212224_create_transaction_mapping_logs_table.php create mode 100644 resources/assets/vue/components/accounting/sections/ImportedInvoiceMappedComponent.vue diff --git a/app/Classes/Modules/Exports/Services/ExportsImportedInvoiceMappeds.php b/app/Classes/Modules/Exports/Services/ExportsImportedInvoiceMappeds.php new file mode 100644 index 00000000..71363b52 --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsImportedInvoiceMappeds.php @@ -0,0 +1,78 @@ +dateTime = $request->input('date').' '.$request->input('time'); + $this->count = 0; + } + + public function headings(): array + { + return [ + 'No', + 'Doc No', + 'Date', + 'Debtor Code', + 'Debtor Name', + 'Shipping Info', + 'Net Total', + 'Cancelled', + 'Mapped Status', + 'Mapped Reference No' + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + return TransactionMappingLog::where('imported_date', $this->dateTime); + } + + /** + * @param Transaction $transaction + * + * @return array + */ + public function map($transaction): array + { + // dd($transaction); + $this->count += 1; + $data = $transaction->data; + return [ + $this->count, + Arr::get($data,'doc_no'), + Arr::get($data,'date'), + Arr::get($data,'debtor_code'), + Arr::get($data,'debtor_name'), + Arr::get($data,'shipping_info'), + Arr::get($data,'net_total'), + Arr::get($data,'cancelled'), + Arr::get($data,'mapped_status'), + Arr::get($data,'mapped_result_reference'), + ]; + + } +} diff --git a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php index 74384157..eebfeed7 100644 --- a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php +++ b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php @@ -15,6 +15,8 @@ use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Maatwebsite\Excel\Excel; +use App\Classes\Modules\Exports\Services\ExportsImportedInvoiceMappeds; +use App\Models\TransactionMappingLog; class ExportCustomersToExcelController { @@ -75,4 +77,10 @@ class ExportCustomersToExcelController ob_end_clean(); return $response; } + + public function importedInvoiceMapped(ExportsImportedInvoiceMappeds $exportsImportedInvoiceMappeds, Request $request) { + $response = $exportsImportedInvoiceMappeds->download('InvoiceMapped.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } } \ No newline at end of file diff --git a/app/Http/Controllers/Imports/ImportStatementInvoiceController.php b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php index a59f9463..2535f870 100644 --- a/app/Http/Controllers/Imports/ImportStatementInvoiceController.php +++ b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php @@ -2,33 +2,47 @@ namespace App\Http\Controllers\Imports; -use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; -use App\Classes\Modules\Imports\Services\GenericImport; -use App\Classes\Modules\Segments\DataTransferObjects\SeasonalSegmentObject; -use App\Classes\ValueObjects\Constants\ApprovalStatus; -use App\Models\Segment; -use App\Models\User; -use Carbon\Carbon; use DateTime; +use Carbon\Carbon; +use App\Models\User; +use App\Models\Company; +use App\Models\Segment; +use App\Models\Transaction; use Illuminate\Http\Request; +use App\Models\SeasonalSegment; +use Illuminate\Http\JsonResponse; +use Illuminate\Support\Facades\DB; use Maatwebsite\Excel\Facades\Excel; +use App\Models\TransactionMappingLog; +use App\Classes\ValueObjects\Constants\HttpStatus; +use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\Modules\Imports\Services\GenericImport; +use App\Classes\ValueObjects\Response\ApiResponseObject; +use App\Classes\Modules\Accounting\Processors\ChecksBillNumber; use App\Classes\Modules\Segments\Services\CreatesSeasonalSegment; use App\Classes\Modules\Companies\Processors\AssignSegmentProcessor; -use App\Models\Company; -use App\Models\SeasonalSegment; -use App\Models\Transaction; +use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; +use App\Classes\Modules\Segments\DataTransferObjects\SeasonalSegmentObject; class ImportStatementInvoiceController { + private $responseTitle; + private $responseMessage; + + public function __construct() { + $this->responseTitle = 'Import Invoice Mapping'; + $this->responseMessage = 'You have successfully imported invoice mapping'; + } + /** * @param Request $request * @return array * @throws \App\Classes\Exceptions\MalformedRequestException */ - public function import(Request $request) + public function import(Request $request) : JsonResponse { ini_set('memory_limit', '-1'); - + $importDate = date('Y-m-d H:i:s'); $object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports'); $file = json_decode($object->getFiles()[0])->file_info->original->file; @@ -37,26 +51,37 @@ class ImportStatementInvoiceController $excelRows = $import->rows; $excelRows = $excelRows->toArray(); + $data = []; foreach ($excelRows as $row) { - dd($row); - // $row['debtor_code'] - - // attempt 1 - try map by amount and date - // $transactionDate = $this->changeExcelDate($row['date']); - // $transaction = Transaction::where('original_amount', $row['total'])->whereDate('created_at', $transactionDate)->get(); - // if ($transaction) { - // // check company - // // $company = Company::where('debtor', $row['debtor_code'])->first(); - // // dd($company); - // // try to verify is it the correct transaction - // } + $row['mapped_result_reference'] = null; + $row['mapped_status'] = 'failed'; + $row['date'] = $this->changeExcelDate($row['date']); // Shipping Info // TOPUP -> map with transaction.bill_no if (str_starts_with($row['shipping_info'], 'TOPUP')) { // find in exchange first, if cannont then find in izyim - // (App()->make(ChecksBillNumber::class))->execute($bill_no, 'exchange'); + foreach (['exchange','izyim'] as $system) { + $returnReference = $this->mappingTopUp($row, $system); + if ($returnReference) { + $row['mapped_result_reference'] = $returnReference; + $row['mapped_status'] = 'success'; + } + } + } else { + $returnReference = $this->mappingExchange($row); + if ($returnReference) { + $row['mapped_result_reference'] = $returnReference; + $row['mapped_status'] = 'success'; + } } + + TransactionMappingLog::create([ + 'imported_date'=>$importDate, + 'data'=>$row, + ]); + array_push($data, $row); + // if 5 digits -> exchange booking reference // find transation @@ -88,6 +113,51 @@ class ImportStatementInvoiceController } + + return $this->response(['data'=>$data,'importedDate'=>$importDate]); + } + + public function response(?array $data = []) : JsonResponse { + return (new ApiResponseObject($this->responseTitle, + $this->responseMessage, + HttpStatus::OK_WITH_MESSAGE, $data))->handler(); + } + + private function mappingTopUp(Array $row, String $system) { + try { + if ($data = (App()->make(ChecksBillNumber::class))->execute($row['shipping_info'], $system)) { + if ($system == 'izyim' && isset($data['owner_reference'])) return $data['owner_reference']; + + if ($system == 'exchange') return $this->updateTransactionOwnerReference($data, $row['doc_no']); + } + } catch (\Throwable $th) { + return false; + } + } + + private function mappingExchange(Array $row) { + $date = $row['date']; + $transactions = Transaction::where('original_amount', $row['net_total'])->whereRaw("DATE(created_at) = '$date'") + ->whereHas('receiverCompany', function($q) use($row) { + $q->where('debtor',$row['debtor_code']); + }) + ->get(); + + if ($transactions && $transactions->count() == 1) { + foreach ($transactions as $key => $transaction) { + return $this->updateTransactionOwnerReference($transaction, $row['doc_no']); + } + } + return false; + } + + public function updateTransactionOwnerReference($transaction, String $docNo) { + $transactionOwner = $transaction->transaction_owner; + if ($transactionOwner) { + $transactionOwner->update(['invoice_reference'=>$docNo]); + return $transactionOwner->owner_reference; + } + return false; } public function changeExcelDate($date) diff --git a/app/Models/StatementTransactionOwner.php b/app/Models/StatementTransactionOwner.php index 5bfe6f82..fb13f4b4 100644 --- a/app/Models/StatementTransactionOwner.php +++ b/app/Models/StatementTransactionOwner.php @@ -21,6 +21,11 @@ class StatementTransactionOwner extends Model 'receipt_reference', 'status', ]; + + public function owner(): morphTo + { + return $this->morphTo(); + } public function transaction(): BelongsTo { diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 05fbfde7..631a5fa5 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -18,6 +18,7 @@ use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphTo; use Staudenmeir\EloquentHasManyDeep\HasTableAlias; +use App\Models\StatementTransactionOwner; class Transaction extends AbstractModel implements Documentable, Transactionable, Voucherifiable @@ -53,6 +54,14 @@ class Transaction extends AbstractModel implements Documentable, Transactionable return $this->MorphOne(Transaction::class, 'owner')->where('type', TransactionType::CREDIT_NOTE); } + /** + * @return \Illuminate\Database\Eloquent\Relations\MorphOne + */ + public function transaction_owner() + { + return $this->MorphOne(StatementTransactionOwner::class, 'owner','owner_type','owner_id'); + } + /** * @return BelongsTo */ @@ -69,6 +78,16 @@ class Transaction extends AbstractModel implements Documentable, Transactionable return $this->BelongsTo( Company::class, 'issuer', 'id'); } + /** + * Get the user that owns the Transaction + * + * @return BelongsTo + */ + public function receiverCompany(): BelongsTo + { + return $this->belongsTo(Company::class, 'receiver', 'id'); + } + /** * @return BelongsTo */ diff --git a/app/Models/TransactionMappingLog.php b/app/Models/TransactionMappingLog.php new file mode 100644 index 00000000..d6093f39 --- /dev/null +++ b/app/Models/TransactionMappingLog.php @@ -0,0 +1,24 @@ + 'array', + ]; + + public static function boot() { + parent::boot(); + + static::creating(function ($model) { + $model->imported_by = auth()->user()->id; + }); + } + +} diff --git a/database/migrations/2023_10_04_212224_create_transaction_mapping_logs_table.php b/database/migrations/2023_10_04_212224_create_transaction_mapping_logs_table.php new file mode 100644 index 00000000..8989d8ae --- /dev/null +++ b/database/migrations/2023_10_04_212224_create_transaction_mapping_logs_table.php @@ -0,0 +1,35 @@ +id(); + $table->bigInteger('imported_by')->unsigned(); + $table->foreign('imported_by')->references('id')->on('users'); + $table->dateTime('imported_date')->nullable(); + $table->text('data')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('transaction_mapping_logs'); + } +} diff --git a/resources/assets/vue/components/accounting/sections/ImportedInvoiceMappedComponent.vue b/resources/assets/vue/components/accounting/sections/ImportedInvoiceMappedComponent.vue new file mode 100644 index 00000000..a8df27f7 --- /dev/null +++ b/resources/assets/vue/components/accounting/sections/ImportedInvoiceMappedComponent.vue @@ -0,0 +1,116 @@ + + + \ No newline at end of file diff --git a/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue index 047a9bd5..abebcb83 100644 --- a/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue +++ b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue @@ -99,7 +99,7 @@ -
+
@@ -123,7 +123,7 @@
-
Import Invoices
+
Import Invoices

Nest Step

@@ -131,6 +131,13 @@ Learn How to do this step?
+
+
+ + + +
+
@@ -191,6 +198,7 @@ export default { files: [], parameters: {}, section: 'bankTransactionSection', + mappedTrue: false, } }, validations: { @@ -200,10 +208,7 @@ export default { }, methods: { importInvoice(){ - this.parameters = { - files: this.files - }; - this.submit(this.route('api.import_invoices.upload'), 'post', this.section, true, false); + this.mappedTrue = true; }, importReceipts(){ this.parameters = { diff --git a/routes/web.php b/routes/web.php index 612fa976..1dece5c3 100644 --- a/routes/web.php +++ b/routes/web.php @@ -261,6 +261,7 @@ Route::get('/export/payment-transactions/f614e339d7058904a831aad742e24d55', 'Exp Route::get('/export/wallet-transactions/f614e339d7058904a831aad742e24d55', 'Exports\ExportCustomersToExcelController@walletTransactions')->name('walletTransactions.export'); Route::get('/export/booking-transactions', 'Exports\ExportCustomersToExcelController@bookingTransactions')->name('export.transactions.booking'); Route::get('/export/invoice-transactions/f614e339d7058904a831aad742e24d55', 'Exports\ExportCustomersToExcelController@invoiceTransactions')->name('invoiceTransactions.export'); +Route::get('/export/imported-invoice-mapped', 'Exports\ExportCustomersToExcelController@importedInvoiceMapped')->name('importedInvoiceMapped.export'); Route::get('/products', function (\App\Classes\Modules\Exports\Services\ExportsProducts $exportsProducts) { $bookings = Booking::where(function($query){