Perbarui cetak dan stok barcode
This commit is contained in:
@@ -4,10 +4,12 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use App\XFiles;
|
use App\XFiles;
|
||||||
use App\SIMBHPJenis;
|
use App\SIMBHPJenis;
|
||||||
use App\SIMBHPReport;
|
use App\SIMBHPReport;
|
||||||
use App\User;
|
use App\User;
|
||||||
|
use App\Services\SimbhpStockService;
|
||||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||||
use Validator;
|
use Validator;
|
||||||
@@ -25,7 +27,9 @@ class GudangController extends Controller
|
|||||||
$cdatane = SIMBHPJenis::all();
|
$cdatane = SIMBHPJenis::all();
|
||||||
$cjenis = count($cdatane);
|
$cjenis = count($cdatane);
|
||||||
if ($cjenis == 0){
|
if ($cjenis == 0){
|
||||||
|
$tasks['jjenis'][0]['id'] = null;
|
||||||
$tasks['jjenis'][0]['jenis'] = 'Belum Ada Jenis Barang';
|
$tasks['jjenis'][0]['jenis'] = 'Belum Ada Jenis Barang';
|
||||||
|
$tasks['jjenis'][0]['kodejenis'] = '';
|
||||||
$tasks['jjenis'][0]['satuan'] = '';
|
$tasks['jjenis'][0]['satuan'] = '';
|
||||||
$tasks['jjenis'][0]['satuan_kecil'] = '';
|
$tasks['jjenis'][0]['satuan_kecil'] = '';
|
||||||
$tasks['jjenis'][0]['konversi_kecil'] = 1;
|
$tasks['jjenis'][0]['konversi_kecil'] = 1;
|
||||||
@@ -33,7 +37,9 @@ class GudangController extends Controller
|
|||||||
} else {
|
} else {
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach($cdatane as $rdata){
|
foreach($cdatane as $rdata){
|
||||||
|
$tasks['jjenis'][$i]['id'] = $rdata->id;
|
||||||
$tasks['jjenis'][$i]['jenis'] = $rdata->jenis;
|
$tasks['jjenis'][$i]['jenis'] = $rdata->jenis;
|
||||||
|
$tasks['jjenis'][$i]['kodejenis'] = $rdata->kodejenis ?? '';
|
||||||
$tasks['jjenis'][$i]['satuan'] = $rdata->satuan;
|
$tasks['jjenis'][$i]['satuan'] = $rdata->satuan;
|
||||||
$tasks['jjenis'][$i]['satuan_kecil'] = $rdata->satuan_kecil ?? '';
|
$tasks['jjenis'][$i]['satuan_kecil'] = $rdata->satuan_kecil ?? '';
|
||||||
$tasks['jjenis'][$i]['konversi_kecil'] = $rdata->konversi_kecil ?? 1;
|
$tasks['jjenis'][$i]['konversi_kecil'] = $rdata->konversi_kecil ?? 1;
|
||||||
@@ -99,6 +105,63 @@ class GudangController extends Controller
|
|||||||
echo json_encode($arraysurat);
|
echo json_encode($arraysurat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function jsonSnapshot()
|
||||||
|
{
|
||||||
|
if (Session::get('previlage') == '') {
|
||||||
|
return response()->json(['message' => 'Unauthorized'], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
$jjenis = [];
|
||||||
|
$cdatane = SIMBHPJenis::orderBy('jenis', 'ASC')->get();
|
||||||
|
if ($cdatane->count() == 0) {
|
||||||
|
$jjenis[] = [
|
||||||
|
'id' => null,
|
||||||
|
'jenis' => 'Belum Ada Jenis Barang',
|
||||||
|
'kodejenis' => '',
|
||||||
|
'satuan' => '',
|
||||||
|
'satuan_kecil' => '',
|
||||||
|
'konversi_kecil' => 1,
|
||||||
|
'stok_minimum' => 0,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
foreach ($cdatane as $rdata) {
|
||||||
|
$jjenis[] = [
|
||||||
|
'id' => $rdata->id,
|
||||||
|
'jenis' => $rdata->jenis,
|
||||||
|
'kodejenis' => $rdata->kodejenis ?? '',
|
||||||
|
'satuan' => $rdata->satuan,
|
||||||
|
'satuan_kecil' => $rdata->satuan_kecil ?? '',
|
||||||
|
'konversi_kecil' => $rdata->konversi_kecil ?? 1,
|
||||||
|
'stok_minimum' => $rdata->stok_minimum ?? 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'jjenis' => $jjenis,
|
||||||
|
'jenisRows' => $this->getJenisRows(),
|
||||||
|
'warningstok' => $this->getLowStockWarnings(),
|
||||||
|
'expiringSoon' => $this->getExpiringSoonItems(),
|
||||||
|
'stat' => [
|
||||||
|
'harian' => [
|
||||||
|
'masuk' => $this->getAggregateBaseQty('pemasukan', 'harian'),
|
||||||
|
'keluar' => $this->getAggregateBaseQty('pengeluaran', 'harian'),
|
||||||
|
'perjenis' => $this->getUsagePerJenis('harian'),
|
||||||
|
],
|
||||||
|
'bulanan' => [
|
||||||
|
'masuk' => $this->getAggregateBaseQty('pemasukan', 'bulanan'),
|
||||||
|
'keluar' => $this->getAggregateBaseQty('pengeluaran', 'bulanan'),
|
||||||
|
'perjenis' => $this->getUsagePerJenis('bulanan'),
|
||||||
|
],
|
||||||
|
'tahunan' => [
|
||||||
|
'masuk' => $this->getAggregateBaseQty('pemasukan', 'tahunan'),
|
||||||
|
'keluar' => $this->getAggregateBaseQty('pengeluaran', 'tahunan'),
|
||||||
|
'perjenis' => $this->getUsagePerJenis('tahunan'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function jsonReportbhp(Request $request) {
|
public function jsonReportbhp(Request $request) {
|
||||||
$bulan = $request->input('val01');
|
$bulan = $request->input('val01');
|
||||||
$tahun = $request->input('val02');
|
$tahun = $request->input('val02');
|
||||||
@@ -293,7 +356,7 @@ class GudangController extends Controller
|
|||||||
$tanggal = $request->input('set03');
|
$tanggal = $request->input('set03');
|
||||||
$jumlah = $request->input('set04');
|
$jumlah = $request->input('set04');
|
||||||
$jenis = $request->input('set05');
|
$jenis = $request->input('set05');
|
||||||
$postujuan = $request->input('set06');
|
$postujuan = $request->input('set06'); // also used as kodejenis when $jenis == 'jenis'
|
||||||
$alasan = $request->input('set07');
|
$alasan = $request->input('set07');
|
||||||
$set08 = $request->input('set08');
|
$set08 = $request->input('set08');
|
||||||
$set09 = $request->input('set09');
|
$set09 = $request->input('set09');
|
||||||
@@ -310,6 +373,7 @@ class GudangController extends Controller
|
|||||||
$jenis = $request->input('set02');
|
$jenis = $request->input('set02');
|
||||||
$satuan = $request->input('set03');
|
$satuan = $request->input('set03');
|
||||||
$idne = $request->input('set01');
|
$idne = $request->input('set01');
|
||||||
|
$kodeInput = trim((string) $request->input('set06'));
|
||||||
$satuanKecil = trim((string) $set08);
|
$satuanKecil = trim((string) $set08);
|
||||||
$konversiKecil = (int) $set09;
|
$konversiKecil = (int) $set09;
|
||||||
if ($konversiKecil <= 0){ $konversiKecil = 1; }
|
if ($konversiKecil <= 0){ $konversiKecil = 1; }
|
||||||
@@ -320,7 +384,21 @@ class GudangController extends Controller
|
|||||||
} elseif ($konversiKecil <= 1) {
|
} elseif ($konversiKecil <= 1) {
|
||||||
return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Jika memakai satuan kecil, konversi harus lebih dari 1']);
|
return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Jika memakai satuan kecil, konversi harus lebih dari 1']);
|
||||||
}
|
}
|
||||||
$kodejenis = preg_replace('/\s+/', '', $jenis);
|
|
||||||
|
if ($kodeInput !== '') {
|
||||||
|
$kodejenis = strtoupper($kodeInput);
|
||||||
|
$kodejenis = preg_replace('/\s+/', '', $kodejenis);
|
||||||
|
$kodejenis = preg_replace('/[^A-Z0-9._-]/', '', $kodejenis);
|
||||||
|
} else {
|
||||||
|
$kodejenis = strtoupper((string) $jenis);
|
||||||
|
$kodejenis = preg_replace('/\s+/', '', $kodejenis);
|
||||||
|
$kodejenis = preg_replace('/[^A-Z0-9._-]/', '', $kodejenis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($kodejenis === '' || strlen($kodejenis) < 2 || strlen($kodejenis) > 40) {
|
||||||
|
return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Kode barang wajib (2-40 karakter). Gunakan huruf/angka dan simbol - _ .']);
|
||||||
|
}
|
||||||
|
|
||||||
if ($idne == 'new' OR $idne == ''){
|
if ($idne == 'new' OR $idne == ''){
|
||||||
$ceksudah = SIMBHPJenis::where('kodejenis', $kodejenis)->where('satuan', $satuan)->count();
|
$ceksudah = SIMBHPJenis::where('kodejenis', $kodejenis)->where('satuan', $satuan)->count();
|
||||||
if ($ceksudah != 0){
|
if ($ceksudah != 0){
|
||||||
@@ -335,6 +413,14 @@ class GudangController extends Controller
|
|||||||
'stok_minimum' => $stokMinimum,
|
'stok_minimum' => $stokMinimum,
|
||||||
]);
|
]);
|
||||||
if ($input){
|
if ($input){
|
||||||
|
if (Schema::hasColumn('simbhpjenis', 'barcode_besar')) {
|
||||||
|
$service = app(SimbhpStockService::class);
|
||||||
|
$setting = $service->getUnitSetting($input);
|
||||||
|
$input->update([
|
||||||
|
'barcode_besar' => $service->makeBarcodeValue((int) $input->id, 'besar'),
|
||||||
|
'barcode_kecil' => ($setting['has_breakdown'] ?? false) ? $service->makeBarcodeValue((int) $input->id, 'kecil') : null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
return response()->json(['status' => 'Success', 'message' => 'Data '.$jenis.' Sukses Ditambahkan']);
|
return response()->json(['status' => 'Success', 'message' => 'Data '.$jenis.' Sukses Ditambahkan']);
|
||||||
} else {
|
} else {
|
||||||
return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $jenis.' Gagal di masukkan, silahkan ulangi beberapa saat lagi']);
|
return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $jenis.' Gagal di masukkan, silahkan ulangi beberapa saat lagi']);
|
||||||
@@ -354,6 +440,16 @@ class GudangController extends Controller
|
|||||||
'stok_minimum' => $stokMinimum,
|
'stok_minimum' => $stokMinimum,
|
||||||
]);
|
]);
|
||||||
if ($input){
|
if ($input){
|
||||||
|
if (Schema::hasColumn('simbhpjenis', 'barcode_besar')) {
|
||||||
|
$row = SIMBHPJenis::find($idne);
|
||||||
|
if ($row) {
|
||||||
|
$service = app(SimbhpStockService::class);
|
||||||
|
$setting = $service->getUnitSetting($row);
|
||||||
|
$row->barcode_besar = $service->makeBarcodeValue((int) $row->id, 'besar');
|
||||||
|
$row->barcode_kecil = ($setting['has_breakdown'] ?? false) ? $service->makeBarcodeValue((int) $row->id, 'kecil') : null;
|
||||||
|
$row->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
return response()->json(['status' => 'Success', 'message' => 'Data '.$jenis.' Sukses Diupdate']);
|
return response()->json(['status' => 'Success', 'message' => 'Data '.$jenis.' Sukses Diupdate']);
|
||||||
} else {
|
} else {
|
||||||
return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $jenis.' Gagal di masukkan, silahkan ulangi beberapa saat lagi']);
|
return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $jenis.' Gagal di masukkan, silahkan ulangi beberapa saat lagi']);
|
||||||
@@ -794,6 +890,7 @@ class GudangController extends Controller
|
|||||||
if ($konversi <= 0) { $konversi = 1; }
|
if ($konversi <= 0) { $konversi = 1; }
|
||||||
$rows[] = [
|
$rows[] = [
|
||||||
'id' => $item->id,
|
'id' => $item->id,
|
||||||
|
'kodejenis' => $item->kodejenis ?? '',
|
||||||
'jenis' => $item->jenis,
|
'jenis' => $item->jenis,
|
||||||
'satuan' => $item->satuan,
|
'satuan' => $item->satuan,
|
||||||
'satuan_kecil' => $item->satuan_kecil ?? '',
|
'satuan_kecil' => $item->satuan_kecil ?? '',
|
||||||
|
|||||||
@@ -0,0 +1,342 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Services\SimbhpStockService;
|
||||||
|
use App\SIMBHPJenis;
|
||||||
|
use App\SIMBHPReport;
|
||||||
|
use App\User;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
|
use Livewire\WithPagination;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class GudangPos extends Component
|
||||||
|
{
|
||||||
|
use WithPagination;
|
||||||
|
|
||||||
|
public string $tanggal = '';
|
||||||
|
public ?int $penerimaId = null;
|
||||||
|
|
||||||
|
public string $scan = '';
|
||||||
|
public string $selectedKode = '';
|
||||||
|
public string $satuanTransaksi = 'besar';
|
||||||
|
public int $qty = 1;
|
||||||
|
|
||||||
|
public string $search = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<int, array{kode:string, jenis:string, qty:int, satuan_transaksi:string}>
|
||||||
|
*/
|
||||||
|
public array $cart = [];
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->tanggal = date('Y-m-d');
|
||||||
|
$this->penerimaId = User::orderBy('nama', 'ASC')->value('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatingSearch(): void
|
||||||
|
{
|
||||||
|
$this->resetPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scanLookup(): void
|
||||||
|
{
|
||||||
|
$service = app(SimbhpStockService::class);
|
||||||
|
$raw = (string) $this->scan;
|
||||||
|
$parsed = $service->parseBarcode($raw);
|
||||||
|
$kode = (string) ($parsed['kode'] ?? '');
|
||||||
|
$jenisId = $parsed['jenis_id'] ?? null;
|
||||||
|
$this->scan = '';
|
||||||
|
|
||||||
|
if ($kode === '' && (is_null($jenisId) || (int) $jenisId <= 0)) {
|
||||||
|
$this->toast('error', 'Kode barcode kosong.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$jenis = $service->getJenisByKode($raw);
|
||||||
|
if (!$jenis) {
|
||||||
|
$this->toast('error', 'Kode barang tidak ditemukan.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->selectedKode = (string) ($jenis->kodejenis ?? '');
|
||||||
|
$this->qty = 1;
|
||||||
|
$this->satuanTransaksi = (string) ($parsed['satuan_transaksi'] ?? 'besar');
|
||||||
|
$this->syncSatuanAvailability();
|
||||||
|
$this->dispatch('gudangpos-open-modal');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[On('gudangpos-select')]
|
||||||
|
public function selectProduct(string $kode): void
|
||||||
|
{
|
||||||
|
$service = app(SimbhpStockService::class);
|
||||||
|
$kode = $service->sanitizeKode($kode);
|
||||||
|
$jenis = $service->getJenisByKode($kode);
|
||||||
|
|
||||||
|
if (!$jenis) {
|
||||||
|
$this->toast('error', 'Kode barang tidak ditemukan: ' . $kode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->selectedKode = (string) ($jenis->kodejenis ?? '');
|
||||||
|
$this->qty = 1;
|
||||||
|
$this->syncSatuanAvailability();
|
||||||
|
$this->dispatch('gudangpos-open-modal');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[On('gudangpos-refresh')]
|
||||||
|
public function refreshComponent(): void
|
||||||
|
{
|
||||||
|
// Force rerender + keep UI responsive when stok berubah dari tab lain.
|
||||||
|
$this->resetPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSelected(): void
|
||||||
|
{
|
||||||
|
$service = app(SimbhpStockService::class);
|
||||||
|
$kode = $service->sanitizeKode($this->selectedKode);
|
||||||
|
if ($kode === '') {
|
||||||
|
$this->toast('error', 'Pilih barang dulu (scan barcode / klik daftar produk).');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$jenis = $service->getJenisByKode($kode);
|
||||||
|
if (!$jenis) {
|
||||||
|
$this->toast('error', 'Kode barang tidak ditemukan: ' . $kode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$qty = (int) $this->qty;
|
||||||
|
if ($qty <= 0) {
|
||||||
|
$this->toast('error', 'Jumlah harus lebih dari 0.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$setting = $service->getUnitSetting($jenis);
|
||||||
|
if ($this->satuanTransaksi === 'kecil' && !$setting['has_breakdown']) {
|
||||||
|
$this->satuanTransaksi = 'besar';
|
||||||
|
$this->toast('info', 'Barang ini tidak memiliki satuan kecil, otomatis pakai satuan besar.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$found = false;
|
||||||
|
foreach ($this->cart as $i => $line) {
|
||||||
|
if (($line['kode'] ?? '') === $kode && ($line['satuan_transaksi'] ?? '') === $this->satuanTransaksi) {
|
||||||
|
$this->cart[$i]['qty'] = ((int) ($this->cart[$i]['qty'] ?? 0)) + $qty;
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$found) {
|
||||||
|
$this->cart[] = [
|
||||||
|
'kode' => $kode,
|
||||||
|
'jenis' => (string) ($jenis->jenis ?? ''),
|
||||||
|
'qty' => $qty,
|
||||||
|
'satuan_transaksi' => $this->satuanTransaksi,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->qty = 1;
|
||||||
|
$this->dispatch('gudangpos-close-modal');
|
||||||
|
$this->dispatch('gudangpos-focus', field: 'scan');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeLine(int $index): void
|
||||||
|
{
|
||||||
|
if (!isset($this->cart[$index])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
array_splice($this->cart, $index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processCart(): void
|
||||||
|
{
|
||||||
|
if (count($this->cart) === 0) {
|
||||||
|
$this->toast('info', 'Keranjang masih kosong.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->penerimaId) {
|
||||||
|
$this->toast('error', 'Penerima wajib dipilih.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tanggal = trim((string) $this->tanggal);
|
||||||
|
if ($tanggal === '') {
|
||||||
|
$this->toast('error', 'Tanggal wajib diisi.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$date = Carbon::parse($tanggal);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->toast('error', 'Format tanggal tidak valid.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$penerima = User::select('id', 'nama')->find($this->penerimaId);
|
||||||
|
if (!$penerima) {
|
||||||
|
$this->toast('error', 'Penerima tidak ditemukan.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$service = app(SimbhpStockService::class);
|
||||||
|
|
||||||
|
$prepared = [];
|
||||||
|
$stockCache = [];
|
||||||
|
foreach ($this->cart as $line) {
|
||||||
|
$kode = $service->sanitizeKode((string) ($line['kode'] ?? ''));
|
||||||
|
$qty = (int) ($line['qty'] ?? 0);
|
||||||
|
$satuan = (string) ($line['satuan_transaksi'] ?? 'besar');
|
||||||
|
if ($kode === '' || $qty <= 0) {
|
||||||
|
$this->toast('error', 'Ada item keranjang yang tidak valid.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$jenis = $service->getJenisByKode($kode);
|
||||||
|
if (!$jenis) {
|
||||||
|
$this->toast('error', 'Kode barang tidak ditemukan: ' . $kode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$setting = $service->getUnitSetting($jenis);
|
||||||
|
if ($satuan === 'kecil' && !$setting['has_breakdown']) {
|
||||||
|
$satuan = 'besar';
|
||||||
|
}
|
||||||
|
|
||||||
|
$qtyBase = $service->calculateBaseQty($jenis, $qty, $satuan);
|
||||||
|
$jenisNama = (string) ($jenis->jenis ?? '');
|
||||||
|
if (!array_key_exists($jenisNama, $stockCache)) {
|
||||||
|
$stockCache[$jenisNama] = $service->getStockBaseByJenis($jenisNama);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($qtyBase > $stockCache[$jenisNama]) {
|
||||||
|
$this->toast('error', 'Stok tidak cukup untuk ' . $jenisNama . '.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$stockCache[$jenisNama] -= $qtyBase;
|
||||||
|
|
||||||
|
$prepared[] = [
|
||||||
|
'jenis' => $jenis,
|
||||||
|
'qty' => $qty,
|
||||||
|
'qty_base' => $qtyBase,
|
||||||
|
'satuan_transaksi' => $satuan,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::transaction(function () use ($prepared, $date, $penerima) {
|
||||||
|
foreach ($prepared as $row) {
|
||||||
|
/** @var SIMBHPJenis $jenis */
|
||||||
|
$jenis = $row['jenis'];
|
||||||
|
SIMBHPReport::create([
|
||||||
|
'tanggal' => (int) $date->format('d'),
|
||||||
|
'bulan' => (int) $date->format('m'),
|
||||||
|
'tahun' => (int) $date->format('Y'),
|
||||||
|
'deskripsi' => 'Diterima oleh ' . ($penerima->nama ?? 'Unkown'),
|
||||||
|
'pemasukan' => null,
|
||||||
|
'pengeluaran' => (int) $row['qty'],
|
||||||
|
'qty_base' => (int) $row['qty_base'],
|
||||||
|
'satuan_transaksi' => (string) $row['satuan_transaksi'],
|
||||||
|
'masa_expired' => null,
|
||||||
|
'jenis' => (string) ($jenis->jenis ?? ''),
|
||||||
|
'keterangan' => '',
|
||||||
|
'marking' => '',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->cart = [];
|
||||||
|
$this->qty = 1;
|
||||||
|
$this->toast('success', 'Barang keluar berhasil diproses.');
|
||||||
|
$this->dispatch('gudang-refresh');
|
||||||
|
$this->dispatch('gudangpos-close-modal');
|
||||||
|
$this->dispatch('gudangpos-focus', field: 'scan');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedSelectedKode(): void
|
||||||
|
{
|
||||||
|
$this->syncSatuanAvailability();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedSatuanTransaksi(): void
|
||||||
|
{
|
||||||
|
$this->syncSatuanAvailability();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncSatuanAvailability(): void
|
||||||
|
{
|
||||||
|
$service = app(SimbhpStockService::class);
|
||||||
|
$kode = $service->sanitizeKode($this->selectedKode);
|
||||||
|
if ($kode === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$jenis = $service->getJenisByKode($kode);
|
||||||
|
if (!$jenis) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$setting = $service->getUnitSetting($jenis);
|
||||||
|
if (!$setting['has_breakdown'] && $this->satuanTransaksi === 'kecil') {
|
||||||
|
$this->satuanTransaksi = 'besar';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function toast(string $type, string $message): void
|
||||||
|
{
|
||||||
|
$this->dispatch('gudangpos-toast', type: $type, message: $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$service = app(SimbhpStockService::class);
|
||||||
|
|
||||||
|
$users = User::select('id', 'nama', 'previlage')->orderBy('nama', 'ASC')->get();
|
||||||
|
|
||||||
|
$query = SIMBHPJenis::query()->orderBy('jenis', 'ASC');
|
||||||
|
$search = trim((string) $this->search);
|
||||||
|
if ($search !== '') {
|
||||||
|
$searchLike = '%' . $search . '%';
|
||||||
|
$query->where(function ($q) use ($searchLike) {
|
||||||
|
$q->where('jenis', 'LIKE', $searchLike)
|
||||||
|
->orWhere('kodejenis', 'LIKE', $searchLike);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$products = $query->paginate(12);
|
||||||
|
|
||||||
|
$jenisNames = $products->getCollection()->pluck('jenis')->filter()->values()->all();
|
||||||
|
$stockMap = $service->getStockBaseByJenisMany($jenisNames);
|
||||||
|
|
||||||
|
$selectedJenis = null;
|
||||||
|
$selectedStockDisplay = null;
|
||||||
|
$selectedSetting = null;
|
||||||
|
$kode = $service->sanitizeKode($this->selectedKode);
|
||||||
|
if ($kode !== '') {
|
||||||
|
$selectedJenis = $service->getJenisByKode($kode);
|
||||||
|
if ($selectedJenis) {
|
||||||
|
$saldoBase = $service->getStockBaseByJenis((string) $selectedJenis->jenis);
|
||||||
|
$selectedSetting = $service->getUnitSetting($selectedJenis);
|
||||||
|
$selectedStockDisplay = $service->formatStockDisplay(
|
||||||
|
$saldoBase,
|
||||||
|
(string) ($selectedJenis->satuan ?? ''),
|
||||||
|
(string) ($selectedJenis->satuan_kecil ?? ''),
|
||||||
|
(int) ($selectedJenis->konversi_kecil ?? 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('livewire.gudang-pos', [
|
||||||
|
'users' => $users,
|
||||||
|
'products' => $products,
|
||||||
|
'stockMap' => $stockMap,
|
||||||
|
'selectedJenis' => $selectedJenis,
|
||||||
|
'selectedStockDisplay' => $selectedStockDisplay,
|
||||||
|
'selectedSetting' => $selectedSetting,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\SIMBHPJenis;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class SimbhpStockService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Barcode format:
|
||||||
|
* - MAIN (satuan besar): 91{ID(8-digit)}1 (example: 91000001231)
|
||||||
|
* - BREAKDOWN (satuan kecil): 91{ID(8-digit)}2 (example: 91000001232)
|
||||||
|
*
|
||||||
|
* Backward compatibility:
|
||||||
|
* - {KODE}-B / {KODE}-K
|
||||||
|
* - plain {KODE} treated as "besar"
|
||||||
|
*
|
||||||
|
* @return array{kode:string, jenis_id:int|null, satuan_transaksi:string, barcode_value:string}
|
||||||
|
*/
|
||||||
|
public function parseBarcode(string $raw): array
|
||||||
|
{
|
||||||
|
$raw = $this->sanitizeKode($raw);
|
||||||
|
if ($raw === '') {
|
||||||
|
return [
|
||||||
|
'kode' => '',
|
||||||
|
'jenis_id' => null,
|
||||||
|
'satuan_transaksi' => 'besar',
|
||||||
|
'barcode_value' => '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^91(\d{8})([12])$/', $raw, $m)) {
|
||||||
|
$id = (int) ltrim((string) $m[1], '0');
|
||||||
|
$unitDigit = (string) $m[2];
|
||||||
|
|
||||||
|
return [
|
||||||
|
'kode' => '',
|
||||||
|
'jenis_id' => $id > 0 ? $id : null,
|
||||||
|
'satuan_transaksi' => ($unitDigit === '2') ? 'kecil' : 'besar',
|
||||||
|
'barcode_value' => $raw,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^(.*)-(B|K)$/', $raw, $m)) {
|
||||||
|
$base = $this->sanitizeKode((string) $m[1]);
|
||||||
|
$suffix = (string) $m[2];
|
||||||
|
return [
|
||||||
|
'kode' => $base,
|
||||||
|
'jenis_id' => null,
|
||||||
|
'satuan_transaksi' => ($suffix === 'K') ? 'kecil' : 'besar',
|
||||||
|
'barcode_value' => $base . '-' . $suffix,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'kode' => $raw,
|
||||||
|
'jenis_id' => null,
|
||||||
|
'satuan_transaksi' => 'besar',
|
||||||
|
'barcode_value' => $raw,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sanitizeKode(string $kode): string
|
||||||
|
{
|
||||||
|
$kode = strtoupper(trim($kode));
|
||||||
|
$kode = preg_replace('/\s+/', '', $kode);
|
||||||
|
$kode = preg_replace('/[^A-Z0-9._-]/', '', $kode);
|
||||||
|
|
||||||
|
return substr($kode, 0, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJenisByKode(string $kode): ?SIMBHPJenis
|
||||||
|
{
|
||||||
|
$parsed = $this->parseBarcode($kode);
|
||||||
|
$jenisId = $parsed['jenis_id'] ?? null;
|
||||||
|
if (!is_null($jenisId) && (int) $jenisId > 0) {
|
||||||
|
return SIMBHPJenis::where('id', (int) $jenisId)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
$kode = $parsed['kode'] ?? '';
|
||||||
|
if ($kode === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SIMBHPJenis::where('kodejenis', $kode)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeBarcodeValue(int $jenisId, string $satuanTransaksi = 'besar'): string
|
||||||
|
{
|
||||||
|
if ($jenisId <= 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$idPart = str_pad((string) $jenisId, 8, '0', STR_PAD_LEFT);
|
||||||
|
$unitDigit = ($satuanTransaksi === 'kecil') ? '2' : '1';
|
||||||
|
|
||||||
|
return '91' . $idPart . $unitDigit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{has_breakdown: bool, konversi: int, satuan_kecil: string}
|
||||||
|
*/
|
||||||
|
public function getUnitSetting(SIMBHPJenis $jenis): array
|
||||||
|
{
|
||||||
|
$satuanKecil = trim((string) ($jenis->satuan_kecil ?? ''));
|
||||||
|
$konversi = (int) ($jenis->konversi_kecil ?? 1);
|
||||||
|
if ($konversi <= 0) {
|
||||||
|
$konversi = 1;
|
||||||
|
}
|
||||||
|
$hasBreakdown = ($satuanKecil !== '' && $konversi > 1);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'has_breakdown' => $hasBreakdown,
|
||||||
|
'konversi' => $konversi,
|
||||||
|
'satuan_kecil' => $satuanKecil,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculateBaseQty(SIMBHPJenis $jenis, int $qtyInput, string $satuanTransaksi = 'besar'): int
|
||||||
|
{
|
||||||
|
$qtyInput = (int) $qtyInput;
|
||||||
|
if ($qtyInput < 0) {
|
||||||
|
$qtyInput = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$setting = $this->getUnitSetting($jenis);
|
||||||
|
$konversi = (int) ($setting['konversi'] ?? 1);
|
||||||
|
if ($konversi <= 0) {
|
||||||
|
$konversi = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($satuanTransaksi === 'kecil') {
|
||||||
|
return $qtyInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qtyInput * $konversi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStockBaseByJenis(string $jenisNama): int
|
||||||
|
{
|
||||||
|
$jenisNama = trim((string) $jenisNama);
|
||||||
|
if ($jenisNama === '') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = DB::table('simbhpreport')
|
||||||
|
->where('jenis', $jenisNama)
|
||||||
|
->selectRaw("SUM(CASE WHEN pemasukan IS NOT NULL AND pemasukan > 0 THEN COALESCE(qty_base, pemasukan) ELSE 0 END) AS masuk_base")
|
||||||
|
->selectRaw("SUM(CASE WHEN pengeluaran IS NOT NULL AND pengeluaran > 0 THEN COALESCE(qty_base, pengeluaran) ELSE 0 END) AS keluar_base")
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$masuk = (int) ($row->masuk_base ?? 0);
|
||||||
|
$keluar = (int) ($row->keluar_base ?? 0);
|
||||||
|
|
||||||
|
return $masuk - $keluar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, string> $jenisNames
|
||||||
|
* @return array<string, int> map jenis => saldo_base
|
||||||
|
*/
|
||||||
|
public function getStockBaseByJenisMany(array $jenisNames): array
|
||||||
|
{
|
||||||
|
$jenisNames = array_values(array_filter(array_map(function ($v) {
|
||||||
|
return trim((string) $v);
|
||||||
|
}, $jenisNames)));
|
||||||
|
|
||||||
|
if (count($jenisNames) === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = DB::table('simbhpreport')
|
||||||
|
->whereIn('jenis', $jenisNames)
|
||||||
|
->select('jenis')
|
||||||
|
->selectRaw("SUM(CASE WHEN pemasukan IS NOT NULL AND pemasukan > 0 THEN COALESCE(qty_base, pemasukan) ELSE 0 END) AS masuk_base")
|
||||||
|
->selectRaw("SUM(CASE WHEN pengeluaran IS NOT NULL AND pengeluaran > 0 THEN COALESCE(qty_base, pengeluaran) ELSE 0 END) AS keluar_base")
|
||||||
|
->groupBy('jenis')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$map = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$jenis = (string) ($row->jenis ?? '');
|
||||||
|
if ($jenis === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$map[$jenis] = ((int) ($row->masuk_base ?? 0)) - ((int) ($row->keluar_base ?? 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($jenisNames as $jenis) {
|
||||||
|
if (!array_key_exists($jenis, $map)) {
|
||||||
|
$map[$jenis] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function formatStockDisplay(int $saldoBase, string $satuanBesar, string $satuanKecil, int $konversi): string
|
||||||
|
{
|
||||||
|
$saldoBase = (int) $saldoBase;
|
||||||
|
$konversi = (int) $konversi;
|
||||||
|
if ($konversi <= 1 || trim($satuanKecil) === '') {
|
||||||
|
return number_format($saldoBase, 0, '.', ',') . ' ' . $satuanBesar;
|
||||||
|
}
|
||||||
|
|
||||||
|
$besar = intdiv($saldoBase, $konversi);
|
||||||
|
$kecil = $saldoBase % $konversi;
|
||||||
|
|
||||||
|
return number_format($besar, 0, '.', ',') . ' ' . $satuanBesar . ' + ' . number_format($kecil, 0, '.', ',') . ' ' . $satuanKecil;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
"laravel/passport": "^11.10",
|
"laravel/passport": "^11.10",
|
||||||
"laravel/sanctum": "^3.3",
|
"laravel/sanctum": "^3.3",
|
||||||
"laravel/tinker": "^2.8",
|
"laravel/tinker": "^2.8",
|
||||||
|
"livewire/livewire": "^3.5",
|
||||||
"onecentlin/laravel-adminer": "^7.0",
|
"onecentlin/laravel-adminer": "^7.0",
|
||||||
"opcodesio/log-viewer": "^3.21",
|
"opcodesio/log-viewer": "^3.21",
|
||||||
"phpoffice/phpspreadsheet": "^3.3",
|
"phpoffice/phpspreadsheet": "^3.3",
|
||||||
|
|||||||
Generated
+78
-2
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "22c48f025e8e682420fe5c8bb1eb55e3",
|
"content-hash": "8a29deb08f7a54f984f29f89ce4209d4",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "aamdsam/bridging-bpjs",
|
"name": "aamdsam/bridging-bpjs",
|
||||||
@@ -2995,6 +2995,82 @@
|
|||||||
],
|
],
|
||||||
"time": "2026-01-15T06:54:53+00:00"
|
"time": "2026-01-15T06:54:53+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "livewire/livewire",
|
||||||
|
"version": "v3.7.11",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/livewire/livewire.git",
|
||||||
|
"reference": "addd6e8e9234df75f29e6a327ee2a745a7d67bb6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/livewire/livewire/zipball/addd6e8e9234df75f29e6a327ee2a745a7d67bb6",
|
||||||
|
"reference": "addd6e8e9234df75f29e6a327ee2a745a7d67bb6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/database": "^10.0|^11.0|^12.0|^13.0",
|
||||||
|
"illuminate/routing": "^10.0|^11.0|^12.0|^13.0",
|
||||||
|
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
|
||||||
|
"illuminate/validation": "^10.0|^11.0|^12.0|^13.0",
|
||||||
|
"laravel/prompts": "^0.1.24|^0.2|^0.3",
|
||||||
|
"league/mime-type-detection": "^1.9",
|
||||||
|
"php": "^8.1",
|
||||||
|
"symfony/console": "^6.0|^7.0|^8.0",
|
||||||
|
"symfony/http-kernel": "^6.2|^7.0|^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"calebporzio/sushi": "^2.1",
|
||||||
|
"laravel/framework": "^10.15.0|^11.0|^12.0|^13.0",
|
||||||
|
"mockery/mockery": "^1.3.1",
|
||||||
|
"orchestra/testbench": "^8.21.0|^9.0|^10.0|^11.0",
|
||||||
|
"orchestra/testbench-dusk": "^8.24|^9.1|^10.0|^11.0",
|
||||||
|
"phpunit/phpunit": "^10.4|^11.5|^12.5",
|
||||||
|
"psy/psysh": "^0.11.22|^0.12"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"aliases": {
|
||||||
|
"Livewire": "Livewire\\Livewire"
|
||||||
|
},
|
||||||
|
"providers": [
|
||||||
|
"Livewire\\LivewireServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/helpers.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Livewire\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Caleb Porzio",
|
||||||
|
"email": "[email protected]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A front-end framework for Laravel.",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/livewire/livewire/issues",
|
||||||
|
"source": "https://github.com/livewire/livewire/tree/v3.7.11"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/livewire",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-02-26T00:58:19+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "maennchen/zipstream-php",
|
"name": "maennchen/zipstream-php",
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
@@ -10951,5 +11027,5 @@
|
|||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "8.4"
|
"php": "8.4"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.6.0"
|
"plugin-api-version": "2.9.0"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('simbhpjenis', function (Blueprint $table) {
|
||||||
|
if (!Schema::hasColumn('simbhpjenis', 'barcode_besar')) {
|
||||||
|
$table->string('barcode_besar', 20)->nullable()->after('kodejenis');
|
||||||
|
}
|
||||||
|
if (!Schema::hasColumn('simbhpjenis', 'barcode_kecil')) {
|
||||||
|
$table->string('barcode_kecil', 20)->nullable()->after('barcode_besar');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!Schema::hasColumn('simbhpjenis', 'barcode_besar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::table('simbhpjenis')
|
||||||
|
->orderBy('id', 'ASC')
|
||||||
|
->chunkById(200, function ($rows) {
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$id = (int) ($row->id ?? 0);
|
||||||
|
if ($id <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$idPart = str_pad((string) $id, 8, '0', STR_PAD_LEFT);
|
||||||
|
$barcodeBesar = '91' . $idPart . '1';
|
||||||
|
|
||||||
|
$existingBesar = isset($row->barcode_besar) ? trim((string) $row->barcode_besar) : '';
|
||||||
|
$existingKecil = isset($row->barcode_kecil) ? trim((string) $row->barcode_kecil) : '';
|
||||||
|
|
||||||
|
$satuanKecil = isset($row->satuan_kecil) ? trim((string) $row->satuan_kecil) : '';
|
||||||
|
$konversi = (int) ($row->konversi_kecil ?? 1);
|
||||||
|
if ($konversi <= 0) {
|
||||||
|
$konversi = 1;
|
||||||
|
}
|
||||||
|
$hasBreakdown = ($satuanKecil !== '' && $konversi > 1);
|
||||||
|
|
||||||
|
$update = [];
|
||||||
|
if ($existingBesar === '') {
|
||||||
|
$update['barcode_besar'] = $barcodeBesar;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasBreakdown) {
|
||||||
|
$barcodeKecil = '91' . $idPart . '2';
|
||||||
|
if ($existingKecil === '') {
|
||||||
|
$update['barcode_kecil'] = $barcodeKecil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($update) > 0) {
|
||||||
|
DB::table('simbhpjenis')->where('id', $id)->update($update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('simbhpjenis', function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn('simbhpjenis', 'barcode_kecil')) {
|
||||||
|
$table->dropColumn('barcode_kecil');
|
||||||
|
}
|
||||||
|
if (Schema::hasColumn('simbhpjenis', 'barcode_besar')) {
|
||||||
|
$table->dropColumn('barcode_besar');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,10 @@
|
|||||||
<meta content="{{ config('global.namaapps') }}" name="description" />
|
<meta content="{{ config('global.namaapps') }}" name="description" />
|
||||||
<meta content="{{ config('global.domainapps') }}" name="author" />
|
<meta content="{{ config('global.domainapps') }}" name="author" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
<link rel="shortcut icon" href="{{ asset('favicon.ico') }}">
|
<link rel="shortcut icon" href="{{ asset('favicon.ico') }}">
|
||||||
@include('base.partials.css-plain')
|
@include('base.partials.css-plain')
|
||||||
|
@livewireStyles
|
||||||
@stack('styles')
|
@stack('styles')
|
||||||
<!--
|
<!--
|
||||||
Programmer : Dwi Swandhana
|
Programmer : Dwi Swandhana
|
||||||
@@ -30,6 +32,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
@include('base.partials.js-plain')
|
@include('base.partials.js-plain')
|
||||||
|
@livewireScripts
|
||||||
@stack('script')
|
@stack('script')
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -96,12 +96,11 @@
|
|||||||
<li><a href="/mikro/buku0">Selainnya</a></li>
|
<li><a href="/mikro/buku0">Selainnya</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/report"><i class="fa fa-hospital-o"></i>Report</a></li>
|
|
||||||
<li><a href="/biorepository"><i class="fa fa-archive"></i>Biorepository</a></li>
|
|
||||||
<li><a href="/pengambilan"><i class="fa fa-stethoscope"></i>Pengambilan</a></li>
|
|
||||||
<li class="has-submenu">
|
<li class="has-submenu">
|
||||||
<a href="#"><i class="fa fa-user-md"></i>Logbook</a>
|
<a href="#"><i class="fa fa-user-md"></i>Report</a>
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
|
<li><a href="/pengambilan">Pengambilan</a></li>
|
||||||
|
<li><a href="/report">Report</a></li>
|
||||||
<li><a href="/logbookmenu">Logbook Menu</a></li>
|
<li><a href="/logbookmenu">Logbook Menu</a></li>
|
||||||
<li><a href="/logbookstatistik">Report and Statistik</a></li>
|
<li><a href="/logbookstatistik">Report and Statistik</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -115,6 +114,7 @@
|
|||||||
<li><a href="/user">User Management</a></li>
|
<li><a href="/user">User Management</a></li>
|
||||||
<li><a href="/template">Expertise Template</a></li>
|
<li><a href="/template">Expertise Template</a></li>
|
||||||
<li><a href="/sirab">Database Antibiotik</a></li>
|
<li><a href="/sirab">Database Antibiotik</a></li>
|
||||||
|
<li><a href="/biorepository">Biorepository</a></li>
|
||||||
<li><a href="/gudang">Gudang</a></li>
|
<li><a href="/gudang">Gudang</a></li>
|
||||||
<li><a href="/backup">Backup</a></li>
|
<li><a href="/backup">Backup</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -143,11 +143,11 @@
|
|||||||
<li><a href="/mikro/buku0">Selainnya</a></li>
|
<li><a href="/mikro/buku0">Selainnya</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/report"><i class="fa fa-hospital-o"></i>Report</a></li>
|
|
||||||
<li><a href="/pengambilan"><i class="fa fa-stethoscope"></i>Pengambilan</a></li>
|
|
||||||
<li class="has-submenu">
|
<li class="has-submenu">
|
||||||
<a href="#"><i class="fa fa-user-md"></i>Logbook</a>
|
<a href="#"><i class="fa fa-user-md"></i>Report</a>
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
|
<li><a href="/pengambilan">Pengambilan</a></li>
|
||||||
|
<li><a href="/report">Report</a></li>
|
||||||
<li><a href="/logbookmenu">Logbook Menu</a></li>
|
<li><a href="/logbookmenu">Logbook Menu</a></li>
|
||||||
<li><a href="/logbookstatistik">Report and Statistik</a></li>
|
<li><a href="/logbookstatistik">Report and Statistik</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -187,12 +187,17 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/pendaftaran"><i class="fa fa-h-square"></i>Registration</a></li>
|
<li><a href="/pendaftaran"><i class="fa fa-h-square"></i>Registration</a></li>
|
||||||
<li><a href="/poli"><i class="fa fa-hospital-o"></i>Room and Request List</a></li>
|
<li class="has-submenu">
|
||||||
<li><a href="/report"><i class="fa fa-hospital-o"></i>Report</a></li>
|
<a href="#"><i class="fa fa-user-md"></i>Report</a>
|
||||||
<li><a href="/pengambilan"><i class="fa fa-stethoscope"></i>Pengambilan</a></li>
|
<ul class="submenu">
|
||||||
|
<li><a href="/pengambilan">Pengambilan</a></li>
|
||||||
|
<li><a href="/report">Report</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
<li class="has-submenu">
|
<li class="has-submenu">
|
||||||
<a href="#"><i class="fa fa-medkit"></i>Settings</a>
|
<a href="#"><i class="fa fa-medkit"></i>Settings</a>
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
|
<li><a href="/poli">Room and Request List</a></li>
|
||||||
<li><a href="/pasien">Patient</a></li>
|
<li><a href="/pasien">Patient</a></li>
|
||||||
<li><a href="/template">Database</a></li>
|
<li><a href="/template">Database</a></li>
|
||||||
<li><a href="/user">User Management</a></li>
|
<li><a href="/user">User Management</a></li>
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
<div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-7">
|
||||||
|
<div class="card-box">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-7">
|
||||||
|
<h4 class="m-t-0">List Barang</h4>
|
||||||
|
<small class="text-muted">Scan barcode di atas list, atau klik item untuk input qty/satuan.</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<input type="text" class="form-control" placeholder="Cari kode / nama..." wire:model.live.debounce.300ms="search">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="m-t-10">
|
||||||
|
<input id="gudangpos_scan" type="text" class="form-control"
|
||||||
|
placeholder="Scan barcode lalu Enter"
|
||||||
|
wire:model.live="scan"
|
||||||
|
wire:keydown.enter.prevent="scanLookup">
|
||||||
|
</div>
|
||||||
|
<hr class="m-t-10 m-b-10">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
@forelse($products as $p)
|
||||||
|
@php
|
||||||
|
$saldoBase = (int) ($stockMap[$p->jenis] ?? 0);
|
||||||
|
$warning = $saldoBase <= (int) ($p->stok_minimum ?? 0);
|
||||||
|
@endphp
|
||||||
|
<div class="col-lg-4 col-md-6">
|
||||||
|
<div class="card-box" style="border:1px solid #eee; cursor:pointer;" wire:click="selectProduct('{{ $p->kodejenis }}')">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div>
|
||||||
|
<div><b>{{ $p->jenis }}</b></div>
|
||||||
|
<div><span class="badge badge-success">{{ $p->kodejenis }}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
@if($warning)
|
||||||
|
<span class="badge badge-danger">LOW</span>
|
||||||
|
@else
|
||||||
|
<span class="badge badge-info">OK</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="m-t-10">
|
||||||
|
<small class="text-muted">Stok (base): {{ number_format($saldoBase, 0, '.', ',') }}</small>
|
||||||
|
</div>
|
||||||
|
<div class="m-t-5">
|
||||||
|
<small class="text-muted">Satuan: {{ $p->satuan }} @if(($p->satuan_kecil ?? '') !== '') / {{ $p->satuan_kecil }} @endif</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@empty
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="alert alert-warning m-b-0">Produk tidak ditemukan.</div>
|
||||||
|
</div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-t-10">
|
||||||
|
{{ $products->links('pagination::bootstrap-4') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-5">
|
||||||
|
<div class="card-box">
|
||||||
|
<h4 class="m-t-0">Keranjang</h4>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Tanggal</label>
|
||||||
|
<input type="date" class="form-control" wire:model.live="tanggal">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Penerima</label>
|
||||||
|
<select class="form-control" wire:model.live="penerimaId">
|
||||||
|
@foreach($users as $u)
|
||||||
|
<option value="{{ $u->id }}">{{ $u->nama }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-striped m-b-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 110px">Kode</th>
|
||||||
|
<th>Barang</th>
|
||||||
|
<th style="width: 90px">Qty</th>
|
||||||
|
<th style="width: 80px">Sat</th>
|
||||||
|
<th style="width: 60px">Aksi</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse($cart as $i => $line)
|
||||||
|
<tr>
|
||||||
|
<td class="text-center"><code>{{ $line['kode'] }}</code></td>
|
||||||
|
<td>{{ $line['jenis'] }}</td>
|
||||||
|
<td class="text-right">{{ number_format((int) ($line['qty'] ?? 0), 0, '.', ',') }}</td>
|
||||||
|
<td class="text-center">{{ strtoupper($line['satuan_transaksi'] ?? '-') }}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button type="button" class="btn btn-sm btn-danger" wire:click="removeLine({{ $i }})">X</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr><td colspan="5" class="text-center text-muted">Keranjang masih kosong.</td></tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-t-10">
|
||||||
|
<button type="button" class="btn btn-custom btn-block" wire:click="processCart" @if(count($cart)===0) disabled @endif>Proses Barang Keluar</button>
|
||||||
|
</div>
|
||||||
|
<small class="text-muted d-block m-t-10">Tidak ada pembayaran; hanya penerima barang.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div wire:ignore.self class="modal fade" id="gudangposModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Input Qty & Satuan</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Kode</label>
|
||||||
|
<input type="text" class="form-control" wire:model.live="selectedKode" readonly>
|
||||||
|
</div>
|
||||||
|
@if($selectedJenis)
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<div><b>{{ $selectedJenis->jenis }}</b></div>
|
||||||
|
<div>Stok: <b>{{ $selectedStockDisplay }}</b></div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Satuan</label>
|
||||||
|
<select class="form-control" wire:model.live="satuanTransaksi">
|
||||||
|
<option value="besar">Satuan Besar</option>
|
||||||
|
<option value="kecil" @if(!($selectedSetting['has_breakdown'] ?? false)) disabled @endif>Satuan Kecil (Pecah Satuan)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group m-b-0">
|
||||||
|
<label>Jumlah</label>
|
||||||
|
<input id="gudangpos_qty" type="number" min="1" class="form-control" wire:model.live="qty" wire:keydown.enter.prevent="addSelected">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
||||||
|
<button type="button" class="btn btn-success" wire:click="addSelected">Tambah</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@once
|
||||||
|
@push('script')
|
||||||
|
<script>
|
||||||
|
document.addEventListener('livewire:init', () => {
|
||||||
|
Livewire.on('gudangpos-toast', (e) => {
|
||||||
|
const type = (e.type || 'info').toString();
|
||||||
|
const icon = (type === 'success') ? 'success' : (type === 'error' ? 'error' : 'info');
|
||||||
|
const loaderBg = (type === 'success') ? '#5ba035' : (type === 'error' ? '#bf441d' : '#3b98b5');
|
||||||
|
if (window.$ && $.toast) {
|
||||||
|
$.toast({
|
||||||
|
heading: type.toUpperCase(),
|
||||||
|
text: e.message || '',
|
||||||
|
position: 'top-right',
|
||||||
|
loaderBg,
|
||||||
|
icon,
|
||||||
|
hideAfter: 3500,
|
||||||
|
stack: 1
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alert(e.message || '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Livewire.on('gudangpos-focus', (e) => {
|
||||||
|
const field = (e.field || '').toString();
|
||||||
|
const id = (field === 'qty') ? 'gudangpos_qty' : 'gudangpos_scan';
|
||||||
|
setTimeout(() => {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (el) el.focus();
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
Livewire.on('gudangpos-open-modal', () => {
|
||||||
|
if (window.$) {
|
||||||
|
$('#gudangposModal').modal('show');
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
const el = document.getElementById('gudangpos_qty');
|
||||||
|
if (el) el.focus();
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
|
||||||
|
Livewire.on('gudangpos-close-modal', () => {
|
||||||
|
if (window.$) {
|
||||||
|
$('#gudangposModal').modal('hide');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
@endonce
|
||||||
|
</div>
|
||||||
@@ -76,6 +76,7 @@ Route::group(['middleware' => 'project.ipg'], function() {
|
|||||||
Route::post('biorepository/delete-rack/{id}', [BiorepositoryController::class, 'deleteRack'])->name('biorepository.deleteRack');
|
Route::post('biorepository/delete-rack/{id}', [BiorepositoryController::class, 'deleteRack'])->name('biorepository.deleteRack');
|
||||||
Route::post('biorepository/delete-cabinet/{id}', [BiorepositoryController::class, 'deleteCabinet'])->name('biorepository.deleteCabinet');
|
Route::post('biorepository/delete-cabinet/{id}', [BiorepositoryController::class, 'deleteCabinet'])->name('biorepository.deleteCabinet');
|
||||||
Route::post('simbhp/exaddbarang', [GudangController::class, 'exAddbarang'])->name('exAddBarang');
|
Route::post('simbhp/exaddbarang', [GudangController::class, 'exAddbarang'])->name('exAddBarang');
|
||||||
|
Route::get('simbhp/snapshot', [GudangController::class, 'jsonSnapshot'])->name('simbhp.snapshot');
|
||||||
Route::post('simbhp/reportbhp', [GudangController::class, 'jsonReportbhp'])->name('reportBHP');
|
Route::post('simbhp/reportbhp', [GudangController::class, 'jsonReportbhp'])->name('reportBHP');
|
||||||
Route::get('simbhp/reportbhp/export', [GudangController::class, 'exportReportbhp'])->name('reportBHPExport');
|
Route::get('simbhp/reportbhp/export', [GudangController::class, 'exportReportbhp'])->name('reportBHPExport');
|
||||||
Route::post('simbhp/kwitansi', [GudangController::class, 'exKwitansi'])->name('kwitansiBHP');
|
Route::post('simbhp/kwitansi', [GudangController::class, 'exKwitansi'])->name('kwitansiBHP');
|
||||||
|
|||||||
Reference in New Issue
Block a user