diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionPostingEnd.php b/app/Classes/General/Eloquent/Filters/StatementTransactionPostingEnd.php new file mode 100644 index 00000000..e837245e --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionPostingEnd.php @@ -0,0 +1,18 @@ +whereDate('posting_date', '<=', date('Y-m-d',strtotime($value))); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionPostingStart.php b/app/Classes/General/Eloquent/Filters/StatementTransactionPostingStart.php new file mode 100644 index 00000000..c3560357 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionPostingStart.php @@ -0,0 +1,18 @@ +whereDate('posting_date', '>=', date('Y-m-d',strtotime($value))); + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php b/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php index 36fc684a..6f7efe5e 100644 --- a/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php +++ b/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php @@ -28,6 +28,7 @@ class CreateBankStatementTransactionOwnersProcessor // $transactions = StatementTransaction::whereDoesntHave('owners')->where('amount', '<', 0)->get(); foreach ($transactions as $transaction) { + $mapped = false; $keywords = array_filter(explode(" ", $transaction->transaction_description . " " . $transaction->transaction_description_2)); if($transaction->amount > 0){ @@ -36,7 +37,7 @@ class CreateBankStatementTransactionOwnersProcessor $creditTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::PAYMENT, Booking::class, PaymentMethodType::WALLET, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], $keywords); foreach ($creditTransactions as $creditTransaction) { $isArray = is_array($creditTransaction); - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::SALES, 'system' => 'EXCHANGE', 'owner_type' => Transaction::class, @@ -45,12 +46,11 @@ class CreateBankStatementTransactionOwnersProcessor ]); } - // Shipping Portal Sales - $creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date, 1), 2, PaymentMethodType::WALLET); + $creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date, 1), [2], PaymentMethodType::WALLET); foreach ($creditTransactions as $creditTransaction) { if($creditTransaction['owner_type'] === Wallet::class) continue; - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::SALES, 'system' => 'SHIPPING_PORTAL', 'owner_type' => $creditTransaction['owner_type'], @@ -63,7 +63,7 @@ class CreateBankStatementTransactionOwnersProcessor $creditTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::TOP_UP, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], $keywords); foreach ($creditTransactions as $creditTransaction) { $isArray = is_array($creditTransaction); - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::WALLET_TOP_UP, 'system' => 'EXCHANGE', 'owner_type' => Transaction::class, @@ -72,9 +72,9 @@ class CreateBankStatementTransactionOwnersProcessor ]); } - $creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date, 1), 5, null); + $creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date, 1), [5,15], null); foreach ($creditTransactions as $creditTransaction) { - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::WALLET_TOP_UP, 'system' => 'SHIPPING_PORTAL', 'owner_type' => $creditTransaction['owner_type'], @@ -85,7 +85,7 @@ class CreateBankStatementTransactionOwnersProcessor // fpx charge refund if($transaction->transaction_description === 'DUITNOW S/CHRG REFUND'){ - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::FPX_CHARGE_REFUND ]); } @@ -94,7 +94,7 @@ class CreateBankStatementTransactionOwnersProcessor // INTERNAL_BANK_TRANSFER_IN if(str_contains($transaction->transaction_description_2, 'CIEF WORLDWIDE')){ - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_IN ]); } @@ -119,7 +119,7 @@ class CreateBankStatementTransactionOwnersProcessor ->where('amount', '<=', (($transaction->amount * -1) + 0.01))->whereDate('created_at', '>=', $paymentDateStart)->whereDate('created_at', '<=', $paymentDateEnd)->get(); foreach ($debitTransactions as $debitTransaction) { - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::SUPPLIER_PAYMENT, 'system' => 'EXCHANGE', 'owner_type' => Group::class, @@ -135,7 +135,7 @@ class CreateBankStatementTransactionOwnersProcessor $debitTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::DEBIT_NOTE, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], $keywords); foreach ($debitTransactions as $debitTransaction) { $isArray = is_array($creditTransaction); - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::WALLET_WITHDRAWAL, 'system' => 'EXCHANGE', 'owner_type' => Transaction::class, @@ -148,50 +148,52 @@ class CreateBankStatementTransactionOwnersProcessor // STATUTORY if(str_contains($transaction->transaction_description_2, 'PEMBANGUNAN SUMBER') || str_contains($transaction->transaction_description_2, 'HASIL') || str_contains($transaction->transaction_description_2, 'PERTUBUHAN KESELAMAT') || str_contains($transaction->transaction_description_2, 'KUMPULAN WANG SIMPAN')){ - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::STATUTORY ]); } // FPX_CHARGE if($transaction->transaction_description === 'DR DUITNOW S/CHRG' || str_contains($transaction->transaction_description, 'Manual FPX') || str_contains($transaction->transaction_description, 'CMS - DR FPX CHG')){ - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::FPX_CHARGE ]); } // BANK_CHARGE if($transaction->transaction_description === 'CMS - DR CORP CHG' || $transaction->transaction_description === 'MONTHLY PROFIT DEBIT'){ - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::BANK_CHARGE ]); } // CREDIT_CARD_PAYMENT if(str_contains($transaction->transaction_description_2, 'VISA CARD')){ - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::CREDIT_CARD_PAYMENT ]); } // INTERNAL_BANK_TRANSFER_OUT if(str_contains($transaction->transaction_description_2, 'CIEF WORLDWIDE') || str_contains($transaction->transaction_description_2, 'CIEF WORLWIDE') || str_contains($transaction->transaction_description_2, 'IZYIM GLOBAL')){ - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_OUT ]); } // non-operational charges if(str_contains($transaction->transaction_description_2, 'HIRE PURCHASE') || str_contains($transaction->transaction_description_2, 'TENAGA NASIONAL') || str_contains($transaction->transaction_description, 'CABLE CHARGE') || str_contains($transaction->transaction_description_2, 'CTOS DATA SYSTEMS') || str_contains($transaction->transaction_description_2, 'MAXIS')){ - $transaction->owners()->firstOrCreate([ + $data = $transaction->owners()->firstOrCreate([ 'type' => StatementTransactionOwnerType::NON_OPERATIONAL ]); } } + if (isset($data) && $data->wasRecentlyCreated) $mapped = true; + $this->updateMappedRate($transaction, $mapped); } - } + } - private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $keywords, $model = Transaction::class) { + private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $keywords, $model = Transaction::class) { $dateRange = $this->getDateRange($date, 4); if (App::environment(['production'])) { $query = $model::whereIn('status', $statuses) @@ -318,7 +320,7 @@ class CreateBankStatementTransactionOwnersProcessor try{ $client = new \GuzzleHttp\Client(['verify' => false]); - $response = $client->request('GET', $url.'?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"order_by":{"column":"id","DESC":true},"status_in":[2]'.$paymentMethodFilter.',"created_after":"'.$dateRange['start_date'].'","created_before":"'.$dateRange['end_date'].'","amount_exceed":'.($amount - 0.01).',"amount_short":'.($amount + 0.01).',"type_in":['.$type.']}'); + $response = $client->request('GET', $url.'?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"order_by":{"column":"id","DESC":true},"status_in":[2]'.$paymentMethodFilter.',"created_after":"'.$dateRange['start_date'].'","created_before":"'.$dateRange['end_date'].'","amount_exceed":'.($amount - 0.01).',"amount_short":'.($amount + 0.01).',"type_in":'.json_encode($type).'}'); $body = $response->getBody(); $data = json_decode($body, true); $payload = $data['payload']; @@ -345,4 +347,11 @@ class CreateBankStatementTransactionOwnersProcessor 'end_date' => $nextDay, ]; } + + private function updateMappedRate($transaction, $mapped) { + $statement = $transaction->statement; + $statement->total_rows = StatementTransaction::where('account_statement_id',$transaction->account_statement_id)->count(); + $statement->mapped_rows = $mapped ? $statement->mapped_rows+1 : $statement->mapped_rows; + $statement->save(); + } } diff --git a/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php b/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php index 79ca4380..2ecaada8 100644 --- a/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php +++ b/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php @@ -4,7 +4,6 @@ namespace App\Classes\Modules\Exports\Services; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType; -use App\Models\StatementTransactionOwner; use App\Models\Transaction; use Maatwebsite\Excel\Concerns\Exportable; use Maatwebsite\Excel\Concerns\FromQuery; @@ -18,6 +17,8 @@ use App\Classes\Modules\Accounting\Processors\ListShippingPortalTransactions; use App\Classes\ValueObjects\Constants\ShippingTransactionType; use App\Classes\ValueObjects\Constants\TransactionType; use Illuminate\Support\Facades\Log; +use App\Classes\General\Eloquent\ApplyFiltersToQuery; +use App\Models\StatementTransaction; class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize { @@ -64,10 +65,9 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading */ public function query() { - $data = StatementTransactionOwner::whereNull('invoice_reference') - ->whereIn('type', [StatementTransactionOwnerType::SALES, StatementTransactionOwnerType::WALLET_TOP_UP]) - ->whereIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED]); - if ($this->request->has('bankStatementOwnerId')) $data = $data->whereIn('id',json_decode($this->request->input('bankStatementOwnerId'))); + $data = (new ApplyFiltersToQuery())->execute(StatementTransaction::query(), json_decode($this->request->input('filter'), true)); + if ($this->request->has('bankStatementTransactionId')) $data = $data->whereIn('id',json_decode($this->request->input('bankStatementTransactionId'), true)); + return $data; } @@ -78,11 +78,12 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading */ public function map($transaction): array { + $statementTransactionOwner = $transaction->owners()->whereIn('status', [ApprovalStatus::APPROVED])->first(); $logArray = [ 'counter' => $this->counter, - 'system' => $transaction->system, - 'StatementTransactionOwner_id' => $transaction->id, - 'transaction_table_id' => $transaction->owner_id, + 'system' => $statementTransactionOwner->system, + 'StatementTransactionOwner_id' => $statementTransactionOwner->id, + 'transaction_table_id' => $statementTransactionOwner->owner_id, ]; $this->counter += 1; $logArray = json_encode($logArray); @@ -92,8 +93,8 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading $textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' ' . $logArray . PHP_EOL; file_put_contents($filePath, $textToAppend, FILE_APPEND); - if ($transaction->system == 'EXCHANGE') { - $row = (App()->make($transaction->owner_type))->where('id', $transaction->owner_id)->first(); + if ($statementTransactionOwner->system == 'EXCHANGE') { + $row = (App()->make($statementTransactionOwner->owner_type))->where('id', $statementTransactionOwner->owner_id)->first(); $company = $row->type === TransactionType::PAYMENT ? $row->owner->company : $row->owner->owner; $booking = $row->owner; @@ -116,15 +117,15 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading ]; } else { $row = (App()->make(ListShippingPortalTransactions::class))->execute([ - 'id' => $transaction->owner_id, + 'id' => $statementTransactionOwner->owner_id, 'with_company' => true, ]); if (empty($row) || $row[0]['status'] != 'success') { $textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' Fetch Shipping Transaction Fail ' . json_encode([ - 'id' => $transaction->owner_id, + 'id' => $statementTransactionOwner->owner_id, 'with_company' => true, - 'StatementTransactionOwner_id' => $transaction->id, + 'StatementTransactionOwner_id' => $statementTransactionOwner->id, ]) . PHP_EOL; file_put_contents($errorFilePath, $textToAppend, FILE_APPEND); diff --git a/app/Models/AccountStatement.php b/app/Models/AccountStatement.php index d9e53d60..2f5b0e8c 100644 --- a/app/Models/AccountStatement.php +++ b/app/Models/AccountStatement.php @@ -16,6 +16,8 @@ class AccountStatement extends Model 'total_amount', 'begin_balance', 'end_balance', + 'total_rows', + 'mapped_rows', ]; protected $casts = [ diff --git a/database/migrations/2023_10_22_140339_add_mapped_rate_to_account_statements_table.php b/database/migrations/2023_10_22_140339_add_mapped_rate_to_account_statements_table.php new file mode 100644 index 00000000..f6a8e7e7 --- /dev/null +++ b/database/migrations/2023_10_22_140339_add_mapped_rate_to_account_statements_table.php @@ -0,0 +1,34 @@ +integer('total_rows')->unsigned()->default(0)->after('end_balance'); + $table->integer('mapped_rows')->unsigned()->default(0)->after('end_balance'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('account_statements', function (Blueprint $table) { + $table->dropColumn('total_rows'); + $table->dropColumn('mapped_rows'); + }); + } +} diff --git a/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue index 1caef408..b09b9a56 100644 --- a/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue +++ b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue @@ -126,7 +126,7 @@
- +
Pending...
diff --git a/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue index 9d74dce1..4604f2e5 100644 --- a/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue +++ b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue @@ -74,6 +74,32 @@ + + +
+
+
+ + + + +
+
+ + + + +
+
+
+ + Search + +
+
+
+
+
@@ -202,6 +228,10 @@ export default { data(){ return { + parameters: { + startDate: '', + endDate: '', + }, type: null, stage: null, exportStage: 0, @@ -215,6 +245,14 @@ export default { } }, validations: { + parameters: { + startDate: { + required + }, + endDate: { + required + }, + }, files: { // required // todo-new: set required if is pdf section } @@ -227,17 +265,31 @@ export default { this.mappedTrue = true; }, exportInvoiceToAutoCount(){ - window.open(this.route('invoiceTransactions.export')+'?bankStatementOwnerId='+this.getCheckedStatementOwners()+'&type=invoices', '_blank'); + const checkedStatementTransactions = this.getCheckedStatementOwners(); + + let route = this.route('invoiceTransactions.export')+'?type=invoices&filter='+JSON.stringify(this.filter); + if (checkedStatementTransactions) { + route += '&bankStatementTransactionId='+checkedStatementTransactions; + } + + window.open(route, '_blank'); }, exportReceiptToAutoCount(){ - window.open(this.route('invoiceTransactions.export')+'?bankStatementOwnerId='+this.getCheckedStatementOwners()+'&type=receipts', '_blank'); + const checkedStatementTransactions = this.getCheckedStatementOwners(); + + let route = this.route('invoiceTransactions.export')+'?type=receipts&filter='+JSON.stringify(this.filter); + if (checkedStatementTransactions) { + route += '&bankStatementTransactionId='+checkedStatementTransactions; + } + + window.open(route, '_blank'); }, getCheckedStatementOwners() { - let bankStatementOwnerId = []; + let bankStatementTransactionId = []; $('.request_export_item:checked').each(function() { - bankStatementOwnerId.push($(this).val()); + bankStatementTransactionId.push($(this).val()); }); - return JSON.stringify(bankStatementOwnerId); + return (bankStatementTransactionId.length > 0 ? JSON.stringify(bankStatementTransactionId) : null); }, successHandler(){ this.step += 1; @@ -263,6 +315,10 @@ export default { break; case 4: this.filter = {min_amount: 0, is_mapped: true, statement_transaction_owner_type_in: [1, 2], statement_transaction_owner_status_in: [2], per_page: 100, order_by: {column: 'posting_date', DESC: true}} + + if (typeof this.parameters.startDate != 'undefined' && this.parameters.startDate != '') this.filter = {...this.filter, ...{statement_transaction_posting_start: this.parameters.startDate}}; + + if (typeof this.parameters.endDate != 'undefined' && this.parameters.endDate != '') this.filter = {...this.filter, ...{statement_transaction_posting_end:this.parameters.endDate}}; break; } } @@ -280,6 +336,11 @@ export default { break; case 4: this.filter = {max_amount: 0, is_mapped: true, statement_transaction_owner_type_in: [3, 5], statement_transaction_owner_status_in: [2], per_page: 100, order_by: {column: 'posting_date', DESC: true}} + + if (typeof this.parameters.startDate != 'undefined' && this.parameters.startDate != '') this.filter = {...this.filter, ...{statement_transaction_posting_start: this.parameters.startDate}}; + + if (typeof this.parameters.endDate != 'undefined' && this.parameters.endDate != '') this.filter = {...this.filter, ...{statement_transaction_posting_end:this.parameters.endDate}}; + break; } }