mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/exchange-2.0.git
synced 2026-08-19 04:23:55 +00:00
1688 PO Automation Project
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\OpenAI\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\Classes\Exceptions\MalformedRequestException;
|
||||
use App\Classes\Exceptions\ConnectionErrorException;
|
||||
|
||||
class CreatesChatGPTResponseWithFiles
|
||||
{
|
||||
public function execute(string $userPrompt, array $fileIds)
|
||||
{
|
||||
try {
|
||||
$fileContents = [];
|
||||
foreach ($fileIds as $fileId) {
|
||||
$fileContents[] = [
|
||||
'type' => 'input_file',
|
||||
'file_id' => $fileId,
|
||||
];
|
||||
}
|
||||
|
||||
$inputs = array_merge([
|
||||
[
|
||||
'type' => 'input_text',
|
||||
'text' => $userPrompt,
|
||||
]
|
||||
], $fileContents);
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . config('openai.api_key'),
|
||||
'Content-Type' => 'application/json',
|
||||
])->post(config('openai.base_url') . '/v1/responses', [
|
||||
'model' => 'gpt-4.1-mini',
|
||||
'input' => [
|
||||
[
|
||||
'role' => 'user',
|
||||
'content' => $inputs,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
return $response->json();
|
||||
}
|
||||
catch (\Illuminate\Http\Client\ConnectionException $exception) {
|
||||
$error = 'Failed to connect to ChatGPT API';
|
||||
throw new ConnectionErrorException($error, $exception->getMessage(), $userPrompt, $exception->getTraceAsString());
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
throw new MalformedRequestException('Unable to get correct response from ChatGPT API: ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\OpenAI\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Classes\Exceptions\MalformedRequestException;
|
||||
use App\Classes\Exceptions\ConnectionErrorException;
|
||||
|
||||
class UploadsOpenAIFiles
|
||||
{
|
||||
public function execute(array $paths): array
|
||||
{
|
||||
try {
|
||||
$ids = [];
|
||||
foreach ($paths as $path) {
|
||||
$response = Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . config('openai.api_key'),
|
||||
])->attach(
|
||||
'file',
|
||||
fopen($path, 'r'),
|
||||
basename($path)
|
||||
)->post(config('openai.base_url') . '/v1/files', [
|
||||
'purpose' => 'user_data',
|
||||
]);
|
||||
|
||||
$data = $response->json();
|
||||
|
||||
if ($response->successful() && isset($data['id'])) {
|
||||
$ids[] = $data['id'];
|
||||
} else {
|
||||
Log::error('File upload failed', [
|
||||
'path' => $path,
|
||||
'response' => $data
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
catch (\Illuminate\Http\Client\ConnectionException $exception) {
|
||||
$error = 'Failed to connect to ChatGPT API';
|
||||
throw new ConnectionErrorException($error, $exception->getMessage(), $exception->getTraceAsString());
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
throw new MalformedRequestException('Unable to get correct response from ChatGPT API: ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-16
@@ -5,11 +5,12 @@ namespace App\Classes\Modules\Transactions\ControllersLogic;
|
||||
|
||||
use App\Classes\Exceptions\InternalServerErrorException;
|
||||
use App\Classes\General\Abstracts\AbstractControllerLogic;
|
||||
use App\Classes\Modules\Transactions\Processors\ETLPurchaseOrderTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Processors\ETLPDFPurchaseOrderTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Standards\Rules\CanETLPurchaseOrder;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ETLPurchaseOrderTransactionLogic extends AbstractControllerLogic
|
||||
{
|
||||
@@ -28,18 +29,18 @@ class ETLPurchaseOrderTransactionLogic extends AbstractControllerLogic
|
||||
/** @var CanETLPurchaseOrder */
|
||||
private $canETLPurchaseOrder;
|
||||
|
||||
/** @var ETLPurchaseOrderTransactionProcessor */
|
||||
private $etlPurchaseOrderTransactionProcessor;
|
||||
/** @var ETLPDFPurchaseOrderTransactionProcessor */
|
||||
private $etlPDFPurchaseOrderTransactionProcessor;
|
||||
|
||||
/**
|
||||
* ETLPurchaseOrderTransactionLogic constructor.
|
||||
* @param CanETLPurchaseOrder $canETLPurchaseOrder
|
||||
* @param ETLPurchaseOrderTransactionProcessor $etlPurchaseOrderTransactionProcessor
|
||||
* @param ETLPDFPurchaseOrderTransactionProcessor $etlPDFPurchaseOrderTransactionProcessor
|
||||
*/
|
||||
public function __construct(CanETLPurchaseOrder $canETLPurchaseOrder, ETLPurchaseOrderTransactionProcessor $etlPurchaseOrderTransactionProcessor)
|
||||
public function __construct(CanETLPurchaseOrder $canETLPurchaseOrder, ETLPDFPurchaseOrderTransactionProcessor $etlPDFPurchaseOrderTransactionProcessor)
|
||||
{
|
||||
$this->canETLPurchaseOrder = $canETLPurchaseOrder;
|
||||
$this->etlPurchaseOrderTransactionProcessor = $etlPurchaseOrderTransactionProcessor;
|
||||
$this->etlPDFPurchaseOrderTransactionProcessor = $etlPDFPurchaseOrderTransactionProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,17 +54,19 @@ class ETLPurchaseOrderTransactionLogic extends AbstractControllerLogic
|
||||
{
|
||||
$this->canETLPurchaseOrder->passes();
|
||||
|
||||
if ($request->has('products') && is_array($request->input('products'))) {
|
||||
$products = $request->input('products');
|
||||
$productsMetadata = $request->input('productsMetadata');
|
||||
}
|
||||
// if ($request->has('products') && is_array($request->input('products'))) {
|
||||
// $products = $request->input('products');
|
||||
// $productsMetadata = $request->input('productsMetadata');
|
||||
// }
|
||||
|
||||
try{
|
||||
$products = $this->etlPurchaseOrderTransactionProcessor->execute($products, $productsMetadata);
|
||||
} catch (\Exception $exception) {
|
||||
throw new InternalServerErrorException('Something went wrong!');
|
||||
}
|
||||
// try{
|
||||
// $products = $this->etlPDFPurchaseOrderTransactionProcessor->execute($products, $productsMetadata);
|
||||
// } catch (\Exception $exception) {
|
||||
// throw new InternalServerErrorException('Something went wrong!');
|
||||
// }
|
||||
|
||||
return $this->response($products);
|
||||
$files = $request->input('files');
|
||||
$result = $this->etlPDFPurchaseOrderTransactionProcessor->execute($files);
|
||||
return $this->response($result);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-22
@@ -9,7 +9,7 @@ use App\Classes\Modules\Bookings\Services\FetchesBooking;
|
||||
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
|
||||
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
|
||||
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Processors\ETLPurchaseOrderTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Processors\ETLPDFPurchaseOrderTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
|
||||
use App\Classes\Modules\Transactions\Standards\Rules\CanETLPurchaseOrder;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
@@ -47,8 +47,8 @@ class ImportPurchaseOrderTransactionLogic extends AbstractControllerLogic
|
||||
/** @var CreatePurchaseOrderTransactionProcessor */
|
||||
private $createPurchaseOrderTransactionProcessor;
|
||||
|
||||
/** @var ETLPurchaseOrderTransactionProcessor */
|
||||
private $etlPurchaseOrderTransactionProcessor;
|
||||
/** @var ETLPDFPurchaseOrderTransactionProcessor */
|
||||
private $etlPDFPurchaseOrderTransactionProcessor;
|
||||
|
||||
/** @var CanETLPurchaseOrder */
|
||||
private $canETLPurchaseOrder;
|
||||
@@ -58,15 +58,15 @@ class ImportPurchaseOrderTransactionLogic extends AbstractControllerLogic
|
||||
* @param FetchesBooking $fetchesBooking
|
||||
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
|
||||
* @param CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor
|
||||
* @param ETLPurchaseOrderTransactionProcessor $etlPurchaseOrderTransactionProcessor
|
||||
* @param ETLPDFPurchaseOrderTransactionProcessor $etlPDFPurchaseOrderTransactionProcessor
|
||||
* @param CanETLPurchaseOrder $canETLPurchaseOrder
|
||||
*/
|
||||
public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor, ETLPurchaseOrderTransactionProcessor $etlPurchaseOrderTransactionProcessor, CanETLPurchaseOrder $canETLPurchaseOrder)
|
||||
public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor, ETLPDFPurchaseOrderTransactionProcessor $etlPDFPurchaseOrderTransactionProcessor, CanETLPurchaseOrder $canETLPurchaseOrder)
|
||||
{
|
||||
$this->fetchesBooking = $fetchesBooking;
|
||||
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
|
||||
$this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
|
||||
$this->etlPurchaseOrderTransactionProcessor = $etlPurchaseOrderTransactionProcessor;
|
||||
$this->etlPDFPurchaseOrderTransactionProcessor = $etlPDFPurchaseOrderTransactionProcessor;
|
||||
$this->canETLPurchaseOrder = $canETLPurchaseOrder;
|
||||
}
|
||||
|
||||
@@ -83,24 +83,13 @@ class ImportPurchaseOrderTransactionLogic extends AbstractControllerLogic
|
||||
$this->canETLPurchaseOrder->passes();
|
||||
|
||||
$products = $request->input('products');
|
||||
$productsMetadata = $request->input('productsMetadata');
|
||||
|
||||
$count = collect($products)
|
||||
->filter(function ($product) {
|
||||
return isset($product['description']) &&
|
||||
preg_match('/\p{Han}+/u', $product['description']);
|
||||
})
|
||||
->count();
|
||||
|
||||
if($count > 10){
|
||||
ETLPurchaseOrderTransactionV2CommandJob::dispatch($products, $productsMetadata, $request->route('id'));
|
||||
$this->notificationMessage = "Please refresh page in a few minutes";
|
||||
return $this->response([]);
|
||||
if(empty($products)){
|
||||
$files = $request->input('files');
|
||||
$products = $this->etlPDFPurchaseOrderTransactionProcessor->execute($files);
|
||||
}
|
||||
else{
|
||||
$products = $this->etlPurchaseOrderTransactionProcessor->execute($products, $productsMetadata);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports');
|
||||
foreach ($object->getFiles() as $file) {
|
||||
$collection = Excel::toCollection(null, json_decode($file)->file_info->original->file, null, null, true);
|
||||
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Transactions\Processors;
|
||||
|
||||
use App\Classes\Modules\OpenAI\Services\UploadsOpenAIFiles;
|
||||
use App\Classes\Modules\OpenAI\Services\CreatesChatGPTResponseWithFiles;
|
||||
use App\Classes\Exceptions\ResourceNotFoundException;
|
||||
use App\Classes\Modules\Bookings\Services\FetchesBooking;
|
||||
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
|
||||
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
|
||||
use App\Classes\ValueObjects\Constants\KVPKey;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\PaymentMethodType;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Models\KeyValuePair;
|
||||
use App\Models\Booking;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ETLPDFPurchaseOrderTransactionProcessor
|
||||
{
|
||||
/** @var FetchesBooking */
|
||||
private $fetchesBooking;
|
||||
|
||||
/** @var GeneratesTransactionBillNumber */
|
||||
private $generatesTransactionBillNumber;
|
||||
|
||||
/** @var CreatePurchaseOrderTransactionProcessor */
|
||||
private $createPurchaseOrderTransactionProcessor;
|
||||
|
||||
/** @var UploadsOpenAIFiles */
|
||||
private $uploadsOpenAIFiles;
|
||||
|
||||
/** @var CreatesChatGPTResponseWithFiles */
|
||||
private $createsChatGPTResponseWithFiles;
|
||||
|
||||
/**
|
||||
* ETLPDFPurchaseOrderTransactionProcessor constructor.
|
||||
* @param FetchesBooking $fetchesBooking
|
||||
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
|
||||
* @param CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor
|
||||
* @param UploadsOpenAIFiles $uploadsOpenAIFiles
|
||||
* @param CreatesChatGPTResponseWithFiles $createsChatGPTResponseWithFiles
|
||||
*/
|
||||
public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor, UploadsOpenAIFiles $uploadsOpenAIFiles,
|
||||
CreatesChatGPTResponseWithFiles $createsChatGPTResponseWithFiles)
|
||||
{
|
||||
$this->fetchesBooking = $fetchesBooking;
|
||||
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
|
||||
$this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
|
||||
$this->uploadsOpenAIFiles = $uploadsOpenAIFiles;
|
||||
$this->createsChatGPTResponseWithFiles = $createsChatGPTResponseWithFiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param UploadedFile[] $files
|
||||
* @param int $bookingId
|
||||
* @return array|null
|
||||
*/
|
||||
public function execute(array $files, ?int $bookingId = null): ?array
|
||||
{
|
||||
// Ensure temp directory exists
|
||||
$tempDir = storage_path('app/documents');
|
||||
if (!file_exists($tempDir)) {
|
||||
mkdir($tempDir, 0755, true);
|
||||
}
|
||||
|
||||
$tempPaths = [];
|
||||
|
||||
try {
|
||||
foreach ($files as $index => $fileBase64) {
|
||||
// Strip prefix if present
|
||||
if (preg_match('/^data:application\/pdf;base64,/', $fileBase64)) {
|
||||
$fileBase64 = preg_replace('/^data:application\/pdf;base64,/', '', $fileBase64);
|
||||
}
|
||||
|
||||
// Decode Base64
|
||||
$fileContent = base64_decode($fileBase64);
|
||||
if ($fileContent === false) {
|
||||
Log::warning("Invalid Base64 for file index $index");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure temp directory exists
|
||||
$tempDir = storage_path('app/documents');
|
||||
if (!file_exists($tempDir)) {
|
||||
mkdir($tempDir, 0755, true);
|
||||
}
|
||||
|
||||
// Generate unique temp file path
|
||||
$fileName = uniqid("file_{$index}_") . '.pdf';
|
||||
$filePath = $tempDir . '/' . $fileName;
|
||||
|
||||
// Save file to disk
|
||||
file_put_contents($filePath, $fileContent);
|
||||
|
||||
// Track temp path
|
||||
$tempPaths[] = $filePath;
|
||||
}
|
||||
|
||||
Log::info('tempPaths: ' . json_encode($tempPaths));
|
||||
|
||||
// Upload to OpenAI
|
||||
$fileIds = $this->uploadsOpenAIFiles->execute($tempPaths);
|
||||
|
||||
$kvp = KeyValuePair::where(
|
||||
'key',
|
||||
KVPKey::CHATGPT_PROMPT_PREFIX . 'EXTRACT_1'
|
||||
)->first();
|
||||
|
||||
if (! $kvp) {
|
||||
throw new ResourceNotFoundException('AI Setup incomplete!');
|
||||
}
|
||||
|
||||
$userPrompt = $kvp->value;
|
||||
$response = $this->createsChatGPTResponseWithFiles->execute($userPrompt, $fileIds);
|
||||
|
||||
// Extract the text content from the first output message
|
||||
$textContent = $response['output'][0]['content'][0]['text'] ?? '[]';
|
||||
|
||||
// Remove ```json fences if present
|
||||
if (preg_match('/```json\s*(.*?)\s*```/s', $textContent, $matches)) {
|
||||
$jsonString = $matches[1];
|
||||
} else {
|
||||
$jsonString = $textContent;
|
||||
}
|
||||
|
||||
// Decode JSON
|
||||
$data = json_decode($jsonString, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
Log::error('Failed to decode OpenAI JSON response', [
|
||||
'error' => json_last_error_msg(),
|
||||
'textContent' => $textContent
|
||||
]);
|
||||
$data = [];
|
||||
}
|
||||
|
||||
// Optional: convert keys to snake_case
|
||||
$data = array_map(function($item) {
|
||||
$newItem = [];
|
||||
foreach ($item as $key => $value) {
|
||||
$newKey = strtolower(str_replace(' ', '_', $key));
|
||||
$newItem[$newKey] = $value;
|
||||
}
|
||||
return $newItem;
|
||||
}, $data);
|
||||
|
||||
if($bookingId){
|
||||
/** @var Booking $booking */
|
||||
$booking = $this->fetchesBooking->execute(['id' => $bookingId]);
|
||||
|
||||
$billNumber = $this->generatesTransactionBillNumber->execute('PO-');
|
||||
|
||||
$total = collect($data)->sum(function($product){
|
||||
return $product['quantity'] * floatval(str_replace(',', '', $product['unit_price']));
|
||||
});
|
||||
|
||||
$object = new TransactionObject($billNumber, TransactionType::PURCHASE_ORDER, $booking->company->id, 1,
|
||||
1, PaymentMethodType::CASH,
|
||||
$total, $total, $booking->fix_currency_id, $booking->fix_currency_id,
|
||||
1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, $data);
|
||||
|
||||
|
||||
$transaction = $this->createPurchaseOrderTransactionProcessor->execute($booking, $object);
|
||||
}
|
||||
return $data;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error processing PDF files with ChatGPT', [
|
||||
'message' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
throw $e;
|
||||
} finally {
|
||||
// Clean up
|
||||
foreach ($tempPaths as $path) {
|
||||
if (file_exists($path)) {
|
||||
unlink($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function convertKeysToSnakeCase(array $items): array {
|
||||
return array_map(function($item) {
|
||||
$newItem = [];
|
||||
foreach ($item as $key => $value) {
|
||||
$newKey = strtolower(str_replace(' ', '_', $key));
|
||||
$newItem[$newKey] = $value;
|
||||
}
|
||||
return $newItem;
|
||||
}, $items);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div class="row" @keyup.enter="submitForm">
|
||||
<div class="col">
|
||||
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="$store.getters.isLoading(section)"></loading-component>
|
||||
<div class="row" v-show="!$store.getters.isLoading(section)">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<file-input-component :validator="$v.parameters.files" v-model="parameters.files">
|
||||
<template slot="label">
|
||||
<div class="font-heading fs-11 text-primary all-caps">Import Purchase Order PDF</div>
|
||||
</template>
|
||||
</file-input-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-20 justify-content-center" v-if="parameters.files.length > 0">
|
||||
<div class="col-auto">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-complete rounded-0 mx-1"
|
||||
@click="exportToCSV"
|
||||
:disabled="isExporting || isImporting">
|
||||
{{ isExporting ? 'Exporting...' : 'Export to CSV' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-complete rounded-0 mx-1"
|
||||
@click="importToSystem"
|
||||
:disabled="isExporting || isImporting">
|
||||
{{ isImporting ? 'Uploading...' : 'Continue Upload' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { required } from "vuelidate/lib/validators";
|
||||
import formHandler from '../../../general/mixins/formHandler';
|
||||
export default {
|
||||
|
||||
props: {
|
||||
section: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
bookingId: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
isExporting: false,
|
||||
isImporting: false,
|
||||
parameters: {
|
||||
files: [],
|
||||
products: []
|
||||
}
|
||||
}
|
||||
},
|
||||
validations: {
|
||||
parameters: {
|
||||
files: {
|
||||
required
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
exportToCSV(){
|
||||
this.parameters.products = [];
|
||||
if (this.isExporting) return;
|
||||
this.isExporting = true;
|
||||
this.submit(this.route('api.transaction.po.etl', this.bookingId), 'post', this.section + 'PDFExport', true, false)
|
||||
},
|
||||
importToSystem() {
|
||||
if (this.isImporting) return;
|
||||
this.isImporting = true;
|
||||
|
||||
this.submit(route('api.transaction.po.import', this.bookingId), 'post', this.section + 'PDFImport', true, true);
|
||||
},
|
||||
escapeCSV(value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
const str = value.toString();
|
||||
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
||||
return `"${str.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return str;
|
||||
},
|
||||
successHandler(response, section){
|
||||
if(this.section + 'PDFImport' === section){
|
||||
this.updateList()
|
||||
this.isImporting = false;
|
||||
}
|
||||
else if(this.section + 'PDFExport' === section){
|
||||
try {
|
||||
const BOM = '\uFEFF';
|
||||
let csvContent = "stock code,description,quantity,unit price\r\n";
|
||||
response.payload.forEach(item => {
|
||||
const formattedRow = [
|
||||
item.stockCode,
|
||||
this.escapeCSV(item.description),
|
||||
item.quantity,
|
||||
item.unit_price
|
||||
].join(',');
|
||||
|
||||
csvContent += formattedRow + "\r\n";
|
||||
});
|
||||
|
||||
const blob = new Blob([BOM + csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.setAttribute("download", "order_details.csv");
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
this.parameters.products = response.payload;
|
||||
} catch (e) {
|
||||
console.error("Export failed:", e);
|
||||
alert("Export failed. Please try again.");
|
||||
} finally {
|
||||
this.isExporting = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
errorHandler(response, statusCode, section){
|
||||
this.isExporting = false;
|
||||
this.isImporting = false;
|
||||
}
|
||||
},
|
||||
mixins: [formHandler]
|
||||
}
|
||||
</script>
|
||||
@@ -119,7 +119,8 @@
|
||||
</div>
|
||||
<div class="row" v-if="useUploadPdfPo">
|
||||
<div class="col">
|
||||
<pdf-table-extractor-component :booking-id="data.id" :section="section"></pdf-table-extractor-component>
|
||||
<!-- <pdf-table-extractor-component :booking-id="data.id" :section="section"></pdf-table-extractor-component> -->
|
||||
<pdf-upload-1688-component :booking-id="data.id" :section="section"></pdf-upload-1688-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user