diff --git a/app/Import/Providers/BaseImport.php b/app/Import/Providers/BaseImport.php index 5cec82ffeb42..66f244b2a2ff 100644 --- a/app/Import/Providers/BaseImport.php +++ b/app/Import/Providers/BaseImport.php @@ -78,7 +78,7 @@ class BaseImport ->setCompany($this->company); } - protected function getCsvData($entity_type) + public function getCsvData($entity_type) { $base64_encoded_csv = Cache::pull($this->hash . '-' . $entity_type); if (empty($base64_encoded_csv)) { @@ -150,10 +150,10 @@ class BaseImport $count = 0; foreach ($data as $key => $record) { + try { $entity = $this->transformer->transform($record); - /** @var \App\Http\Requests\Request $request */ $request = new $this->request_name(); @@ -179,6 +179,7 @@ class BaseImport $count++; } + } catch (\Exception $ex) { if ($ex instanceof ImportException) { @@ -194,8 +195,9 @@ class BaseImport ]; } - return $count; } + + return $count; } public function ingestInvoices($invoices, $invoice_number_key) @@ -216,8 +218,10 @@ class BaseImport $invoices = $this->groupInvoices($invoices, $invoice_number_key); foreach ($invoices as $raw_invoice) { + try { $invoice_data = $invoice_transformer->transform($raw_invoice); + nlog($invoice_data); $invoice_data['line_items'] = $this->cleanItems( $invoice_data['line_items'] ?? [] ); @@ -512,4 +516,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 9aa9d3e4abb2..7d50261227fd 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 b7ec1ce6f12c..967f7b773323 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 9b325e350f63..7e12ff74e064 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); + + $data = $this->preTransform($data, $entity_type); + + 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; + + } + + + public function product() { + + //done automatically inside the invoice() method as we need to harvest the products from the line items + + } + + public function invoice() { + + //make sure we update and create products with wave + $initial_update_products_value = $this->company->update_products; + $this->company->update_products = true; + + $this->company->save(); + + $entity_type = 'invoice'; + + $data = $this->getCsvData($entity_type); + + $data = $this->preTransform($data, $entity_type); + + if (empty($data)) { + $this->entity_count['invoices'] = 0; + return; + } + + $this->request_name = StoreInvoiceRequest::class; + $this->repository_name = InvoiceRepository::class; + $this->factory_name = InvoiceFactory::class; + + $this->repository = app()->make($this->repository_name); + $this->repository->import_mode = true; + + $this->transformer = new InvoiceTransformer($this->company); + + $invoice_count = $this->ingestInvoices($data, 'Invoice Number'); + + $this->entity_count['invoices'] = $invoice_count; + + $this->company->update_products = $initial_update_products_value; + $this->company->save(); + + } + + public function payment() + { + //these are pulled in when processing invoices + } + + public function vendor() + { + + $entity_type = 'vendor'; + + $data = $this->getCsvData($entity_type); + $data = $this->preTransform($data, $entity_type); + + if (empty($data)) { + $this->entity_count['vendors'] = 0; + return; + } + + $this->request_name = StoreVendorRequest::class; + $this->repository_name = VendorRepository::class; + $this->factory_name = VendorFactory::class; + + $this->repository = app()->make($this->repository_name); + $this->repository->import_mode = true; + + $this->transformer = new VendorTransformer($this->company); + + $vendor_count = $this->ingest($data, $entity_type); + + $this->entity_count['vendors'] = $vendor_count; + + } + + public function expense() + { + $entity_type = 'expense'; + + $data = $this->getCsvData($entity_type); + $data = $this->preTransform($data, $entity_type); + + if (empty($data)) { + $this->entity_count['expense'] = 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->ingestExpenses($data, $entity_type); + + $this->entity_count['expenses'] = $expense_count; + + } + + public function transform(array $data){} + + + private function groupExpenses($csvData) + { + + $grouped_expense = []; + $key = 'Transaction ID'; + + foreach($csvData as $expense) + { + if($expense['Account Group'] == 'Expense') + $grouped[$expense[$key]][] = $expense; + } + + return $grouped; + } + + public function ingestExpenses($data) + { + $key = 'Transaction ID'; + + $expense_transformer = $this->transformer; + + $vendor_repository = app()->make(VendorRepository::class); + $expense_repository = app()->make(ExpenseRepository::class); + + $expenses = $this->groupExpenses($data); + +// nlog($expenses); +// exit; + foreach ($expenses as $raw_expense) { + + try { + + $expense_data = $expense_transformer->transform($raw_expense); + + // If we don't have a client ID, but we do have client data, go ahead and create the client. + if (empty($expense_data['vendor_id']) ) { + $vendor_data['user_id'] = $this->getUserIDForRecord($expense_data); + + $vendor_repository->save( + ['name' => $raw_expense['Vendor Name']], + $vendor = VendorFactory::create( + $this->company->id, + $vendor_data['user_id'] + ) + ); + $expense_data['vendor_id'] = $vendor->id; + } + + $validator = Validator::make( + $expense_data, + (new StoreExpenseRequest())->rules() + ); + if ($validator->fails()) { + $this->error_array['expense'][] = [ + 'expense' => $expense_data, + 'error' => $validator->errors()->all(), + ]; + } else { + $expense = ExpenseFactory::create( + $this->company->id, + $this->getUserIDForRecord($expense_data) + ); + + $expense_repository->save($expense_data, $expense); + + } + } catch (\Exception $ex) { + if ($ex instanceof ImportException) { + $message = $ex->getMessage(); + } else { + report($ex); + $message = 'Unknown error'; + } + + $this->error_array['expense'][] = [ + 'expense' => $raw_expense, + 'error' => $message, + ]; + } + } + } -} \ No newline at end of file +} diff --git a/app/Import/Transformer/BaseTransformer.php b/app/Import/Transformer/BaseTransformer.php index a5e7d7098d00..179c991fa85b 100644 --- a/app/Import/Transformer/BaseTransformer.php +++ b/app/Import/Transformer/BaseTransformer.php @@ -11,9 +11,12 @@ namespace App\Import\Transformer; +use App\Factory\ExpenseCategoryFactory; use App\Factory\ProjectFactory; +use App\Factory\VendorFactory; use App\Models\ClientContact; use App\Models\Country; +use App\Models\ExpenseCategory; use App\Models\PaymentType; use App\Models\User; use App\Utils\Number; @@ -35,7 +38,12 @@ class BaseTransformer public function getString($data, $field) { - return isset($data[$field]) && $data[$field] ? $data[$field] : ''; + return isset($data[$field]) && $data[$field] ? trim($data[$field]) : ''; + } + + public function getValueOrNull($data, $field) + { + return isset($data[$field]) && $data[$field] ? $data[$field] : null; } public function getCurrencyByCode($data, $key = 'client.currency_id') @@ -363,6 +371,19 @@ class BaseTransformer ->exists(); } + /** * + * @return bool + */ + public function hasExpense($expense_number) + { + return $this->company + ->expenses() + ->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [ + strtolower(str_replace(' ', '', $expense_number)), + ]) + ->exists(); + } + /** * @param $quote_number * @@ -412,6 +433,23 @@ class BaseTransformer return $vendor ? $vendor->id : null; } + public function getVendorIdOrCreate($name) + { + if(empty($name)) + return null; + + $vendor = $this->getVendorId($name); + + if($vendor) + return $vendor; + + $vendor = VendorFactory::create($this->company->id, $this->company->owner()->id); + $vendor->name = $name; + $vendor->save(); + + return $vendor->id; + } + /** * @param $name * @@ -429,6 +467,23 @@ class BaseTransformer return $ec ? $ec->id : null; } + public function getOrCreateExpenseCategry($name) + { + if(empty($name)) + return null; + + $ec = $this->getExpenseCategoryId($name); + + if($ec) + return $ec; + + $expense_category = ExpenseCategoryFactory::create($this->company->id, $this->company->owner()->id); + $expense_category->name = $name; + $expense_category->save(); + + return $expense_category->id; + } + /** * @param $name * @@ -473,4 +528,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 3df03ad95e9f..b66b17b5414a 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 000000000000..537d3f3db651 --- /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 000000000000..f5431b90c50a --- /dev/null +++ b/app/Import/Transformer/Wave/ExpenseTransformer.php @@ -0,0 +1,66 @@ + $this->company->id, + 'vendor_id' => $this->getVendorIdOrCreate($this->getString($data, 'Vendor')), + 'number' => $this->getString($data, 'Bill Number'), + 'public_notes'=> $this->getString($data, 'Notes / Memo'), + 'date' => date( 'Y-m-d', strtotime( $data['Transaction Date Added'] ) ) ?: now()->format('Y-m-d'), //27-01-2022 + 'currency_id' => $this->company->settings->currency_id, + 'category_id' => $this->getOrCreateExpenseCategry($data['Account Name']), + 'amount' => $amount, + 'tax_name1' => $data['Sales Tax Name'], + 'tax_rate1' => $tax_rate, + ]; + + return $transformed; + } +} + + diff --git a/app/Import/Transformer/Wave/InvoiceTransformer.php b/app/Import/Transformer/Wave/InvoiceTransformer.php new file mode 100644 index 000000000000..7b8e4e8e6d45 --- /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/app/Import/Transformer/Wave/VendorTransformer.php b/app/Import/Transformer/Wave/VendorTransformer.php new file mode 100644 index 000000000000..0f0fe76b322c --- /dev/null +++ b/app/Import/Transformer/Wave/VendorTransformer.php @@ -0,0 +1,56 @@ +hasVendor( $data['vendor_name'] ) ) { + throw new ImportException('Vendor already exists'); + } + + return [ + 'company_id' => $this->company->id, + 'name' => $this->getString( $data, 'vendor_name' ), + 'number' => $this->getValueOrNull( $data, 'account_number' ), + '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' ), + 'currency_id' => $this->getCurrencyByCode( $data, 'vendor_currency' ), + '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/tests/Feature/Import/CSV/CsvImportTest.php b/tests/Feature/Import/CSV/CsvImportTest.php index d295ae5b2975..7b5ec9ade2b1 100644 --- a/tests/Feature/Import/CSV/CsvImportTest.php +++ b/tests/Feature/Import/CSV/CsvImportTest.php @@ -188,7 +188,8 @@ class CsvImportTest extends TestCase $base_transformer = new BaseTransformer($this->company); $this->assertTrue($base_transformer->hasClient('Ludwig Krajcik DVM')); - + $this->assertTrue($base_transformer->hasClient('Bradly Jaskolski Sr.')); + $client_id = $base_transformer->getClient('Ludwig Krajcik DVM', null); $c = Client::find($client_id); diff --git a/tests/Feature/Import/Wave/WaveTest.php b/tests/Feature/Import/Wave/WaveTest.php new file mode 100644 index 000000000000..50fe6122da69 --- /dev/null +++ b/tests/Feature/Import/Wave/WaveTest.php @@ -0,0 +1,645 @@ +withoutMiddleware(ThrottleRequests::class); + + config(['database.default' => config('ninja.db.default')]); + + $this->makeTestData(); + + $this->withoutExceptionHandling(); + } + + // public function testExpenseImport() + // { + + // $csv = file_get_contents( + // base_path() . '/tests/Feature/Import/wave_expenses.csv' + // ); + // $hash = Str::random(32); + + // $column_map = [ + // 0 => 'Transaction ID', + // 1 => 'Transaction Date', + // 2 => 'Account Name', + // 3 => 'Transaction Description', + // 4 => 'Transaction Line Description', + // 5 => 'Amount (One column)', + // 6 => ' ', + // 7 => 'Debit Amount (Two Column Approach)', + // 8 => 'Credit Amount (Two Column Approach)', + // 9 => 'Other Accounts for this Transaction', + // 10 => 'Customer', + // 11 => 'Vendor', + // 12 => 'Invoice Number', + // 13 => 'Bill Number', + // 14 => 'Notes / Memo', + // 15 => 'Amount Before Sales Tax', + // 16 => 'Sales Tax Amount', + // 17 => 'Sales Tax Name', + // 18 => 'Transaction Date Added', + // 19 => 'Transaction Date Last Modified', + // 20 => 'Account Group', + // 21 => 'Account Type', + // 22 => 'Account ID', + // ]; + + // $data = [ + // 'hash' => $hash, + // 'column_map' => ['expense' => ['mapping' => $column_map]], + // 'skip_header' => true, + // 'import_type' => 'waveaccounting', + // ]; + + // Cache::put($hash . '-expense', base64_encode($csv), 360); + + // $csv_importer = new Wave($data, $this->company); + + // $count = $csv_importer->import('expense'); + + + // } + + public function testVendorAndExpenseWaveImport() + { + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/wave_vendors.csv' + ); + $hash = Str::random(32); + + $column_map = [ + 0 => 'vendor_name', + 1 => 'email', + 2 => 'contact_first_name', + 3 => 'contact_last_name', + 4 => 'vendor_currency', + 5 => 'account_number', + 6 => 'phone', + 7 => 'fax', + 8 => 'mobile', + 9 => 'toll_free', + 10 => 'website', + 11 => 'country', + 12 => 'province/state', + 13 => 'address_line_1', + 14 => 'address_line_2', + 15 => 'city', + 16 => 'postal_code/zip_code', + ]; + + $data = [ + 'hash' => $hash, + 'column_map' => ['vendor' => ['mapping' => $column_map]], + 'skip_header' => true, + 'import_type' => 'waveaccounting', + ]; + + Cache::put($hash . '-vendor', base64_encode($csv), 360); + + $csv_importer = new Wave($data, $this->company); + + $count = $csv_importer->import('vendor'); + + $base_transformer = new BaseTransformer($this->company); + + $this->assertTrue($base_transformer->hasVendor('Vendor Name')); + + $vendor_id = $base_transformer->getVendorId('Vendor Name'); + + $vendor = Vendor::find($vendor_id); + + $this->assertInstanceOf(Vendor::class, $vendor); + + $this->assertEquals(12, $vendor->currency_id); + $this->assertEquals('Australian Capital Territory', $vendor->state); + $this->assertEquals('city', $vendor->city); + $this->assertEquals('postal_cod', $vendor->postal_code); + + $this->assertEquals('firstname', $vendor->contacts->first()->first_name); + $this->assertEquals('lastname', $vendor->contacts->first()->last_name); + $this->assertEquals('vendor@gmail.com', $vendor->contacts->first()->email); + $this->assertEquals('phone', $vendor->contacts->first()->phone); + + + // now lets try importing expenses / bills + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/wave_expenses.csv' + ); + $hash = Str::random(32); + + $column_map = [ + 0 => 'Transaction ID', + 1 => 'Transaction Date', + 2 => 'Account Name', + 3 => 'Transaction Description', + 4 => 'Transaction Line Description', + 5 => 'Amount (One column)', + 6 => ' ', + 7 => 'Debit Amount (Two Column Approach)', + 8 => 'Credit Amount (Two Column Approach)', + 9 => 'Other Accounts for this Transaction', + 10 => 'Customer', + 11 => 'Vendor', + 12 => 'Invoice Number', + 13 => 'Bill Number', + 14 => 'Notes / Memo', + 15 => 'Amount Before Sales Tax', + 16 => 'Sales Tax Amount', + 17 => 'Sales Tax Name', + 18 => 'Transaction Date Added', + 19 => 'Transaction Date Last Modified', + 20 => 'Account Group', + 21 => 'Account Type', + 22 => 'Account ID', + ]; + + $data = [ + 'hash' => $hash, + 'column_map' => ['expense' => ['mapping' => $column_map]], + 'skip_header' => true, + 'import_type' => 'waveaccounting', + ]; + + Cache::put($hash . '-expense', base64_encode($csv), 360); + + $csv_importer = new Wave($data, $this->company); + + $count = $csv_importer->import('expense'); + + $base_transformer = new BaseTransformer($this->company); + + $this->assertTrue($base_transformer->hasExpense("66")); + + } + + + + public function testClientWaveImport() + { + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/wave_clients.csv' + ); + $hash = Str::random(32); + + $column_map = [ + 0 => 'customer_name', + 1 => 'email', + 2 => 'contact_first_name', + 3 => 'contact_last_name', + 4 => 'customer_currency', + 5 => 'account_number', + 6 => 'phone', + 7 => 'fax', + 8 => 'mobile', + 9 => 'toll_free', + 10 => 'website', + 11 => 'country', + 12 => 'province/state', + 13 => 'address_line_1', + 14 => 'address_line_2', + 15 => 'city', + 16 => 'postal_code/zip_code', + 17 => 'shipping_address', + 18 => 'ship-to_contact', + 19 => 'ship-to_country', + 20 => 'ship-to_province/state', + 21 => 'ship-to_address_line_1', + 22 => 'ship-to_address_line_2', + 23 => 'ship-to_city', + 24 => 'ship-to_postal_code/zip_code', + 25 => 'ship-to_phone', + 26 => 'delivery_instructions', + ]; + + $data = [ + 'hash' => $hash, + 'column_map' => ['client' => ['mapping' => $column_map]], + 'skip_header' => true, + 'import_type' => 'waveaccounting', + ]; + + 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')); + + $client_id = $base_transformer->getClient('Jessica Jones', 'jessica@jones.com'); + + $client = Client::find($client_id); + + $this->assertInstanceOf(Client::class, $client); + + $this->assertEquals('12', $client->settings->currency_id); + $this->assertEquals('Queensland', $client->state); + $this->assertEquals('NYC', $client->city); + $this->assertEquals('11213', $client->postal_code); + + $this->assertEquals('Jessica Jones', $client->contacts->first()->first_name); + $this->assertEquals('', $client->contacts->first()->last_name); + $this->assertEquals('jessica@jones.com', $client->contacts->first()->email); + $this->assertEquals('555-867-5309', $client->contacts->first()->phone); + + } + + public function testInvoiceWaveImport() + { + //first import all the clients + + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/wave_clients.csv' + ); + $hash = Str::random(32); + + $column_map = [ + 0 => 'customer_name', + 1 => 'email', + 2 => 'contact_first_name', + 3 => 'contact_last_name', + 4 => 'customer_currency', + 5 => 'account_number', + 6 => 'phone', + 7 => 'fax', + 8 => 'mobile', + 9 => 'toll_free', + 10 => 'website', + 11 => 'country', + 12 => 'province/state', + 13 => 'address_line_1', + 14 => 'address_line_2', + 15 => 'city', + 16 => 'postal_code/zip_code', + 17 => 'shipping_address', + 18 => 'ship-to_contact', + 19 => 'ship-to_country', + 20 => 'ship-to_province/state', + 21 => 'ship-to_address_line_1', + 22 => 'ship-to_address_line_2', + 23 => 'ship-to_city', + 24 => 'ship-to_postal_code/zip_code', + 25 => 'ship-to_phone', + 26 => 'delivery_instructions', + ]; + + $data = [ + 'hash' => $hash, + 'column_map' => ['client' => ['mapping' => $column_map]], + 'skip_header' => true, + 'import_type' => 'waveaccounting', + ]; + + Cache::put($hash . '-client', base64_encode($csv), 360); + + $csv_importer = new Wave($data, $this->company); + + $count = $csv_importer->import('client'); + + //now import the invoices + + $csv = file_get_contents( + base_path() . '/tests/Feature/Import/wave_invoices.csv' + ); + $hash = Str::random(32); + + $column_map = [ + 0 => 'Transaction ID', + 1 => 'Transaction Date', + 2 => 'Account Name', + 3 => 'Transaction Description', + 4 => 'Transaction Line Description', + 5 => 'Amount (One column)', + 6 => ' ', + 7 => 'Debit Amount (Two Column Approach)', + 8 => 'Credit Amount (Two Column Approach)', + 9 => 'Other Accounts for this Transaction', + 10 => 'Customer', + 11 => 'Vendor', + 12 => 'Invoice Number', + 13 => 'Bill Number', + 14 => 'Notes / Memo', + 15 => 'Amount Before Sales Tax', + 16 => 'Sales Tax Amount', + 17 => 'Sales Tax Name', + 18 => 'Transaction Date Added', + 19 => 'Transaction Date Last Modified', + 20 => 'Account Group', + 21 => 'Account Type', + 22 => 'Account ID', + ]; + + $data = [ + 'hash' => $hash, + 'column_map' => ['invoice' => ['mapping' => $column_map]], + 'skip_header' => true, + 'import_type' => 'waveaccounting', + ]; + + Cache::put($hash . '-invoice', base64_encode($csv), 360); + + $csv_importer = new Wave($data, $this->company); + + $count = $csv_importer->import('invoice'); + + $base_transformer = new BaseTransformer($this->company); + + $this->assertTrue($base_transformer->hasInvoice("2")); + $this->assertTrue($base_transformer->hasInvoice("3")); + $this->assertTrue($base_transformer->hasInvoice("4")); + + $invoice_id = $base_transformer->getInvoiceId("4"); + $invoice = Invoice::find($invoice_id); + + $this->assertEquals(3500.41 , $invoice->amount); + $this->assertEquals(0 , $invoice->balance); + $this->assertEquals(2 , count($invoice->line_items)); + + $this->assertTrue($invoice->payments()->exists()); + $this->assertEquals(3500.41, $invoice->payments->first()->amount); + + } + + + // 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')); + // } + + +} + diff --git a/tests/Feature/Import/wave_expenses.csv b/tests/Feature/Import/wave_expenses.csv new file mode 100644 index 000000000000..4a5a6426060f --- /dev/null +++ b/tests/Feature/Import/wave_expenses.csv @@ -0,0 +1,26 @@ +Transaction ID,Transaction Date,Account Name,Transaction Description,Transaction Line Description,Amount (One column), ,Debit Amount (Two Column Approach),Credit Amount (Two Column Approach),Other Accounts for this Transaction,Customer,Vendor,Invoice Number,Bill Number,Notes / Memo,Amount Before Sales Tax,Sales Tax Amount,Sales Tax Name,Transaction Date Added,Transaction Date Last Modified,Account Group,Account Type,Account ID +537537669383611013,2015-12-30,Owner Investment / Drawings,-hjj,-hjj,96.00,,,96.00,Computer – Hardware,,,,,,96.00,,,2015-12-30,2022-02-10,Equity,Business Owner Contribution and Drawing, +537537669383611013,2015-12-30,Computer – Hardware,-hjj,-hjj,96.00,,96.00,,Owner Investment / Drawings,,,,,,96.00,,,2015-12-30,2022-02-10,Expense,Operating Expense, +537537671069721225,2016-02-20,Cash on Hand,-,-,-20.00,,,20.00,Business Licenses & Permits,,,,,,-20.00,,,2016-02-21,2022-02-10,Asset,Cash and Bank, +537537671069721225,2016-02-20,Business Licenses & Permits,-,-,20.00,,20.00,,Cash on Hand,,,,,,20.00,,,2016-02-21,2022-02-10,Expense,Operating Expense, +1405423775985459539,2022-02-10,Sales,asa - 2,asa - 2 - bananas,10.00,,,10.00,"Accounts Receivable, Capitation Fees, GST",asa,,2,,,10.00,,,2022-02-10,2022-02-10,Income,Income, +1405423775985459539,2022-02-10,Sales,asa - 2,asa - 2 - bananas,10.00,,,10.00,"Accounts Receivable, Capitation Fees, GST",asa,,2,,,10.00,,,2022-02-10,2022-02-10,Income,Income, +1405423775985459539,2022-02-10,Capitation Fees,asa - 2,asa - 2 - Best Product Name,10.00,,,10.00,"Accounts Receivable, GST, Sales",asa,,2,,,10.00,1.00,GST,2022-02-10,2022-02-10,Income,Income, +1405423775985459539,2022-02-10,GST,asa - 2,asa - 2 - Best Production Description,1.00,,,1.00,"Accounts Receivable, Capitation Fees, Sales",asa,,2,,,1.00,,,2022-02-10,2022-02-10,Liability,Sales Tax on Sales and Purchases, +1405423775985459539,2022-02-10,Capitation Fees,asa - 2,asa - 2 - Best Product Name,10.00,,,10.00,"Accounts Receivable, GST, Sales",asa,,2,,,10.00,1.00,GST,2022-02-10,2022-02-10,Income,Income, +1405423775985459539,2022-02-10,GST,asa - 2,asa - 2 - Best Production Description,1.00,,,1.00,"Accounts Receivable, Capitation Fees, Sales",asa,,2,,,1.00,,,2022-02-10,2022-02-10,Liability,Sales Tax on Sales and Purchases, +1405423775985459539,2022-02-10,Accounts Receivable,asa - 2,asa - 2,42.00,,42.00,,"Capitation Fees, GST, Sales",asa,,2,,,42.00,,,2022-02-10,2022-02-10,Asset,System Receivable Invoice, +1405429262361584366,2022-02-10,Computer – Hosting,Vendor Name - Bill ,Vendor Name - Bill - Best Product Name,10.00,,10.00,,"Accounts Payable, GST",,Vendor Name,,,,10.00,1.00,GST,2022-02-10,2022-02-10,Expense,Operating Expense, +1405429262361584366,2022-02-10,GST,Vendor Name - Bill ,Vendor Name - Bill - Best Product Name,-1.00,,1.00,,"Accounts Payable, Computer – Hosting",,Vendor Name,,,,1.00,,,2022-02-10,2022-02-10,Liability,Sales Tax on Sales and Purchases, +1405429262361584366,2022-02-10,Accounts Payable,Vendor Name - Bill ,Vendor Name - Bill ,11.00,,,11.00,"Computer – Hosting, GST",,Vendor Name,,,,11.00,,,2022-02-10,2022-02-10,Liability,System Payable Bill, +1405437089897538538,2022-02-10,Computer – Hosting,Vendor Name - Bill 66,Vendor Name - Bill 66 - Best Product Name,200.00,,200.00,,"Accounts Payable, GST",,Vendor Name,,66,,200.00,20.00,GST,2022-02-10,2022-02-10,Expense,Operating Expense, +1405437089897538538,2022-02-10,GST,Vendor Name - Bill 66,Vendor Name - Bill 66 - Best Product Name,-20.00,,20.00,,"Accounts Payable, Computer – Hosting",,Vendor Name,,66,,20.00,,,2022-02-10,2022-02-10,Liability,Sales Tax on Sales and Purchases, +1405437089897538538,2022-02-10,Computer – Hosting,Vendor Name - Bill 66,Vendor Name - Bill 66 - Best Product Name,10.00,,10.00,,"Accounts Payable, GST",,Vendor Name,,66,,10.00,1.00,GST,2022-02-10,2022-02-10,Expense,Operating Expense, +1405437089897538538,2022-02-10,GST,Vendor Name - Bill 66,Vendor Name - Bill 66 - Best Product Name,-1.00,,1.00,,"Accounts Payable, Computer – Hosting",,Vendor Name,,66,,1.00,,,2022-02-10,2022-02-10,Liability,Sales Tax on Sales and Purchases, +1405437089897538538,2022-02-10,Computer – Hosting,Vendor Name - Bill 66,Vendor Name - Bill 66 - Best Product Name,10.00,,10.00,,"Accounts Payable, GST",,Vendor Name,,66,,10.00,1.00,GST,2022-02-10,2022-02-10,Expense,Operating Expense, +1405437089897538538,2022-02-10,GST,Vendor Name - Bill 66,Vendor Name - Bill 66 - Best Product Name,-1.00,,1.00,,"Accounts Payable, Computer – Hosting",,Vendor Name,,66,,1.00,,,2022-02-10,2022-02-10,Liability,Sales Tax on Sales and Purchases, +1405437089897538538,2022-02-10,Computer – Hosting,Vendor Name - Bill 66,Vendor Name - Bill 66 - Best Product Name,10.00,,10.00,,"Accounts Payable, GST",,Vendor Name,,66,,10.00,1.00,GST,2022-02-10,2022-02-10,Expense,Operating Expense, +1405437089897538538,2022-02-10,GST,Vendor Name - Bill 66,Vendor Name - Bill 66 - Best Product Name,-1.00,,1.00,,"Accounts Payable, Computer – Hosting",,Vendor Name,,66,,1.00,,,2022-02-10,2022-02-10,Liability,Sales Tax on Sales and Purchases, +1405437089897538538,2022-02-10,Accounts Payable,Vendor Name - Bill 66,Vendor Name - Bill 66,253.00,,,253.00,"Computer – Hosting, GST",,Vendor Name,,66,,253.00,,,2022-02-10,2022-02-10,Liability,System Payable Bill, +1405441958066807314,2022-02-10,Accounts Payable,Bill Payment,Bill Payment,-253.00,,253.00,,Cash on Hand,,Vendor Name,,66,,-253.00,,,2022-02-10,2022-02-10,Liability,System Payable Bill, +1405441958066807314,2022-02-10,Cash on Hand,Bill Payment,Bill Payment,-253.00,,,253.00,Accounts Payable,,,,,,-253.00,,,2022-02-10,2022-02-10,Asset,Cash and Bank, diff --git a/tests/Feature/Import/wave_vendors.csv b/tests/Feature/Import/wave_vendors.csv new file mode 100644 index 000000000000..a7e23df0efd7 --- /dev/null +++ b/tests/Feature/Import/wave_vendors.csv @@ -0,0 +1,2 @@ +vendor_name,email,contact_first_name,contact_last_name,vendor_currency,account_number,phone,fax,mobile,toll_free,website,country,province/state,address_line_1,address_line_2,city,postal_code/zip_code +Vendor Name,vendor@gmail.com,firstname,lastname,AUD,,phone,fax,mob,,,Australia,Australian Capital Territory,address1,address2,city ,postal_cod diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 7c0155185bc9..898a59d75941 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -233,6 +233,8 @@ trait MockAccountData $user_id = $user->id; $this->user = $user; + auth()->login($user); + CreateCompanyTaskStatuses::dispatchNow($this->company, $this->user); $this->cu = CompanyUserFactory::create($user->id, $this->company->id, $this->account->id);