From 6231f8bd204456a5b463bea05df6231c8ea3ec55 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 3 Feb 2022 18:53:39 +1100 Subject: [PATCH] Expense import --- app/Import/Definitions/ExpenseMap.php | 22 +- app/Import/Providers/BaseImport.php | 8 +- app/Import/Providers/Csv.php | 28 +++ app/Import/Transformer/BaseTransformer.php | 15 ++ .../Transformer/Csv/ExpenseTransformer.php | 72 ++++++ tests/Feature/Import/CSV/CsvImportTest.php | 227 ++++++++++-------- 6 files changed, 259 insertions(+), 113 deletions(-) create mode 100644 app/Import/Transformer/Csv/ExpenseTransformer.php diff --git a/app/Import/Definitions/ExpenseMap.php b/app/Import/Definitions/ExpenseMap.php index 642589a7d091..43ea39494b75 100644 --- a/app/Import/Definitions/ExpenseMap.php +++ b/app/Import/Definitions/ExpenseMap.php @@ -35,17 +35,17 @@ class ExpenseMap { return [ 0 => 'texts.vendor', - 1 => 'texts.client', - 2 => 'texts.project', - 3 => 'texts.category', - 4 => 'texts.amount', - 5 => 'texts.currency', - 6 => 'texts.date', - 7 => 'texts.payment_type', - 8 => 'texts.payment_date', - 9 => 'texts.transaction_reference', - 10 => 'texts.public_notes', - 11 => 'texts.private_notes', + 1 => 'texts.client', + 2 => 'texts.project', + 3 => 'texts.category', + 4 => 'texts.amount', + 5 => 'texts.currency', + 6 => 'texts.date', + 7 => 'texts.payment_type', + 8 => 'texts.payment_date', + 9 => 'texts.transaction_reference', + 10 => 'texts.public_notes', + 11 => 'texts.private_notes', ]; } } diff --git a/app/Import/Providers/BaseImport.php b/app/Import/Providers/BaseImport.php index f5b325057f39..ec5ab84f18a9 100644 --- a/app/Import/Providers/BaseImport.php +++ b/app/Import/Providers/BaseImport.php @@ -142,8 +142,11 @@ class BaseImport { $count = 0; - foreach ($data as $record) { + nlog("importing ".count($data) ." ". $entity_type); + + foreach ($data as $key => $record) { try { + nlog("importing {$key}"); $entity = $this->transformer->transform($record); /** @var \App\Http\Requests\Request $request */ @@ -169,8 +172,11 @@ class BaseImport $entity->saveQuietly(); $count++; + + nlog("finished importing {$key}"); } } catch (\Exception $ex) { + nlog("caught exception for {$key}"); if ($ex instanceof ImportException) { $message = $ex->getMessage(); } else { diff --git a/app/Import/Providers/Csv.php b/app/Import/Providers/Csv.php index 9d0d687c0f9c..0040c426c8f0 100644 --- a/app/Import/Providers/Csv.php +++ b/app/Import/Providers/Csv.php @@ -8,14 +8,17 @@ * * @license https://www.elastic.co/licensing/elastic-license */ + namespace App\Import\Providers; use App\Factory\ClientFactory; +use App\Factory\ExpenseFactory; use App\Factory\InvoiceFactory; use App\Factory\PaymentFactory; use App\Factory\ProductFactory; use App\Factory\VendorFactory; use App\Http\Requests\Client\StoreClientRequest; +use App\Http\Requests\Expense\StoreExpenseRequest; use App\Http\Requests\Invoice\StoreInvoiceRequest; use App\Http\Requests\Payment\StorePaymentRequest; use App\Http\Requests\Product\StoreProductRequest; @@ -24,11 +27,13 @@ use App\Import\ImportException; use App\Import\Providers\BaseImport; use App\Import\Providers\ImportInterface; use App\Import\Transformer\Csv\ClientTransformer; +use App\Import\Transformer\Csv\ExpenseTransformer; use App\Import\Transformer\Csv\InvoiceTransformer; use App\Import\Transformer\Csv\PaymentTransformer; use App\Import\Transformer\Csv\ProductTransformer; use App\Import\Transformer\Csv\VendorTransformer; use App\Repositories\ClientRepository; +use App\Repositories\ExpenseRepository; use App\Repositories\InvoiceRepository; use App\Repositories\PaymentRepository; use App\Repositories\ProductRepository; @@ -195,6 +200,29 @@ class Csv extends BaseImport implements ImportInterface private function expense() { + $entity_type = 'expense'; + + $data = $this->getCsvData($entity_type); + + $data = $this->preTransform($data, $entity_type); + + if (empty($data)) { + $this->entity_count['expenses'] = 0; + return; + } + + $this->request_name = StoreExpenseRequest::class; + $this->repository_name = ExpenseRepository::class; + $this->factory_name = ExpenseFactory::class; + + $this->repository = app()->make($this->repository_name); + $this->repository->import_mode = true; + + $this->transformer = new ExpenseTransformer($this->company); + + $expense_count = $this->ingest($data, $entity_type); + + $this->entity_count['expenses'] = $expense_count; } private function quote() diff --git a/app/Import/Transformer/BaseTransformer.php b/app/Import/Transformer/BaseTransformer.php index caa7bbdec8ee..0a6045627386 100644 --- a/app/Import/Transformer/BaseTransformer.php +++ b/app/Import/Transformer/BaseTransformer.php @@ -124,6 +124,21 @@ class BaseTransformer ->exists(); } + /** + * @param $name + * + * @return bool + */ + public function hasProject($name) + { + return $this->company + ->projects() + ->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [ + strtolower(str_replace(' ', '', $name)), + ]) + ->exists(); + } + /** * @param $key * diff --git a/app/Import/Transformer/Csv/ExpenseTransformer.php b/app/Import/Transformer/Csv/ExpenseTransformer.php new file mode 100644 index 000000000000..5c824057613d --- /dev/null +++ b/app/Import/Transformer/Csv/ExpenseTransformer.php @@ -0,0 +1,72 @@ +getClientId($data['expense.client']) + : null; + + return [ + 'company_id' => $this->company->id, + 'amount' => $this->getFloat($data, 'expense.amount'), + 'currency_id' => $this->getCurrencyByCode( + $data, + 'expense.currency_id' + ), + 'vendor_id' => isset($data['expense.vendor']) + ? $this->getVendorId($data['expense.vendor']) + : null, + 'client_id' => isset($data['expense.client']) + ? $this->getClientId($data['expense.client']) + : null, + 'date' => isset($data['expense.date']) + ? date('Y-m-d', strtotime($data['expense.date'])) + : null, + 'public_notes' => $this->getString($data, 'expense.public_notes'), + 'private_notes' => $this->getString($data, 'expense.private_notes'), + 'category_id' => isset($data['expense.category']) + ? $this->getExpenseCategoryId($data['expense.category']) + : null, + 'project_id' => isset($data['expense.project']) + ? $this->getProjectId($data['expense.project']) + : null, + 'payment_type_id' => isset($data['expense.payment_type']) + ? $this->getPaymentTypeId($data['expense.payment_type']) + : null, + 'payment_date' => isset($data['expense.payment_date']) + ? date('Y-m-d', strtotime($data['expense.payment_date'])) + : null, + 'custom_value1' => $this->getString($data, 'expense.custom_value1'), + 'custom_value2' => $this->getString($data, 'expense.custom_value2'), + 'custom_value3' => $this->getString($data, 'expense.custom_value3'), + 'custom_value4' => $this->getString($data, 'expense.custom_value4'), + 'transaction_reference' => $this->getString( + $data, + 'expense.transaction_reference' + ), + 'should_be_invoiced' => $clientId ? true : false, + ]; + } +} diff --git a/tests/Feature/Import/CSV/CsvImportTest.php b/tests/Feature/Import/CSV/CsvImportTest.php index 71fb152fa8f5..a87513bfdd00 100644 --- a/tests/Feature/Import/CSV/CsvImportTest.php +++ b/tests/Feature/Import/CSV/CsvImportTest.php @@ -39,27 +39,60 @@ class CsvImportTest extends TestCase use MakesHash; use MockAccountData; - public function setUp() :void + public function setUp(): void { parent::setUp(); - $this->withoutMiddleware( - ThrottleRequests::class - ); + $this->withoutMiddleware(ThrottleRequests::class); config(['database.default' => config('ninja.db.default')]); $this->makeTestData(); - + $this->withoutExceptionHandling(); } - - public function testVendorCsvImport() { - $csv = file_get_contents( base_path() . '/tests/Feature/Import/vendors.csv' ); - $hash = Str::random( 32 ); + public function testExpenseCsvImport() + { + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/expenses.csv' + ); + $hash = Str::random(32); $column_map = [ - 0 => 'vendor.name', + 0 => 'expense.client', + 1 => 'expense.project', + 2 => 'expense.notes', + 3 => 'expense.amount', + ]; + + $data = [ + 'hash' => $hash, + 'column_map' => ['expense' => ['mapping' => $column_map]], + 'skip_header' => true, + 'import_type' => 'csv', + ]; + + Cache::put($hash . '-expense', base64_encode($csv), 360); + + $csv_importer = new Csv($data, $this->company); + + $count = $csv_importer->import('expense'); + + $base_transformer = new BaseTransformer($this->company); + + nlog($csv_importer->entity_count); + + $this->assertTrue($base_transformer->hasProject('officiis')); + } + + public function testVendorCsvImport() + { + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/vendors.csv' + ); + $hash = Str::random(32); + $column_map = [ + 0 => 'vendor.name', 19 => 'vendor.currency_id', 20 => 'vendor.public_notes', 21 => 'vendor.private_notes', @@ -68,15 +101,15 @@ class CsvImportTest extends TestCase ]; $data = [ - 'hash' => $hash, - 'column_map' => [ 'vendor' => [ 'mapping' => $column_map ] ], + 'hash' => $hash, + 'column_map' => ['vendor' => ['mapping' => $column_map]], 'skip_header' => true, 'import_type' => 'csv', ]; $pre_import = Vendor::count(); - Cache::put( $hash . '-vendor', base64_encode( $csv ), 360 ); + Cache::put($hash . '-vendor', base64_encode($csv), 360); $csv_importer = new Csv($data, $this->company); @@ -84,18 +117,16 @@ class CsvImportTest extends TestCase $base_transformer = new BaseTransformer($this->company); - $this->assertTrue($base_transformer->hasVendor("Ludwig Krajcik DVM")); - - + $this->assertTrue($base_transformer->hasVendor('Ludwig Krajcik DVM')); } - - public function testProductImport() { - $csv = file_get_contents( base_path() . '/tests/Feature/Import/products.csv' ); - $hash = Str::random( 32 ); - Cache::put( $hash . '-product', base64_encode( $csv ), 360 ); + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/products.csv' + ); + $hash = Str::random(32); + Cache::put($hash . '-product', base64_encode($csv), 360); $column_map = [ 1 => 'product.product_key', @@ -104,14 +135,14 @@ class CsvImportTest extends TestCase ]; $data = [ - 'hash' => $hash, - 'column_map' => [ 'product' => [ 'mapping' => $column_map ] ], + 'hash' => $hash, + 'column_map' => ['product' => ['mapping' => $column_map]], 'skip_header' => true, 'import_type' => 'csv', ]; $csv_importer = new Csv($data, $this->company); - + $this->assertInstanceOf(Csv::class, $csv_importer); $csv_importer->import('product'); @@ -120,60 +151,65 @@ class CsvImportTest extends TestCase $this->assertTrue($base_transformer->hasProduct('officiis')); // $this->assertTrue($base_transformer->hasProduct('maxime')); - } public function testClientImport() { - $csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv'); + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/clients.csv' + ); $hash = Str::random(32); $column_map = [ - 1 => 'client.balance', - 2 => 'client.paid_to_date', - 0 => 'client.name', - 19 => 'client.currency_id', - 20 => 'client.public_notes', - 21 => 'client.private_notes', - 22 => 'contact.first_name', - 23 => 'contact.last_name', - 24 => 'contact.email', + 1 => 'client.balance', + 2 => 'client.paid_to_date', + 0 => 'client.name', + 19 => 'client.currency_id', + 20 => 'client.public_notes', + 21 => 'client.private_notes', + 22 => 'contact.first_name', + 23 => 'contact.last_name', + 24 => 'contact.email', ]; $data = [ - 'hash' => $hash, - 'column_map' => [ 'client' => [ 'mapping' => $column_map ] ], + 'hash' => $hash, + 'column_map' => ['client' => ['mapping' => $column_map]], 'skip_header' => true, 'import_type' => 'csv', ]; - Cache::put( $hash . '-client', base64_encode( $csv ), 360 ); + Cache::put($hash . '-client', base64_encode($csv), 360); $csv_importer = new Csv($data, $this->company); - + $this->assertInstanceOf(Csv::class, $csv_importer); $csv_importer->import('client'); $base_transformer = new BaseTransformer($this->company); - $this->assertTrue($base_transformer->hasClient("Ludwig Krajcik DVM")); + $this->assertTrue($base_transformer->hasClient('Ludwig Krajcik DVM')); - $client_id = $base_transformer->getClient("Ludwig Krajcik DVM", null); + $client_id = $base_transformer->getClient('Ludwig Krajcik DVM', null); $c = Client::find($client_id); $this->assertEquals($client_id, $c->id); - $client_id = $base_transformer->getClient("a non existent clent", "brook59@example.org"); + $client_id = $base_transformer->getClient( + 'a non existent clent', + 'brook59@example.org' + ); $this->assertEquals($client_id, $c->id); - } public function testInvoiceImport() { /*Need to import clients first*/ - $csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv'); + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/clients.csv' + ); $hash = Str::random(32); $column_map = [ 1 => 'client.balance', @@ -187,28 +223,30 @@ class CsvImportTest extends TestCase ]; $data = [ - 'hash' => $hash, - 'column_map' => [ 'client' => [ 'mapping' => $column_map ] ], + 'hash' => $hash, + 'column_map' => ['client' => ['mapping' => $column_map]], 'skip_header' => true, 'import_type' => 'csv', ]; - Cache::put( $hash . '-client', base64_encode( $csv ), 360 ); + Cache::put($hash . '-client', base64_encode($csv), 360); $csv_importer = new Csv($data, $this->company); - + $this->assertInstanceOf(Csv::class, $csv_importer); $csv_importer->import('client'); $base_transformer = new BaseTransformer($this->company); - $this->assertTrue($base_transformer->hasClient("Ludwig Krajcik DVM")); + $this->assertTrue($base_transformer->hasClient('Ludwig Krajcik DVM')); /* client import verified*/ /*Now import invoices*/ - $csv = file_get_contents(base_path().'/tests/Feature/Import/invoice.csv'); + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/invoice.csv' + ); $hash = Str::random(32); $column_map = [ @@ -230,27 +268,26 @@ class CsvImportTest extends TestCase ]; $data = [ - 'hash' => $hash, - 'column_map' => [ 'invoice' => [ 'mapping' => $column_map ] ], + 'hash' => $hash, + 'column_map' => ['invoice' => ['mapping' => $column_map]], 'skip_header' => true, 'import_type' => 'csv', ]; - Cache::put( $hash . '-invoice', base64_encode( $csv ), 360 ); + Cache::put($hash . '-invoice', base64_encode($csv), 360); $csv_importer = new Csv($data, $this->company); $csv_importer->import('invoice'); - $this->assertTrue($base_transformer->hasInvoice("801")); - - + $this->assertTrue($base_transformer->hasInvoice('801')); /* Lets piggy back payments tests here to save rebuilding the test multiple times*/ - - $csv = file_get_contents( base_path() . '/tests/Feature/Import/payments.csv' ); - $hash = Str::random( 32 ); + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/payments.csv' + ); + $hash = Str::random(32); $column_map = [ 0 => 'payment.client_id', @@ -260,69 +297,57 @@ class CsvImportTest extends TestCase ]; $data = [ - 'hash' => $hash, - 'column_map' => [ 'payment' => [ 'mapping' => $column_map ] ], + 'hash' => $hash, + 'column_map' => ['payment' => ['mapping' => $column_map]], 'skip_header' => true, 'import_type' => 'csv', ]; - Cache::put( $hash . '-payment', base64_encode( $csv ), 360 ); + Cache::put($hash . '-payment', base64_encode($csv), 360); $csv_importer = new Csv($data, $this->company); $csv_importer->import('payment'); - $this->assertTrue($base_transformer->hasInvoice("801")); + $this->assertTrue($base_transformer->hasInvoice('801')); - $invoice_id = $base_transformer->getInvoiceId("801"); + $invoice_id = $base_transformer->getInvoiceId('801'); $invoice = Invoice::find($invoice_id); $this->assertTrue($invoice->payments()->exists()); $this->assertEquals(1, $invoice->payments()->count()); $this->assertEquals(400, $invoice->payments()->sum('payments.amount')); - } - - - - - - - - - - } +// public function testClientCsvImport() +// { +// $csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv'); +// $hash = Str::random(32); +// $column_map = [ +// 1 => 'client.balance', +// 2 => 'client.paid_to_date', +// 0 => 'client.name', +// 19 => 'client.currency_id', +// 20 => 'client.public_notes', +// 21 => 'client.private_notes', +// 22 => 'contact.first_name', +// 23 => 'contact.last_name', +// ]; - // public function testClientCsvImport() - // { - // $csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv'); - // $hash = Str::random(32); - // $column_map = [ - // 1 => 'client.balance', - // 2 => 'client.paid_to_date', - // 0 => 'client.name', - // 19 => 'client.currency_id', - // 20 => 'client.public_notes', - // 21 => 'client.private_notes', - // 22 => 'contact.first_name', - // 23 => 'contact.last_name', - // ]; +// $data = [ +// 'hash' => $hash, +// 'column_map' => [ 'client' => [ 'mapping' => $column_map ] ], +// 'skip_header' => true, +// 'import_type' => 'csv', +// ]; - // $data = [ - // 'hash' => $hash, - // 'column_map' => [ 'client' => [ 'mapping' => $column_map ] ], - // 'skip_header' => true, - // 'import_type' => 'csv', - // ]; +// $pre_import = Client::count(); - // $pre_import = Client::count(); +// Cache::put( $hash . '-client', base64_encode( $csv ), 360 ); - // Cache::put( $hash . '-client', base64_encode( $csv ), 360 ); +// CSVImport::dispatchNow( $data, $this->company ); - // CSVImport::dispatchNow( $data, $this->company ); - - // $this->assertGreaterThan( $pre_import, Client::count() ); - // } +// $this->assertGreaterThan( $pre_import, Client::count() ); +// }