'test'], ['name' => 'T', 'price' => 0, 'features' => []]); $this->company = Company::create(['plan_id' => $plan->id, 'slug' => 'exc-' . uniqid(), 'name' => 'Exc Co', 'status' => 'active']); app(TenantManager::class)->setCurrent($this->company); $this->supplier = Supplier::create(['name' => 'Rossko MD', 'phone' => '+37322000000']); } private function makeXlsx(array $rows, string $headerName = 'Articol'): string { $spreadsheet = new Spreadsheet; $sheet = $spreadsheet->getActiveSheet(); $sheet->setCellValue('A1', ''); $sheet->setCellValue('B1', $headerName); $sheet->setCellValue('C1', 'Denumire'); $sheet->setCellValue('D1', 'Brand'); $sheet->setCellValue('E1', 'Cant'); $sheet->setCellValue('F1', 'Preț'); $r = 2; foreach ($rows as $row) { $sheet->setCellValue("B$r", $row[0]); $sheet->setCellValue("C$r", $row[1]); $sheet->setCellValue("D$r", $row[2]); $sheet->setCellValue("E$r", $row[3]); $sheet->setCellValue("F$r", $row[4]); $r++; } $tmp = tempnam(sys_get_temp_dir(), 'inv') . '.xlsx'; (new Xlsx($spreadsheet))->save($tmp); return $tmp; } public function test_headers_preview_returns_first_5_rows_and_column_letters(): void { $path = $this->makeXlsx([ ['W71221', 'Filtru ulei Mann', 'Mann', 10, 61.00], ['GDB1550', 'Plăcuțe TRW', 'TRW', 4, 280.00], ]); $svc = app(ExcelInvoiceImportService::class); $preview = $svc->headersPreview($path); $this->assertContains('B', $preview['columns']); $this->assertContains('F', $preview['columns']); $this->assertEquals('Articol', $preview['rows'][0]['B']); $this->assertEquals('Filtru ulei Mann', $preview['rows'][1]['C']); unlink($path); } public function test_preview_classifies_rows_as_found_or_new(): void { // Existing part in DB Part::create(['name' => 'Filtru ulei Mann W712/83', 'article' => 'W71221', 'buy_price' => 60, 'sell_price' => 85]); $path = $this->makeXlsx([ ['W71221', 'Filtru ulei', 'Mann', 10, 61.00], ['NEW-001', 'Articol nou', 'Generic', 5, 30.00], ['', 'Fără cod', '', 1, 100.00], ]); $result = app(ExcelInvoiceImportService::class)->preview($path, [ 'article_col' => 'B', 'name_col' => 'C', 'brand_col' => 'D', 'qty_col' => 'E', 'price_col' => 'F', 'header_row' => 1, ]); $this->assertEquals(3, $result['summary']['total']); $this->assertEquals(1, $result['summary']['found']); $this->assertEquals(1, $result['summary']['new']); $this->assertEquals(1, $result['summary']['no_article']); $this->assertEquals('found', $result['rows'][0]['status']); $this->assertEquals('new', $result['rows'][1]['status']); $this->assertEquals('no_article', $result['rows'][2]['status']); unlink($path); } public function test_import_creates_purchase_with_items_and_auto_creates_new_parts(): void { Part::create(['name' => 'Existing', 'article' => 'EX-1', 'buy_price' => 50]); $svc = app(ExcelInvoiceImportService::class); $path = $this->makeXlsx([ ['EX-1', 'Existing', 'B1', 2, 50], ['NEW-A', 'New thing', 'B2', 3, 120], ]); $preview = $svc->preview($path, [ 'article_col' => 'B', 'name_col' => 'C', 'brand_col' => 'D', 'qty_col' => 'E', 'price_col' => 'F', 'header_row' => 1, ]); $purchase = $svc->import($this->supplier, $preview['rows'], createNew: true); $this->assertNotNull($purchase); $this->assertEquals(2, PurchaseItem::where('purchase_id', $purchase->id)->count()); // 2*50 + 3*120 = 460 $this->assertEqualsWithDelta(460.0, (float) $purchase->total, 0.01); // NEW-A should have been auto-created as a Part $this->assertNotNull(Part::where('article', 'NEW-A')->first()); unlink($path); } public function test_remember_mapping_persists_and_round_trips(): void { $mapping = ['article_col' => 'B', 'name_col' => 'C', 'qty_col' => 'E', 'price_col' => 'F', 'header_row' => 2]; $svc = app(ExcelInvoiceImportService::class); $svc->rememberMapping($this->supplier, $mapping, 'rossko-march.xlsx'); $loaded = $svc->rememberedMappingFor($this->supplier); $this->assertEquals('B', $loaded['article_col']); $this->assertEquals(2, $loaded['header_row']); // Update — should upsert, not duplicate $mapping['header_row'] = 3; $svc->rememberMapping($this->supplier, $mapping); $this->assertEquals(1, SupplierInvoiceMapping::where('supplier_id', $this->supplier->id)->count()); $this->assertEquals(3, $svc->rememberedMappingFor($this->supplier)['header_row']); } public function test_decimal_parser_tolerates_european_format(): void { $path = $this->makeXlsx([ ['EU-1', 'European', 'Brand', '2', '1 234,56'], ]); $preview = app(ExcelInvoiceImportService::class)->preview($path, [ 'article_col' => 'B', 'name_col' => 'C', 'qty_col' => 'E', 'price_col' => 'F', 'header_row' => 1, ]); $this->assertEqualsWithDelta(1234.56, $preview['rows'][0]['price'], 0.01); unlink($path); } }