create export product stock dealers
This commit is contained in:
201
app/Exports/ProductStockDealers.php
Normal file
201
app/Exports/ProductStockDealers.php
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\Dealer;
|
||||||
|
use App\Models\Product;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithTitle;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithStyles;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithColumnWidths;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||||
|
|
||||||
|
class ProductStockDealers implements WithMultipleSheets
|
||||||
|
{
|
||||||
|
public function sheets(): array
|
||||||
|
{
|
||||||
|
$sheets = [];
|
||||||
|
$usedNames = [];
|
||||||
|
|
||||||
|
// Get all dealers with their stock data
|
||||||
|
$dealers = Dealer::with(['stocks.product.category'])->get();
|
||||||
|
|
||||||
|
/** @var Dealer $dealer */
|
||||||
|
foreach ($dealers as $dealer) {
|
||||||
|
$dealerSheet = new DealerStockSheet($dealer);
|
||||||
|
$sheetTitle = $dealerSheet->title();
|
||||||
|
|
||||||
|
// Handle duplicate sheet names
|
||||||
|
$originalTitle = $sheetTitle;
|
||||||
|
$counter = 1;
|
||||||
|
while (in_array($sheetTitle, $usedNames)) {
|
||||||
|
$sheetTitle = substr($originalTitle, 0, 28) . '_' . $counter;
|
||||||
|
$counter++;
|
||||||
|
}
|
||||||
|
$usedNames[] = $sheetTitle;
|
||||||
|
|
||||||
|
// Set the unique title
|
||||||
|
$dealerSheet->setUniqueTitle($sheetTitle);
|
||||||
|
$sheets[] = $dealerSheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sheets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DealerStockSheet implements FromCollection, WithTitle, WithHeadings, WithStyles, WithColumnWidths
|
||||||
|
{
|
||||||
|
protected $dealer;
|
||||||
|
protected $uniqueTitle;
|
||||||
|
|
||||||
|
public function __construct(Dealer $dealer)
|
||||||
|
{
|
||||||
|
$this->dealer = $dealer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function collection()
|
||||||
|
{
|
||||||
|
// Get all products with stock for this dealer
|
||||||
|
$stocks = $this->dealer->stocks()
|
||||||
|
->with(['product.category'])
|
||||||
|
->whereHas('product', function($query) {
|
||||||
|
$query->where('active', true);
|
||||||
|
})
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$data = collect();
|
||||||
|
$no = 1;
|
||||||
|
|
||||||
|
foreach ($stocks as $stock) {
|
||||||
|
$product = $stock->product;
|
||||||
|
$data->push([
|
||||||
|
'no' => $no++,
|
||||||
|
'kode_produk' => $product->code,
|
||||||
|
'nama_produk' => $product->name,
|
||||||
|
'kategori' => $product->category ? $product->category->name : '-',
|
||||||
|
'satuan' => $product->unit ?? '-',
|
||||||
|
'stok' => number_format($stock->quantity, 2)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no stock, add empty row
|
||||||
|
if ($data->isEmpty()) {
|
||||||
|
$data->push([
|
||||||
|
'no' => '-',
|
||||||
|
'kode_produk' => '-',
|
||||||
|
'nama_produk' => 'Tidak ada stok produk',
|
||||||
|
'kategori' => '-',
|
||||||
|
'satuan' => '-',
|
||||||
|
'stok' => '0'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUniqueTitle(string $title): void
|
||||||
|
{
|
||||||
|
$this->uniqueTitle = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function title(): string
|
||||||
|
{
|
||||||
|
if (isset($this->uniqueTitle)) {
|
||||||
|
return $this->uniqueTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean dealer name for sheet title (remove invalid characters and handle edge cases)
|
||||||
|
$cleanName = $this->dealer->name;
|
||||||
|
|
||||||
|
// Remove parentheses and their contents
|
||||||
|
$cleanName = preg_replace('/\([^)]*\)/', '', $cleanName);
|
||||||
|
|
||||||
|
// Remove dots, commas, and other special characters
|
||||||
|
$cleanName = preg_replace('/[^A-Za-z0-9\-_ ]/', '', $cleanName);
|
||||||
|
|
||||||
|
// Clean up multiple spaces and trim
|
||||||
|
$cleanName = preg_replace('/\s+/', ' ', trim($cleanName));
|
||||||
|
|
||||||
|
// If name is empty after cleaning, use dealer ID
|
||||||
|
if (empty($cleanName)) {
|
||||||
|
$cleanName = 'Dealer_' . $this->dealer->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to 31 characters and ensure no leading/trailing spaces
|
||||||
|
$cleanName = trim(substr($cleanName, 0, 31));
|
||||||
|
|
||||||
|
// Ensure it doesn't end with a space (which can cause Excel issues)
|
||||||
|
return rtrim($cleanName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'No',
|
||||||
|
'Kode Produk',
|
||||||
|
'Nama Produk',
|
||||||
|
'Kategori',
|
||||||
|
'Satuan',
|
||||||
|
'Stok'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function styles(Worksheet $sheet)
|
||||||
|
{
|
||||||
|
// Add dealer info at the top first
|
||||||
|
$sheet->insertNewRowBefore(1, 2);
|
||||||
|
$sheet->setCellValue('A1', 'STOK PRODUK DEALER: ' . strtoupper($this->dealer->name));
|
||||||
|
$sheet->setCellValue('A2', 'Tanggal Export: ' . now()->format('d/m/Y H:i:s'));
|
||||||
|
|
||||||
|
// Merge cells for dealer info
|
||||||
|
$sheet->mergeCells('A1:F1');
|
||||||
|
$sheet->mergeCells('A2:F2');
|
||||||
|
|
||||||
|
$lastRow = $sheet->getHighestRow();
|
||||||
|
|
||||||
|
// Style dealer info
|
||||||
|
$sheet->getStyle('A1:A2')->applyFromArray([
|
||||||
|
'font' => ['bold' => true, 'size' => 12],
|
||||||
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Style headers (row 3 after inserting 2 rows)
|
||||||
|
$sheet->getStyle('A3:F3')->applyFromArray([
|
||||||
|
'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']],
|
||||||
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '4472C4']],
|
||||||
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
|
||||||
|
'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Style data rows if they exist
|
||||||
|
if ($lastRow > 3) {
|
||||||
|
$sheet->getStyle('A4:F' . $lastRow)->applyFromArray([
|
||||||
|
'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => 'CCCCCC']]],
|
||||||
|
'alignment' => ['vertical' => Alignment::VERTICAL_CENTER]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Center align specific columns
|
||||||
|
$sheet->getStyle('A4:A' . $lastRow)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||||
|
$sheet->getStyle('E4:F' . $lastRow)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function columnWidths(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'A' => 8, // No
|
||||||
|
'B' => 15, // Kode Produk
|
||||||
|
'C' => 30, // Nama Produk
|
||||||
|
'D' => 20, // Kategori
|
||||||
|
'E' => 12, // Satuan
|
||||||
|
'F' => 12 // Stok
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ use App\Models\Dealer;
|
|||||||
use App\Models\Menu;
|
use App\Models\Menu;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\Models\ProductCategory;
|
use App\Models\ProductCategory;
|
||||||
|
use App\Exports\ProductStockDealers;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@@ -15,6 +16,7 @@ use Illuminate\Support\Facades\Gate;
|
|||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Yajra\DataTables\Facades\DataTables;
|
use Yajra\DataTables\Facades\DataTables;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
|
|
||||||
class ProductsController extends Controller
|
class ProductsController extends Controller
|
||||||
{
|
{
|
||||||
@@ -270,4 +272,16 @@ class ProductsController extends Controller
|
|||||||
|
|
||||||
return DataTables::of($data)->make(true);
|
return DataTables::of($data)->make(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function exportDealersStock()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$fileName = 'stok_produk_dealers_' . date('Y-m-d_H-i-s') . '.xlsx';
|
||||||
|
|
||||||
|
return Excel::download(new ProductStockDealers(), $fileName);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Export dealers stock error: ' . $e->getMessage());
|
||||||
|
return back()->with('error', 'Gagal mengexport data. Silakan coba lagi.');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,15 +81,18 @@ table.dataTable thead th.sorting:hover:before {
|
|||||||
Tabel Produk
|
Tabel Produk
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@can('create', $menus['products.index'])
|
|
||||||
<div class="kt-portlet__head-toolbar">
|
<div class="kt-portlet__head-toolbar">
|
||||||
<div class="kt-portlet__head-wrapper">
|
<div class="kt-portlet__head-wrapper">
|
||||||
<div class="kt-portlet__head-actions">
|
<div class="kt-portlet__head-actions">
|
||||||
|
<a href="{{ route('products.export.dealers_stock') }}" class="btn btn-bold btn-success btn--sm" style="margin-right: 8px;">
|
||||||
|
<i class="flaticon2-download"></i>Export Stok Dealer
|
||||||
|
</a>
|
||||||
|
@can('create', $menus['products.index'])
|
||||||
<a href="{{ route('products.create') }}" class="btn btn-bold btn-label-brand btn--sm">Tambah</a>
|
<a href="{{ route('products.create') }}" class="btn btn-bold btn-label-brand btn--sm">Tambah</a>
|
||||||
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endcan
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="kt-portlet__body">
|
<div class="kt-portlet__body">
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ Route::group(['middleware' => 'auth'], function() {
|
|||||||
Route::post('/', 'store')->name('products.store');
|
Route::post('/', 'store')->name('products.store');
|
||||||
Route::get('all','all_products')->name('products.all');
|
Route::get('all','all_products')->name('products.all');
|
||||||
Route::get('dealers-stock', 'dealers_stock')->name('products.dealers_stock');
|
Route::get('dealers-stock', 'dealers_stock')->name('products.dealers_stock');
|
||||||
|
Route::get('export/dealers-stock', 'exportDealersStock')->name('products.export.dealers_stock');
|
||||||
Route::get('{product}', 'show')->name('products.show');
|
Route::get('{product}', 'show')->name('products.show');
|
||||||
Route::get('{product}/edit', 'edit')->name('products.edit');
|
Route::get('{product}/edit', 'edit')->name('products.edit');
|
||||||
Route::put('{product}', 'update')->name('products.update');
|
Route::put('{product}', 'update')->name('products.update');
|
||||||
|
|||||||
Reference in New Issue
Block a user