Files
lis/htdocs/app/Services/SimbhpStockService.php
T

213 lines
6.5 KiB
PHP

<?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;
}
}