diff --git a/app/Classes/Modules/Exports/Services/ExportsSalesInvoiceWithRefundReport.php b/app/Classes/Modules/Exports/Services/ExportsSalesInvoiceWithRefundReport.php new file mode 100644 index 00000000..2809fad9 --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsSalesInvoiceWithRefundReport.php @@ -0,0 +1,275 @@ +startDate = $startDate ? Carbon::parse($startDate)->startOfDay() : Carbon::now()->subMonths(1); + $this->endDate = $endDate ? Carbon::parse($endDate)->endOfDay() : Carbon::now(); + } + + public function headings(): array + { + return [ + 'DocNo', + 'DocDate', + 'DebtorCode', + 'Ref', + 'ShipInfo', + 'AccNo', + 'DetailDescription', + 'FurtherDescription', + 'Classification', + 'DeptNo', + 'Qty', + 'UnitPrice', + 'SubmitEinvoice', + 'ConsolidatedEInvoice', + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + $startDate = $this->startDate; + $endDate = $this->endDate; + + Log::info('ExportsSalesInvoiceWithRefundReport startDate: ' . $startDate->format('Y-m-d H:i:s')); + Log::info('ExportsSalesInvoiceWithRefundReport endDate: ' . $endDate->format('Y-m-d H:i:s')); + + return Booking::whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED, ApprovalStatus::SUSPENDED]) + ->whereHas('transactions', function ($query) use ($startDate, $endDate) { + $query->payments() + ->where('status', ApprovalStatus::REFUNDED) + ->whereBetween('created_at', [$startDate, $endDate]) + ->latest('created_at'); + }); + } + + /** + * @param Booking $booking + * @return array + */ + public function map($booking): array + { + Log::info('ExportsSalesInvoiceWithRefundReport booking : ' . json_encode($booking)); + $records = []; + $averageCurrencyRate = 0; + $currencyId = 0; + + $purchaseOrder = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first(); + $company = $booking->company()->first(); + + // $lastPaymentTransaction = $booking->transactions()->payments()->complete()->latest()->first(); + $lastPaymentTransaction = $booking->transactions()->payments()->where('status', ApprovalStatus::REFUNDED)->latest()->first(); + if(!$lastPaymentTransaction){ + return $records; + } + + $documentDate = $lastPaymentTransaction->created_at; + if ($documentDate < $this->startDate || $documentDate > $this->endDate) { + return $records; + } + + if($company->e_invoice === 1){ + $documentDate = $documentDate->copy()->endOfMonth(); + } + + $invoiceTransaction = $booking->transactions()->where('type', TransactionType::INVOICE)->complete()->first(); + // $invoiceTransaction = $booking->transactions()->where('type', TransactionType::INVOICE)->first(); + + $currencyId = $booking->fix_currency_id; + + $subtotal = 0; + $displayedSubtotal = 0; + $totalPayment = 0; + $averageCurrencyRate = $invoiceTransaction ? $invoiceTransaction->currency_rate : 0; + $paymentSum = $booking->transactions() + ->where('type', TransactionType::PAYMENT) + ->where('status', ApprovalStatus::COMPLETED) + ->get() + ->sum(function ($transaction) { + return round($transaction->amount, 2); + }); + if ($paymentSum){ + $averageCurrencyRate = $booking->transactions() + ->where('type', TransactionType::PAYMENT) + ->where('status', ApprovalStatus::COMPLETED) + ->get() + ->sum(function ($transaction) { + return $transaction->currency_rate; + }) / $booking->transactions() + ->where('type', TransactionType::PAYMENT) + ->where('status', ApprovalStatus::COMPLETED) + ->count(); + + $refundedAmount = (App()->make(CalculatesBookingRefundAmount::class))->execute($booking, 1); + $refundedServiceCharge = (App()->make(CalculatesBookingRefundServiceCharge::class))->execute($booking, 1); + + $totalPayment = $paymentSum - $refundedAmount - $refundedServiceCharge; + } + + $formattedDocumentDate = Carbon::parse($documentDate)->format('m/d/Y'); + + $firstItem = true; + $transactionDetails = $purchaseOrder ? $purchaseOrder->transactionDetails : null; + if($transactionDetails){ + foreach ($transactionDetails as $detail) { + $displayUnitPrice = 0; + if($averageCurrencyRate && $currencyId){ + $exactUnitPrice = ($currencyId) === 1 ? $detail->price : bcdiv($detail->price, $averageCurrencyRate, 7); + $displayUnitPrice = round($exactUnitPrice, 2); + $itemTotal = bcmul($exactUnitPrice, $detail->quantity, 5); + $displayedItemTotal = round(bcmul($displayUnitPrice, $detail->quantity, 7), 2); + $displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2); + $subtotal = bcadd($subtotal, $itemTotal, 5); + } + + + $records[] = [ + $firstItem ? '<>' : '', + $formattedDocumentDate, + $company->debtor, + $booking->marking, + $booking->marking, + '500-0000', + 'PRODUCT NAME :', + $detail->product_name, + '022', + 'C', + $detail->quantity, + $displayUnitPrice ? number_format($displayUnitPrice, 2): 0, + $firstItem ? 'T' : '', + $firstItem ? ($company->e_invoice ? 'F' : 'T') : '' + ]; + + if($firstItem) { + $firstItem = false; + } + } + + // Service Charge - Starts + $serviceCharge = 0; + if (!$totalPayment && $invoiceTransaction) { + $serviceCharge = $invoiceTransaction->service_charge; + } + else { + $serviceCharge = $booking->transactions() + ->where('type', TransactionType::PAYMENT) + ->where('status', ApprovalStatus::COMPLETED) + ->get() + ->sum(function ($transaction) { + return $transaction->service_charge; + }); + } + + $records[] = [ + '', + $formattedDocumentDate, + $company->debtor, + $booking->marking, + $booking->marking, + '500-0000', + 'PRODUCT NAME :', + 'Service Charge', + '022', + 'C', + '1', + $serviceCharge ? number_format($serviceCharge, 2): '0', + '', + '' + ]; + // Service Charge - Ends + + // Adjustment - Starts + if($invoiceTransaction){ + $adjustment = 0; + $voucherRedemption = $invoiceTransaction->voucherRedemption; + $voucherDiscount = $voucherRedemption ? bcmul((string)$voucherRedemption->value, "-1", 2) : "0"; + + $displayedSubtotal = is_numeric($displayedSubtotal) ? sprintf('%F', $displayedSubtotal) : '0'; + $serviceCharge = is_numeric($serviceCharge) ? sprintf('%F', $serviceCharge) : '0'; + $tax = is_numeric($invoiceTransaction->tax) ? sprintf('%F', $invoiceTransaction->tax) : '0'; + $voucherDiscount = is_numeric($voucherDiscount) ? sprintf('%F', $voucherDiscount) : '0'; + + $displayedTotal = bcadd( + bcadd( + bcadd($displayedSubtotal, $serviceCharge, 5), + $tax, + 5 + ), + $voucherDiscount, + 5 + ); + + $expectedTotal = bcadd(bcadd(bcadd($subtotal, $serviceCharge, 5), $invoiceTransaction->tax, 5), $voucherDiscount, 5); + $adjustment = bcsub($expectedTotal, $displayedTotal, 5); + + if ($totalPayment) { + $expectedTotal = $totalPayment; + $adjustment = bcsub($expectedTotal, $displayedTotal, 5); + } + + $records[] = [ + '', + $formattedDocumentDate, + $company->debtor, + $booking->marking, + $booking->marking, + '500-0000', + 'PRODUCT NAME :', + 'Adjustment', + '022', + 'C', + '1', + $adjustment ? number_format($adjustment, 2): '0', + '', + '' + ]; + } + // Adjustment - Ends + } + else{ + Log::info('ExportsSalesInvoiceWithRefundReport EMPTY RECORD for booking marking: ' . $booking->marking); + $records[] = [ + '<>', + $formattedDocumentDate, + $company->debtor, + $booking->marking, + $booking->marking, + '500-0000', + 'PRODUCT NAME :', + 'First Mile Delivery', + '022', + 'C', + '1', + '0', + '', + '' + ]; + } + return $records; + } +} diff --git a/app/Http/Controllers/Exports/ExportController.php b/app/Http/Controllers/Exports/ExportController.php index 89d67322..c1c6b1b5 100644 --- a/app/Http/Controllers/Exports/ExportController.php +++ b/app/Http/Controllers/Exports/ExportController.php @@ -3,11 +3,12 @@ namespace App\Http\Controllers\Exports; -use App\Classes\Modules\Exports\Services\ExportsSalesInvoiceReport; use Illuminate\Http\Request; use Maatwebsite\Excel\Excel; use Illuminate\Support\Facades\Storage; use App\Classes\General\AWSS3Helper; +use App\Classes\Modules\Exports\Services\ExportsSalesInvoiceReport; +use App\Classes\Modules\Exports\Services\ExportsSalesInvoiceWithRefundReport; use App\Classes\Modules\Exports\Services\ExportsARCreditNoteReport; use App\Classes\Modules\Exports\Services\ExportsCompanies; use App\Classes\Modules\Exports\Services\ExportsReceivePaymentDepositEntryReport; @@ -23,6 +24,12 @@ class ExportController return $this->handleExport($exporter, 'Exchange - Sales Invoice Report.xls'); } + public function salesInvoicesWithRefund(Request $request){ + [$startDate, $endDate] = $this->getValidatedDates($request); + $exporter = new ExportsSalesInvoiceWithRefundReport($startDate, $endDate); + return $this->handleExport($exporter, 'Exchange - Sales Invoice Report (with refund).xls'); + } + public function companies(Request $request){ [$startDate, $endDate] = $this->getValidatedDates($request); $exporter = new ExportsCompanies($startDate, $endDate); diff --git a/docker-setup/docker-compose.yml b/docker-setup/docker-compose.yml index 9e33f109..4fd3ec64 100644 --- a/docker-setup/docker-compose.yml +++ b/docker-setup/docker-compose.yml @@ -32,6 +32,7 @@ services: MYSQL_DATABASE: exchange-db MYSQL_USER: master MYSQL_PASSWORD: cDe7gcrRBWetaAP + TZ: Asia/Singapore volumes: - mysql-data:/var/lib/mysql networks: diff --git a/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue b/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue index 5dc427ac..62b859e5 100644 --- a/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue +++ b/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue @@ -180,6 +180,7 @@ export default { options() { return [ 'Sales Invoice Report', + 'Sales Invoice Report (with refund)', 'Customers Report', '01D - RECEIVE PAYMENT (FULL PAYMENT) [AR DEPOSIT ENTRY]', '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]', @@ -195,6 +196,7 @@ export default { const routesMap = { 'Sales Invoice Report': route('api.export.bookings.sales_invoices'), + 'Sales Invoice Report (with refund)': route('api.export.bookings.sales_invoices_w_refund'), 'Customers Report': route('api.export.companies.customers_data'), '01D - RECEIVE PAYMENT (FULL PAYMENT) [AR DEPOSIT ENTRY]': route('api.export.transactions.receive_payment_deposit_entry'), '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]': route('api.export.transactions.receive_payment_for_booking'), diff --git a/routes/export.php b/routes/export.php index b864b3c6..78737423 100644 --- a/routes/export.php +++ b/routes/export.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Route; Route::group(['prefix' => 'export', 'as' => 'export.', 'namespace' => 'Exports'], function () { Route::group(['prefix' => 'bookings', 'as' => 'bookings.'], function () { Route::get('/sales-invoices', [ExportController::class, 'salesInvoices'])->name('sales_invoices'); + Route::get('/sales-invoices-refund', [ExportController::class, 'salesInvoicesWithRefund'])->name('sales_invoices_w_refund'); }); Route::group(['prefix' => 'companies', 'as' => 'companies.'], function () { Route::get('/customers-data', [ExportController::class, 'companies'])->name('customers_data');