From a0439f23b245ff3d9373bc9a0fc6463bae226388 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Feb 2022 13:02:02 +1100 Subject: [PATCH] Wave tests --- app/Import/Providers/BaseImport.php | 28 +- app/Import/Providers/Csv.php | 21 -- app/Import/Providers/ImportInterface.php | 2 - app/Import/Providers/Wave.php | 79 ++++- app/Import/Transformer/BaseTransformer.php | 22 ++ .../Transformer/Csv/ClientTransformer.php | 2 +- .../Transformer/Wave/ClientTransformer.php | 74 ++++ .../Transformer/Wave/ExpenseTransformer.php | 45 +++ .../Transformer/Wave/InvoiceTransformer.php | 80 +++++ tests/Feature/Import/Wave/WaveTest.php | 334 ++++++++++++++++++ 10 files changed, 658 insertions(+), 29 deletions(-) create mode 100644 app/Import/Transformer/Wave/ClientTransformer.php create mode 100644 app/Import/Transformer/Wave/ExpenseTransformer.php create mode 100644 app/Import/Transformer/Wave/InvoiceTransformer.php create mode 100644 tests/Feature/Import/Wave/WaveTest.php diff --git a/app/Import/Providers/BaseImport.php b/app/Import/Providers/BaseImport.php index 5cec82ffeb..e6822705b1 100644 --- a/app/Import/Providers/BaseImport.php +++ b/app/Import/Providers/BaseImport.php @@ -148,12 +148,12 @@ class BaseImport public function ingest($data, $entity_type) { $count = 0; - +nlog("record count = ".count($data)); foreach ($data as $key => $record) { try { $entity = $this->transformer->transform($record); - +nlog($entity); /** @var \App\Http\Requests\Request $request */ $request = new $this->request_name(); @@ -174,6 +174,7 @@ class BaseImport $this->getUserIDForRecord($entity) ) ); +nlog("saving {$entity->name}"); $entity->saveQuietly(); $count++; @@ -181,6 +182,8 @@ class BaseImport } } catch (\Exception $ex) { +nlog($e->getMessage()); + if ($ex instanceof ImportException) { $message = $ex->getMessage(); } else { @@ -512,4 +515,25 @@ class BaseImport NinjaMailerJob::dispatch($nmo); } + + public function preTransform(array $data, $entity_type) + { + if (empty($this->column_map[$entity_type])) { + return false; + } + + if ($this->skip_header) { + array_shift($data); + } + + //sort the array by key + $keys = $this->column_map[$entity_type]; + ksort($keys); + + $data = array_map(function ($row) use ($keys) { + return array_combine($keys, array_intersect_key($row, $keys)); + }, $data); + + return $data; + } } diff --git a/app/Import/Providers/Csv.php b/app/Import/Providers/Csv.php index 9aa9d3e4ab..7d50261227 100644 --- a/app/Import/Providers/Csv.php +++ b/app/Import/Providers/Csv.php @@ -237,27 +237,6 @@ class Csv extends BaseImport implements ImportInterface } - public function preTransform(array $data, $entity_type) - { - if (empty($this->column_map[$entity_type])) { - return false; - } - - if ($this->skip_header) { - array_shift($data); - } - - //sort the array by key - $keys = $this->column_map[$entity_type]; - ksort($keys); - - $data = array_map(function ($row) use ($keys) { - return array_combine($keys, array_intersect_key($row, $keys)); - }, $data); - - return $data; - } - public function transform(array $data) { } diff --git a/app/Import/Providers/ImportInterface.php b/app/Import/Providers/ImportInterface.php index b7ec1ce6f1..967f7b7733 100644 --- a/app/Import/Providers/ImportInterface.php +++ b/app/Import/Providers/ImportInterface.php @@ -15,8 +15,6 @@ interface ImportInterface public function import(string $entity); - public function preTransform(array $data, string $entity_type); - public function transform(array $data); public function client(); diff --git a/app/Import/Providers/Wave.php b/app/Import/Providers/Wave.php index 9b325e350f..6875a73e88 100644 --- a/app/Import/Providers/Wave.php +++ b/app/Import/Providers/Wave.php @@ -1,4 +1,5 @@ {$entity}(); + } + + //collate any errors + + $this->finalizeImport(); + } + + public function client() + { + $entity_type = 'client'; + + $data = $this->getCsvData($entity_type); +nlog($data); + + $data = $this->preTransform($data, $entity_type); +nlog($data); + + if (empty($data)) { + $this->entity_count['clients'] = 0; + return; + } + + $this->request_name = StoreClientRequest::class; + $this->repository_name = ClientRepository::class; + $this->factory_name = ClientFactory::class; + + $this->repository = app()->make($this->repository_name); + $this->repository->import_mode = true; + + $this->transformer = new ClientTransformer($this->company); + + $client_count = $this->ingest($data, $entity_type); + + $this->entity_count['clients'] = $client_count; + + nlog($this->entity_count); + } + + public function transform(array $data){} + + public function product() {} + + public function invoice() {} + + public function payment() {} + + public function vendor() {} + + public function expense() {} + +} diff --git a/app/Import/Transformer/BaseTransformer.php b/app/Import/Transformer/BaseTransformer.php index a5e7d7098d..8a3473ea55 100644 --- a/app/Import/Transformer/BaseTransformer.php +++ b/app/Import/Transformer/BaseTransformer.php @@ -14,6 +14,7 @@ namespace App\Import\Transformer; use App\Factory\ProjectFactory; use App\Models\ClientContact; use App\Models\Country; +use App\Models\ExpenseCategory; use App\Models\PaymentType; use App\Models\User; use App\Utils\Number; @@ -38,6 +39,11 @@ class BaseTransformer return isset($data[$field]) && $data[$field] ? $data[$field] : ''; } + public function getValueOrNull($data, $field) + { + return isset($data[$field]) && $data[$field] ? $data[$field] : null; + } + public function getCurrencyByCode($data, $key = 'client.currency_id') { $code = array_key_exists($key, $data) ? $data[$key] : false; @@ -429,6 +435,20 @@ class BaseTransformer return $ec ? $ec->id : null; } + public function getOrCreateExpenseCategry($name) + { + $ec = $this->getExpenseCategoryId($name); + + if($ec) + return $ec; + + $expense_category = ExpenseCategory::create($this->company->id, $this->company->owner()->id); + $expense_category->name = $name; + $expense_category->save(); + + return $expense_category->id; + } + /** * @param $name * @@ -473,4 +493,6 @@ class BaseTransformer return $pt ? $pt->id : null; } + + } diff --git a/app/Import/Transformer/Csv/ClientTransformer.php b/app/Import/Transformer/Csv/ClientTransformer.php index 3df03ad95e..b66b17b541 100644 --- a/app/Import/Transformer/Csv/ClientTransformer.php +++ b/app/Import/Transformer/Csv/ClientTransformer.php @@ -26,7 +26,7 @@ class ClientTransformer extends BaseTransformer */ public function transform($data) { - if (isset($data->name) && $this->hasClient($data->name)) { + if (isset($data->name) && $this->getString($data, 'client.name')) { throw new ImportException('Client already exists'); } diff --git a/app/Import/Transformer/Wave/ClientTransformer.php b/app/Import/Transformer/Wave/ClientTransformer.php new file mode 100644 index 0000000000..537d3f3db6 --- /dev/null +++ b/app/Import/Transformer/Wave/ClientTransformer.php @@ -0,0 +1,74 @@ +hasClient( $data['customer_name'] ) ) { + throw new ImportException('Client already exists'); + } + + $settings = new \stdClass; + $settings->currency_id = (string) $this->getCurrencyByCode( $data, 'customer_currency' ); + + if ( strval( $data['Payment Terms'] ?? '' ) > 0 ) { + $settings->payment_terms = $data['Payment Terms']; + } + + return [ + 'company_id' => $this->company->id, + 'name' => $this->getString( $data, 'customer_name' ), + 'number' => $this->getValueOrNull( $data, 'account_number' ), + 'work_phone' => $this->getString( $data, 'phone' ), + 'website' => $this->getString( $data, 'website' ), + 'country_id' => !empty( $data['country'] ) ? $this->getCountryId( $data['country'] ) : null, + 'state' => $this->getString( $data, 'province/state' ), + 'address1' => $this->getString( $data, 'address_line_1' ), + 'address2' => $this->getString( $data, 'address_line_2' ), + 'city' => $this->getString( $data, 'city' ), + 'postal_code' => $this->getString( $data, 'postal_code/zip_code' ), + + + 'shipping_country_id' => !empty( $data['ship-to_country'] ) ? $this->getCountryId( $data['country'] ) : null, + 'shipping_state' => $this->getString( $data, 'ship-to_province/state' ), + 'shipping_address1' => $this->getString( $data, 'ship-to_address_line_1' ), + 'shipping_address2' => $this->getString( $data, 'ship-to_address_line_2' ), + 'shipping_city' => $this->getString( $data, 'ship-to_city' ), + 'shipping_postal_code' => $this->getString( $data, 'ship-to_postal_code/zip_code' ), + 'public_notes' => $this->getString( $data, 'delivery_instructions' ), + + 'credit_balance' => 0, + 'settings' =>$settings, + 'client_hash' => Str::random( 40 ), + 'contacts' => [ + [ + 'first_name' => $this->getString( $data, 'contact_first_name' ), + 'last_name' => $this->getString( $data, 'contact_last_name' ), + 'email' => $this->getString( $data, 'email' ), + 'phone' => $this->getString( $data, 'phone' ), + ], + ], + ]; + } +} \ No newline at end of file diff --git a/app/Import/Transformer/Wave/ExpenseTransformer.php b/app/Import/Transformer/Wave/ExpenseTransformer.php new file mode 100644 index 0000000000..f8d5b5ad31 --- /dev/null +++ b/app/Import/Transformer/Wave/ExpenseTransformer.php @@ -0,0 +1,45 @@ + $this->company->id, + 'vendor_id' => $this->getVendorId($vendor_name = $this->getString($data, 'vendor')), + 'number' => $this->getString($data, 'invoice_number'), + 'public_notes'=> $this->getString($data, 'description'), + 'date' => date( 'Y-m-d', strtotime( $data['bill_date'] ) ) ?: now()->format('Y-m-d'), //27-01-2022 + 'currency_id' => $this->getCurrencyByCode( $data, 'currency' ), + 'category_id' => $this->getOrCreateExpenseCategry($data['account']), + 'amount' => $this->getFloat($data['quantity']) * $this->getFloat($data['amount']), + 'tax_name1' => $this->getTaxName($data['taxes']), + 'tax_rate1' => $this->getTaxRate($data['taxes']), + ]; + + + return $transformed; + } +} \ No newline at end of file diff --git a/app/Import/Transformer/Wave/InvoiceTransformer.php b/app/Import/Transformer/Wave/InvoiceTransformer.php new file mode 100644 index 0000000000..8c824f12bf --- /dev/null +++ b/app/Import/Transformer/Wave/InvoiceTransformer.php @@ -0,0 +1,80 @@ +hasInvoice( $invoice_data['Invoice Number'] ) ) { + throw new ImportException( 'Invoice number already exists' ); + } + + $transformed = [ + 'company_id' => $this->company->id, + 'client_id' => $this->getClient( $customer_name = $this->getString( $invoice_data, 'Customer' ), null ), + 'number' => $invoice_number = $this->getString( $invoice_data, 'Invoice Number' ), + 'date' => date( 'Y-m-d', strtotime( $invoice_data['Transaction Date'] ) ) ?: now()->format('Y-m-d'), //27-01-2022 + 'currency_id' => $this->getCurrencyByCode( $invoice_data, 'Currency' ), + 'status_id' => Invoice::STATUS_SENT, + ]; + + $line_items = []; + $payments = []; + foreach ( $line_items_data as $record ) { + if ( $record['Account Type'] === 'Income' ) { + $description = $this->getString( $record, 'Transaction Line Description' ); + + // Remove duplicate data from description + if ( substr( $description, 0, strlen( $customer_name ) + 3 ) === $customer_name . ' - ' ) { + $description = substr( $description, strlen( $customer_name ) + 3 ); + } + + if ( substr( $description, 0, strlen( $invoice_number ) + 3 ) === $invoice_number . ' - ' ) { + $description = substr( $description, strlen( $invoice_number ) + 3 ); + } + + $line_items[] = [ + 'notes' => $description, + 'cost' => $this->getFloat( $record, 'Amount Before Sales Tax' ), + 'tax_name1' => $this->getString( $record, 'Sales Tax Name' ), + 'tax_rate1' => $this->getFloat( $record, 'Sales Tax Amount' ), + + 'quantity' => 1, + ]; + } elseif ( $record['Account Type'] === 'System Receivable Invoice' ) { + // This is a payment + $payments[] = [ + 'date' => date( 'Y-m-d', strtotime( $invoice_data['Transaction Date'] ) ), + 'amount' => $this->getFloat( $record, 'Amount (One column)' ), + ]; + } + } + + $transformed['line_items'] = $line_items; + $transformed['payments'] = $payments; + + return $transformed; + } +} \ No newline at end of file diff --git a/tests/Feature/Import/Wave/WaveTest.php b/tests/Feature/Import/Wave/WaveTest.php new file mode 100644 index 0000000000..68039d9823 --- /dev/null +++ b/tests/Feature/Import/Wave/WaveTest.php @@ -0,0 +1,334 @@ +withoutMiddleware(ThrottleRequests::class); + + config(['database.default' => config('ninja.db.default')]); + + $this->makeTestData(); + + $this->withoutExceptionHandling(); + } + + public function testClientWaveImport() + { + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/wave_clients.csv' + ); + $hash = Str::random(32); + + $column_map = [ + 0 => 'client.name', + 1 => 'contact.email', + 2 => 'contact.first_name', + 3 => 'contact.last_name', + 4 => 'client.currency_id', + 6 => 'client.phone', + 10 => 'client.website', + 11 => 'client.country_id', + 12 => 'client.state', + 13 => 'client.address1', + 14 => 'client.address2', + 15 => 'client.city', + 16 => 'client.postal_code', + 19 => 'client.shipping_country_id', + 20 => 'client.shipping_state', + 21 => 'client.shipping_address1', + 22 => 'client.shipping_address2', + 23 => 'client.shipping_city', + ]; + + $data = [ + 'hash' => $hash, + 'column_map' => ['client' => ['mapping' => $column_map]], + 'skip_header' => true, + 'import_type' => 'wave', + ]; + + Cache::put($hash . '-client', base64_encode($csv), 360); + + $csv_importer = new Wave($data, $this->company); + + $count = $csv_importer->import('client'); + + $base_transformer = new BaseTransformer($this->company); + + $this->assertTrue($base_transformer->hasClient('Homer Simpson')); + // $this->assertTrue($base_transformer->hasClient('Jessica Jones')); + // $this->assertTrue($base_transformer->hasClient('Lucas Cage')); + // $this->assertTrue($base_transformer->hasClient('Mark Walberg')); + + } + + // 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', + // 22 => 'vendor.first_name', + // 23 => 'vendor.last_name', + // ]; + + // $data = [ + // '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); + + // $csv_importer = new Csv($data, $this->company); + + // $csv_importer->import('vendor'); + + // $base_transformer = new BaseTransformer($this->company); + + // $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); + + // $column_map = [ + // 1 => 'product.product_key', + // 2 => 'product.notes', + // 3 => 'product.cost', + // ]; + + // $data = [ + // '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'); + + // $base_transformer = new BaseTransformer($this->company); + + // $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' + // ); + // $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', + // ]; + + // $data = [ + // 'hash' => $hash, + // 'column_map' => ['client' => ['mapping' => $column_map]], + // 'skip_header' => true, + // 'import_type' => 'csv', + // ]; + + // 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')); + + // $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' + // ); + + // $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' + // ); + // $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', + // ]; + + // 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')); + + // /* client import verified*/ + + // /*Now import invoices*/ + // $csv = file_get_contents( + // base_path() . '/tests/Feature/Import/invoice.csv' + // ); + // $hash = Str::random(32); + + // $column_map = [ + // 1 => 'client.email', + // 3 => 'payment.amount', + // 5 => 'invoice.po_number', + // 8 => 'invoice.due_date', + // 9 => 'item.discount', + // 11 => 'invoice.partial_due_date', + // 12 => 'invoice.public_notes', + // 13 => 'invoice.private_notes', + // 0 => 'client.name', + // 2 => 'invoice.number', + // 7 => 'invoice.date', + // 14 => 'item.product_key', + // 15 => 'item.notes', + // 16 => 'item.cost', + // 17 => 'item.quantity', + // ]; + + // $data = [ + // 'hash' => $hash, + // 'column_map' => ['invoice' => ['mapping' => $column_map]], + // 'skip_header' => true, + // 'import_type' => 'csv', + // ]; + + // 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')); + + // /* 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); + + // $column_map = [ + // 0 => 'payment.client_id', + // 1 => 'payment.invoice_number', + // 2 => 'payment.amount', + // 3 => 'payment.date', + // ]; + + // $data = [ + // 'hash' => $hash, + // 'column_map' => ['payment' => ['mapping' => $column_map]], + // 'skip_header' => true, + // 'import_type' => 'csv', + // ]; + + // 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')); + + // $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')); + // } + + +} +