Working on additional helper to debug bad imports

This commit is contained in:
David Bomba
2026-01-14 18:31:50 +11:00
parent 33d300f4e6
commit f6f888decc
8 changed files with 98 additions and 42 deletions

View File

@@ -236,7 +236,7 @@ class PayPalBalanceAffecting
// $csv = Reader::createFromString($csvFile); // $csv = Reader::fromString($csvFile);
// // $csvdelimiter = self::detectDelimiter($csvfile); // // $csvdelimiter = self::detectDelimiter($csvfile);
// $csv->setDelimiter(","); // $csv->setDelimiter(",");
// $stmt = new Statement(); // $stmt = new Statement();

View File

@@ -11,37 +11,42 @@
namespace App\Helpers\Cache; namespace App\Helpers\Cache;
use Illuminate\Contracts\Redis\Factory as RedisFactory;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
class Atomic class Atomic
{ {
public static function set($key, $value = true, $ttl = 1): bool public static function set(string $key, mixed $value = true, int $ttl = 1): bool
{ {
$new_ttl = now()->addSeconds($ttl); $new_ttl = now()->addSeconds($ttl);
try { try {
return Redis::connection('sentinel-cache')->set($key, $value, 'EX', $ttl, 'NX') ? true : false; /** @var RedisFactory $redis */
$redis = app('redis');
$result = $redis->connection('sentinel-cache')->command('set', [$key, $value, 'EX', $ttl, 'NX']);
return (bool) $result;
} catch (\Throwable) { } catch (\Throwable) {
return Cache::add($key, $value, $new_ttl) ? true : false; return Cache::add($key, $value, $new_ttl) ? true : false;
} }
} }
public static function get($key) public static function get(string $key): mixed
{ {
try { try {
return Redis::connection('sentinel-cache')->get($key); /** @var RedisFactory $redis */
$redis = app('redis');
return $redis->connection('sentinel-cache')->command('get', [$key]);
} catch (\Throwable) { } catch (\Throwable) {
return Cache::get($key); return Cache::get($key);
} }
} }
public static function del($key) public static function del(string $key): mixed
{ {
try { try {
return Redis::connection('sentinel-cache')->del($key); /** @var RedisFactory $redis */
$redis = app('redis');
return $redis->connection('sentinel-cache')->command('del', [$key]);
} catch (\Throwable) { } catch (\Throwable) {
return Cache::forget($key); return Cache::forget($key);
} }

View File

@@ -446,7 +446,6 @@ class ImportController extends Controller
$csv = Reader::fromString($csvfile); $csv = Reader::fromString($csvfile);
// $csv = Reader::createFromString($csvfile);
$csvdelimiter = self::detectDelimiter($csvfile); $csvdelimiter = self::detectDelimiter($csvfile);
$csv->setDelimiter($csvdelimiter); $csv->setDelimiter($csvdelimiter);
$stmt = new Statement(); $stmt = new Statement();

View File

@@ -12,33 +12,35 @@
namespace App\Import\Providers; namespace App\Import\Providers;
use App\Models\User;
use App\Utils\Ninja;
use App\Models\Quote;
use League\Csv\Reader;
use App\Models\Company;
use App\Models\Invoice;
use League\Csv\Statement;
use App\Factory\TaskFactory;
use App\Factory\QuoteFactory;
use App\Factory\ClientFactory; use App\Factory\ClientFactory;
use Illuminate\Support\Carbon;
use App\Factory\InvoiceFactory; use App\Factory\InvoiceFactory;
use App\Factory\PaymentFactory; use App\Factory\PaymentFactory;
use App\Factory\QuoteFactory;
use App\Factory\RecurringInvoiceFactory;
use App\Factory\TaskFactory;
use App\Http\Requests\Quote\StoreQuoteRequest;
use App\Import\ImportException; use App\Import\ImportException;
use App\Jobs\Mail\NinjaMailerJob; use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject; use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\Import\CsvImportCompleted;
use App\Models\Company;
use App\Models\Invoice;
use App\Models\Quote;
use App\Models\User;
use App\Repositories\ClientRepository;
use App\Repositories\InvoiceRepository;
use App\Repositories\PaymentRepository;
use App\Repositories\QuoteRepository;
use App\Repositories\RecurringInvoiceRepository;
use App\Repositories\TaskRepository; use App\Repositories\TaskRepository;
use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\CleanLineItems;
use Illuminate\Support\Carbon; use App\Repositories\QuoteRepository;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use App\Repositories\ClientRepository;
use App\Mail\Import\CsvImportCompleted;
use App\Repositories\InvoiceRepository;
use App\Repositories\PaymentRepository;
use App\Factory\RecurringInvoiceFactory;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use League\Csv\Reader; use App\Http\Requests\Quote\StoreQuoteRequest;
use League\Csv\Statement; use App\Repositories\RecurringInvoiceRepository;
use App\Notifications\Ninja\GenericNinjaAdminNotification;
class BaseImport class BaseImport
{ {
@@ -70,6 +72,8 @@ class BaseImport
public array $entity_count = []; public array $entity_count = [];
public bool $store_import_for_research = false;
public function __construct(array $request, Company $company) public function __construct(array $request, Company $company)
{ {
$this->company = $company; $this->company = $company;
@@ -107,7 +111,7 @@ class BaseImport
$csv = base64_decode($base64_encoded_csv); $csv = base64_decode($base64_encoded_csv);
// $csv = mb_convert_encoding($csv, 'UTF-8', 'UTF-8'); // $csv = mb_convert_encoding($csv, 'UTF-8', 'UTF-8');
$csv = Reader::createFromString($csv); $csv = Reader::fromString($csv);
$csvdelimiter = self::detectDelimiter($csv); $csvdelimiter = self::detectDelimiter($csv);
$csv->setDelimiter($csvdelimiter); $csv->setDelimiter($csvdelimiter);
@@ -119,7 +123,8 @@ class BaseImport
// Remove Invoice Ninja headers // Remove Invoice Ninja headers
if ( if (
count($headers) && is_array($headers) &&
count($headers) > 0 &&
count($data) > 4 && count($data) > 4 &&
$this->import_type === 'csv' $this->import_type === 'csv'
) { ) {
@@ -320,7 +325,8 @@ class BaseImport
$entity->saveQuietly(); $entity->saveQuietly();
$count++; $count++;
} }
} catch (\Exception $ex) { }
catch (\Exception $ex) {
if (\DB::connection(config('database.default'))->transactionLevel() > 0) { if (\DB::connection(config('database.default'))->transactionLevel() > 0) {
\DB::connection(config('database.default'))->rollBack(); \DB::connection(config('database.default'))->rollBack();
} }
@@ -339,6 +345,20 @@ class BaseImport
nlog("Ingest {$ex->getMessage()}"); nlog("Ingest {$ex->getMessage()}");
nlog($record); nlog($record);
$this->store_import_for_research = true;
}
catch(\Throwable $ex){
if (\DB::connection(config('database.default'))->transactionLevel() > 0) {
\DB::connection(config('database.default'))->rollBack();
}
nlog("Throwable:: Ingest {$ex->getMessage()}");
nlog($record);
$this->store_import_for_research = true;
} }
} }
@@ -945,6 +965,40 @@ class BaseImport
$nmo->to_user = $this->company->owner(); $nmo->to_user = $this->company->owner();
NinjaMailerJob::dispatch($nmo, true); NinjaMailerJob::dispatch($nmo, true);
if (Ninja::isHosted() && $this->store_import_for_research) {
$content = [
'company_key' => $this->company->company_key,
'class_name' => class_basename($this),
'hash' => $this->hash,
];
$potential_imports = [
'client',
'product',
'invoice',
'payment',
'vendor',
'expense',
'quote',
'bank_transaction',
'task',
'recurring_invoice',
];
foreach ($potential_imports as $import) {
if(Cache::has($this->hash.'-'.$import)) {
// store on s3
// Cache::put($this->hash.'-'.$import, Cache::get($this->hash.'-'.$import), 60*60*24*2);
}
}
$this->company->notification(new GenericNinjaAdminNotification($content))->ninja();
}
} }
public function preTransform(array $data, $entity_type) public function preTransform(array $data, $entity_type)

View File

@@ -382,7 +382,6 @@ class Csv extends BaseImport implements ImportInterface
$this->entity_count['tasks'] = $task_count; $this->entity_count['tasks'] = $task_count;
} }
public function transform(array $data) public function transform(array $data)

View File

@@ -937,11 +937,10 @@ Código seguro de verificación (CSV): {$verifactu_log->status}";
$tax_label = ''; $tax_label = '';
if (collect($this->entity->line_items)->contains('tax_id', \App\Models\Product::PRODUCT_TYPE_REVERSE_TAX)) { if (collect($this->entity->line_items)->contains('tax_id', \App\Models\Product::PRODUCT_TYPE_REVERSE_TAX)) {
$tax_label .= ctrans('texts.reverse_tax_info') . "<br>"; $tax_label .= ctrans('texts.reverse_tax_info') . " <br>";
} }
else if ((int)$this->client->country_id !== (int)$this->company->settings->country_id) {
if ((int)$this->client->country_id !== (int)$this->company->settings->country_id) { $tax_label .= ctrans('texts.intracommunity_tax_info') . " <br>";
$tax_label .= ctrans('texts.intracommunity_tax_info') . "<br>";
if ($this->entity_calc->getTotalTaxes() > 0) { if ($this->entity_calc->getTotalTaxes() > 0) {
$tax_label = ''; $tax_label = '';

View File

@@ -178,7 +178,7 @@ input:checked ~ .dot {
Enterprise Plan Enterprise Plan
</h3> </h3>
<p class="text-5xl font-bold text-center text-white" id="y_plan_price"> <p class="text-5xl font-bold text-center text-white" id="y_plan_price">
$140 $180
</p> </p>
<p class="text-xs text-center uppercase text-white"> <p class="text-xs text-center uppercase text-white">
yearly yearly
@@ -275,12 +275,12 @@ document.getElementById('handleProYearlyClick').addEventListener('click', functi
}); });
const price_map = new Map(); const price_map = new Map();
//monthly //monthly
price_map.set('7LDdwRb1YK', '$16'); price_map.set('7LDdwRb1YK', '$18');
price_map.set('MVyb8mdvAZ', '$32'); price_map.set('MVyb8mdvAZ', '$32');
price_map.set('WpmbkR5azJ', '$54'); price_map.set('WpmbkR5azJ', '$54');
price_map.set('k8mepY2aMy', '$84'); price_map.set('k8mepY2aMy', '$84');
//yearly //yearly
price_map.set('LYqaQWldnj', '$160'); price_map.set('LYqaQWldnj', '$180');
price_map.set('kQBeX6mbyK', '$320'); price_map.set('kQBeX6mbyK', '$320');
price_map.set('GELe32Qd69', '$540'); price_map.set('GELe32Qd69', '$540');
price_map.set('MVyb86oevA', '$840'); price_map.set('MVyb86oevA', '$840');

View File

@@ -1134,7 +1134,7 @@ class ReportCsvGenerationTest extends TestCase
$csv = $response->body(); $csv = $response->body();
$reader = Reader::createFromString($csv); $reader = Reader::fromString($csv);
$reader->setHeaderOffset(0); $reader->setHeaderOffset(0);
$res = $reader->fetchColumnByName('Street'); $res = $reader->fetchColumnByName('Street');
@@ -1983,7 +1983,7 @@ class ReportCsvGenerationTest extends TestCase
$csv = $response->body(); $csv = $response->body();
$reader = Reader::createFromString($csv); $reader = Reader::fromString($csv);
$reader->setHeaderOffset(0); $reader->setHeaderOffset(0);
$res = $reader->fetchColumnByName('Contact First Name'); $res = $reader->fetchColumnByName('Contact First Name');
@@ -2014,7 +2014,7 @@ class ReportCsvGenerationTest extends TestCase
private function getFirstValueByColumn($csv, $column) private function getFirstValueByColumn($csv, $column)
{ {
$reader = Reader::createFromString($csv); $reader = Reader::fromString($csv);
$reader->setHeaderOffset(0); $reader->setHeaderOffset(0);
$res = $reader->fetchColumnByName($column); $res = $reader->fetchColumnByName($column);