first commit
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
/**
|
||||
* Immutable-ish configuration bag built once at bootstrap from
|
||||
* config.php defaults + .env overlay (see mergeEnv).
|
||||
*
|
||||
* Inject this class into controllers/middleware/models.
|
||||
* Do not read $_ENV outside bootstrap/env merge.
|
||||
*/
|
||||
|
||||
final class Config {
|
||||
|
||||
/** @var array<string,mixed> */
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* Explicit map of environment variable name => dotted config path.
|
||||
* Env wins when the variable is set and non-empty.
|
||||
*
|
||||
* @var array<string,string>
|
||||
*/
|
||||
public const ENV_MAP = [
|
||||
'APP_ENV' => 'app.env',
|
||||
'APP_DEBUG' => 'app.debug',
|
||||
'FLIGHT_BASE_URL' => 'app.base_url',
|
||||
'APP_TIMEZONE' => 'app.timezone',
|
||||
'DB_DRIVER' => 'database.driver',
|
||||
'DB_HOST' => 'database.host',
|
||||
'DB_DATABASE' => 'database.dbname',
|
||||
'DB_USERNAME' => 'database.user',
|
||||
'DB_PASSWORD' => 'database.password',
|
||||
'DB_SQLITE_PATH' => 'database.file_path',
|
||||
'DB_SOCKET' => 'database.socket',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $data Merged configuration array
|
||||
*/
|
||||
public function __construct(array $data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value by dotted key path (e.g. "database.host").
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key, $default = null) {
|
||||
$segments = explode('.', $key);
|
||||
$value = $this->data;
|
||||
foreach ($segments as $segment) {
|
||||
if (!is_array($value) || !array_key_exists($segment, $value)) {
|
||||
return $default;
|
||||
}
|
||||
$value = $value[$segment];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function all(): array {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function isDebug(): bool {
|
||||
$debug = $this->get('app.debug', false);
|
||||
if (is_bool($debug)) {
|
||||
return $debug;
|
||||
}
|
||||
if (is_string($debug)) {
|
||||
return in_array(strtolower($debug), ['1', 'true', 'yes', 'on'], true);
|
||||
}
|
||||
return (bool) $debug;
|
||||
}
|
||||
|
||||
public function env(): string {
|
||||
return (string) $this->get('app.env', 'production');
|
||||
}
|
||||
|
||||
/**
|
||||
* Application base URL with a trailing slash so templates can
|
||||
* safely join paths: {{ base_url }}posts → /myapp/posts
|
||||
*/
|
||||
public function baseUrl(): string {
|
||||
$url = (string) $this->get('app.base_url', '/');
|
||||
if ($url === '') return '/';
|
||||
return rtrim($url, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Overlay mapped environment variables onto file config.
|
||||
* Env wins when the variable is set and the string is non-empty.
|
||||
*
|
||||
* @param array<string,mixed> $fileConfig From config.php (literals only)
|
||||
* @param array<string,mixed> $env Typically $_ENV after loadEnv
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function mergeEnv(array $fileConfig, array $env): array {
|
||||
$merged = $fileConfig;
|
||||
foreach (self::ENV_MAP as $envKey => $configPath) {
|
||||
if (!array_key_exists($envKey, $env)) {
|
||||
continue;
|
||||
}
|
||||
$raw = $env[$envKey];
|
||||
if ($raw === null || $raw === '') {
|
||||
continue;
|
||||
}
|
||||
$value = self::castEnvValue($configPath, (string) $raw);
|
||||
self::setPath($merged, $configPath, $value);
|
||||
}
|
||||
return $merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param string $raw
|
||||
* @return mixed
|
||||
*/
|
||||
private static function castEnvValue(string $path, string $raw) {
|
||||
if ($path === 'app.debug') {
|
||||
return in_array(strtolower($raw), ['1', 'true', 'yes', 'on'], true);
|
||||
}
|
||||
return $raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $data
|
||||
* @param string $path Dotted path
|
||||
* @param mixed $value
|
||||
*/
|
||||
private static function setPath(array &$data, string $path, $value): void {
|
||||
$segments = explode('.', $path);
|
||||
$ref = &$data;
|
||||
foreach ($segments as $i => $segment) {
|
||||
if ($i === count($segments) - 1) {
|
||||
$ref[$segment] = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($ref[$segment]) || !is_array($ref[$segment])) {
|
||||
$ref[$segment] = [];
|
||||
}
|
||||
$ref = &$ref[$segment];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use flight\database\SimplePdo;
|
||||
use InvalidArgumentException;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Builds a SimplePdo connection from Config.
|
||||
* Shared helper for bootstrap services and CLI (migrate).
|
||||
*/
|
||||
final class DBFactory {
|
||||
/**
|
||||
* Whether database is configured (driver non-empty).
|
||||
*/
|
||||
public static function isEnabled(Config $config): bool {
|
||||
$driver = (string) $config->get('database.driver', '');
|
||||
return $driver !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SimplePdo instance from config.
|
||||
*
|
||||
* @throws InvalidArgumentException When driver is empty or unsupported
|
||||
*/
|
||||
public static function create(Config $config): SimplePdo {
|
||||
$driver = strtolower((string) $config->get('database.driver', ''));
|
||||
|
||||
if ($driver === '') {
|
||||
throw new InvalidArgumentException(
|
||||
'Database is not configured. Set database.driver in config.php or DB_DRIVER in .env'
|
||||
);
|
||||
}
|
||||
|
||||
$dsn = self::buildDsn($config, $driver);
|
||||
$user = $config->get('database.user');
|
||||
$password = $config->get('database.password');
|
||||
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
|
||||
return new SimplePdo(
|
||||
$dsn,
|
||||
$user !== null && $user !== '' ? (string) $user : null,
|
||||
$password !== null ? (string) $password : null,
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
private static function buildDsn(Config $config, string $driver): string {
|
||||
if ($driver === 'sqlite') {
|
||||
$path = (string) $config->get('database.file_path', '');
|
||||
if ($path === '') {
|
||||
throw new InvalidArgumentException('database.file_path is required for sqlite');
|
||||
}
|
||||
|
||||
$dir = dirname($path);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0775, true);
|
||||
}
|
||||
|
||||
return 'sqlite:' . $path;
|
||||
}
|
||||
|
||||
if ($driver === 'mysql') {
|
||||
$host = (string) $config->get('database.host', 'localhost');
|
||||
$dbname = (string) $config->get('database.dbname', '');
|
||||
$charset = (string) $config->get('database.charset', 'utf8mb4');
|
||||
$socket = (string) $config->get('database.socket', '');
|
||||
if (!empty($socket)) return 'mysql:unix_socket=' . $socket . ';dbname=' . $dbname . ';charset=' . $charset;
|
||||
return 'mysql:host=' . $host . ';dbname=' . $dbname . ';charset=' . $charset;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Unsupported database driver: ' . $driver);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
/**
|
||||
* Minimal first-party .env loader. Only used at bootstrap.
|
||||
* Controllers/middleware/models must not call this or read $_ENV.
|
||||
*/
|
||||
final class Env
|
||||
{
|
||||
/**
|
||||
* Load KEY=VALUE pairs from a .env file into putenv / $_ENV / $_SERVER
|
||||
* without overwriting variables already present in the real environment.
|
||||
*/
|
||||
public static function load(string $path): void
|
||||
{
|
||||
if (!is_file($path) || !is_readable($path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lines = file($path, FILE_IGNORE_NEW_LINES);
|
||||
if ($lines === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
|
||||
if ($line === '' || strpos($line, '#') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Support optional "export KEY=..."
|
||||
if (strpos($line, 'export ') === 0) {
|
||||
$line = trim(substr($line, 7));
|
||||
}
|
||||
|
||||
$eqPos = strpos($line, '=');
|
||||
if ($eqPos === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = trim(substr($line, 0, $eqPos));
|
||||
$value = trim(substr($line, $eqPos + 1));
|
||||
|
||||
if ($name === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = self::stripQuotes($value);
|
||||
|
||||
// Real environment wins over .env file
|
||||
$existing = getenv($name);
|
||||
if ($existing !== false && $existing !== '') {
|
||||
if (!array_key_exists($name, $_ENV)) {
|
||||
$_ENV[$name] = $existing;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
putenv($name . '=' . $value);
|
||||
$_ENV[$name] = $value;
|
||||
$_SERVER[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
private static function stripQuotes(string $value): string
|
||||
{
|
||||
$len = strlen($value);
|
||||
if ($len < 2) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$first = $value[0];
|
||||
$last = $value[$len - 1];
|
||||
|
||||
if (($first === '"' && $last === '"') || ($first === "'" && $last === "'")) {
|
||||
return substr($value, 1, -1);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
<?php
|
||||
|
||||
/*******************************************************************
|
||||
|
||||
PHP Class : /Utils/Gel
|
||||
Description : Class khusus untuk Gel / Pembukuan
|
||||
Created By : Siau Pheng ([email protected]).
|
||||
Created Date : February 10th, 2012.
|
||||
Last Modified : October 24th, 2026.
|
||||
|
||||
(c) 3FONIA Software; WWW.3FONIA.COM.
|
||||
|
||||
*******************************************************************/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use flight\Engine;
|
||||
use flight\database\SimplePdo;
|
||||
use Webapp\Web\Param;
|
||||
|
||||
final class Gel {
|
||||
|
||||
private int $id_server;
|
||||
private int $id_cab;
|
||||
private string $rek_shu;
|
||||
protected Engine $app;
|
||||
protected SimplePdo $db;
|
||||
|
||||
public function __construct(Engine $app) {
|
||||
$this->app = $app;
|
||||
$this->db = $app->db();
|
||||
$this->id_server = (int) $app->session()->get('WEB_APP')['SERVER_ID'];
|
||||
$this->id_cab = (int) $app->session()->get('APP_INV')['CABANGID'];
|
||||
$this->rek_shu = (string) $app->session()->get('APP_GEL')['SHU'];
|
||||
}
|
||||
|
||||
public function Jurnal_Baru(int $cab=0, string $tgl="", bool $save=false): string {
|
||||
if (empty($tgl)) {
|
||||
$tno = "G" . date("y") . abjad((int) date("n"));
|
||||
} else {
|
||||
$tmp = explode("-", $tgl);
|
||||
$tno = "G" . substr((string) $tmp[2],2,2) . abjad((int) $tmp[1]);
|
||||
}
|
||||
$tno .= $this->id_server . $cab;
|
||||
$ino = (int) $this->db->fetchField("SELECT MAX(RIGHT(Kode_Jurnal,5)) FROM gel_jurnal WHERE Kode_Jurnal LIKE ?", [$tno.'%']) ?? 0;
|
||||
$tno .= isi_nol(++$ino, 5);
|
||||
if ($save === true) $this->db->runQuery("INSERT INTO gel_jurnal (Kode_Jurnal) VALUES(?)", [$tno]);
|
||||
return $tno;
|
||||
}
|
||||
|
||||
public function Jurnal_Post(array $ajurnal = [], array $adetail = []): string {
|
||||
if (count($ajurnal) === 0) return '';
|
||||
foreach ($ajurnal as $_key => $_val) {
|
||||
if (is_int($_key)) {
|
||||
unset($ajurnal[$_key]);
|
||||
$ajurnal[$_val] = Param::Get($_val);
|
||||
}
|
||||
}
|
||||
$ajurnal['CabID'] ??= $this->id_cab;
|
||||
$ajurnal['Tanggal'] ??= tgl_dmy();
|
||||
$ajurnal['Kode_Jurnal'] ??= $this->Jurnal_Baru((int) $ajurnal['CabID'], $ajurnal['Tanggal']);
|
||||
|
||||
$afield = []; $avalue = []; $aupdate = []; $aholder = array_fill(0, count($ajurnal), '?');
|
||||
foreach ($ajurnal as $_key => $_val) {
|
||||
$afield[] = $_key;
|
||||
$avalue[] = (in_array($_key, ["Tanggal"]) ? tgl_sql2str($_val) : $_val);
|
||||
if ($_key == "Kode_Jurnal") continue;
|
||||
$aupdate[] = $_key . '=VALUES('. $_key . ')';
|
||||
}
|
||||
array_push($afield, "DateStamp", "UserID");
|
||||
array_push($aholder, 'NOW()', '?');
|
||||
array_push($aupdate, 'DateStamp=VALUES(DateStamp)', 'UserID=VALUES(UserID)');
|
||||
array_push($avalue, $this->app->user()->id());
|
||||
$this->db->runQuery("INSERT INTO gel_jurnal (".implode(",",$afield).") VALUES (".implode(",",$aholder).") ON DUPLICATE KEY UPDATE ".implode(",",$aupdate), $avalue);
|
||||
|
||||
if (count($adetail) === 0) return $ajurnal['Kode_Jurnal'];
|
||||
$aper = $this->Periode($ajurnal['Tanggal']);
|
||||
$this->db->delete("gel_jurnal_detail", "Kode_Jurnal=?", [$ajurnal['Kode_Jurnal']]);
|
||||
foreach ($adetail as $_key1 => $_detail) {
|
||||
if (empty($_detail['Kode_Rek']) || (float) $_detail['Jumlah'] === 0) continue;
|
||||
$ainsert = [];
|
||||
foreach ($_detail as $_key2 => $_val) {
|
||||
$ainsert[$_key2] = (in_array($_key2, ["Jumlah"]) ? (abs($_val) * (((string) $_detail['DK']==="K") ? -1 : 1)) : $_val);
|
||||
}
|
||||
$ainsert = array_merge($ainsert, ['Kode_Jurnal'=>$ajurnal['Kode_Jurnal'], 'TipeID'=>$ajurnal['TipeID'], 'Tgl_Ref'=>tgl_sql2str($ajurnal['Tanggal']), 'Tahun'=>$aper['Tahun'], 'Bulan'=>$aper['Bulan'], 'CabID'=>$ajurnal['CabID'], 'UserID'=>$this->app->user()->id()]);
|
||||
$this->db->insert("gel_jurnal_detail", $ainsert);
|
||||
}
|
||||
return $ajurnal['Kode_Jurnal'];
|
||||
}
|
||||
|
||||
public function Jurnal_Save(string $kode_jurnal, int $tipe, int $cab, string $tgl, ?string $no_ref, ?string $ket): string {
|
||||
$tgl = (empty($tgl)) ? tgl_dmy() : $tgl;
|
||||
$kode_jurnal = empty($kode_jurnal) ? $this->Jurnal_Baru($cab, $tgl) : $kode_jurnal;
|
||||
|
||||
$afield = [ 'Kode_Jurnal', 'TipeID', 'CabID', 'Tanggal', 'Kode_Ref', 'Catatan', 'DateStamp', 'UserID' ];
|
||||
$aholder = [ '?', '?', '?', '?', '?', '?', 'NOW()', '?' ];
|
||||
$avalue = [ $kode_jurnal, $tipe, $cab, tgl_sql2str($tgl), (string) $no_ref, (string) $ket, $this->app->user()->id() ];
|
||||
$aupdate = array_map(fn($fi) => $fi . '=VALUES(' .$fi. ')', array_diff($afield, ['Kode_Jurnal']));
|
||||
|
||||
$this->db->runQuery("INSERT INTO gel_jurnal (".implode(",",$afield).") VALUES (".implode(",",$aholder).") ON DUPLICATE KEY UPDATE ".implode(",",$aupdate), $avalue);
|
||||
return $kode_jurnal;
|
||||
}
|
||||
|
||||
public function Detail_Copy(string $kode_jurnal): void {
|
||||
if (empty($kode_jurnal)) return;
|
||||
$this->Detail_Cancel($kode_jurnal);
|
||||
$this->db->runQuery("INSERT INTO tmp_gel_jurnal_detail (Kode_Jurnal,TipeID,Tgl_Ref,Tahun,Bulan,Kode_Rek,MemberID,SupID,CabID,DK,Jumlah,UserID,Unix) SELECT Kode_Jurnal,TipeID,Tgl_Ref,Tahun,Bulan,Kode_Rek,MemberID,SupID,CabID,DK,Jumlah,UserID,'".$this->app->user()->unix()."' FROM gel_jurnal_detail WHERE Kode_Jurnal='".$kode_jurnal."'");
|
||||
}
|
||||
|
||||
public function Detail_Save(string $kode_jurnal, int $tipe=0, int $cab=0, string $tgl="", bool $baru=false, string $kode_lama=""): void {
|
||||
if (empty($kode_jurnal)) return;
|
||||
$aper = $this->Periode($tgl);
|
||||
$this->db->delete("gel_jurnal_detail", "Kode_Jurnal=? OR Kode_Jurnal=?", [$kode_jurnal, $kode_lama]);
|
||||
if ($baru === true) {
|
||||
$this->Detail_Cancel($kode_jurnal);
|
||||
$this->db->update("tmp_gel_jurnal_detail", ['Kode_Jurnal'=>$kode_jurnal], "Kode_Jurnal=? AND Unix=?", [$kode_lama, $this->app->user()->unix()]);
|
||||
}
|
||||
$this->db->runQuery("INSERT INTO gel_jurnal_detail (Kode_Jurnal,TipeID,Tgl_Ref,Tahun,Bulan,Kode_Rek,MemberID,SupID,CabID,DK,Jumlah,UserID) SELECT Kode_Jurnal,?,'".tgl_sql2str($tgl)."','".$aper['Tahun']."','".$aper['Bulan']."',Kode_Rek,MemberID,SupID,?,DK,Jumlah,UserID FROM tmp_gel_jurnal_detail WHERE Kode_Jurnal=?", [$tipe, $cab, $kode_jurnal]);
|
||||
$this->Detail_Cancel($kode_jurnal);
|
||||
}
|
||||
|
||||
public function Detail_Cancel(string $kode_jurnal): void {
|
||||
if (empty($kode_jurnal)) return;
|
||||
$this->db->delete("tmp_gel_jurnal_detail", "Kode_Jurnal=?", [$kode_jurnal]);
|
||||
}
|
||||
|
||||
public function JurnalSelisih(array $ajurnal=[]): float {
|
||||
$saldo = 0;
|
||||
foreach ($ajurnal as $jurnal) {
|
||||
$saldo += (($jurnal[1]==="D") ? 1 : -1) * abs($jurnal[2]);
|
||||
}
|
||||
return $saldo;
|
||||
}
|
||||
|
||||
public function Saldo(string $tgl="", int $mod=1, int $level=3, string $tipe="N"): array {
|
||||
//mod=1:akum,2:bln | tipe=N:Neraca,R:Rugilaba
|
||||
$aper = $this->Periode($tgl);
|
||||
|
||||
// generate sql script, for accumulate monthly rekap
|
||||
$tjml = "R.Muta_".isi_nol($aper["Bulan"], 2);
|
||||
if ($mod === 1) {
|
||||
$tjml = "R.Open";
|
||||
for ($i=1; $i<=$aper["Bulan"]; $i++) {
|
||||
$tjml .= "+R.Muta_".isi_nol($i, 2);
|
||||
}
|
||||
}
|
||||
$tsql = " AND (Grup_RekID BETWEEN ". (($tipe==="N") ? "1 AND 3" : "4 AND 5") .") ";
|
||||
|
||||
// ambil rekening yang sesuai batas level
|
||||
$arek = $this->db->fetchAll("SELECT A.*, ".$tjml." Jumlah FROM (SELECT * FROM gel_acc WHERE Level<=?".$tsql.") A LEFT JOIN gel_rekap R ON (A.Kode_Rek=R.Kode_Rek AND R.Tahun='".$aper['Tahun']."') ORDER BY A.Kode_Rek", [$level]);
|
||||
|
||||
// ambil rekening diluar batas level laporan dan mode group
|
||||
$arek2 = $this->db->fetchAll("SELECT *,0 Jumlah FROM gel_acc WHERE Level>? AND Mode='G'".$tsql, [$level]);
|
||||
|
||||
// ambil rekap diluar batas level laporan dan mode detail
|
||||
$aluar = $this->db->fetchAll("SELECT A.Kode_Induk Kode_Rek,SUM(".$tjml.") Jumlah FROM (SELECT * FROM gel_acc WHERE Level>? AND Mode='D'".$tsql.") A LEFT JOIN gel_rekap R ON (A.Kode_Rek=R.Kode_Rek AND R.Tahun='".$aper['Tahun']."') GROUP BY A.Kode_Induk", [$level]);
|
||||
foreach ($aluar as $row) {
|
||||
//cek batas luar
|
||||
$ba = false;
|
||||
foreach ($arek2 as $key => $val) {
|
||||
if ($val['Kode_Rek'] == $row['Kode_Rek']) {
|
||||
$ba = true;
|
||||
$arek2[$key]['Jumlah'] += $row['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ba === false) {
|
||||
foreach ($arek as $key => $val) {
|
||||
if ($val['Kode_Rek'] == $row['Kode_Rek']) {
|
||||
$ba = true;
|
||||
$arek[$key]['Jumlah'] += $row['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($ba === false) echo $row['Kode_Rek']; //debug
|
||||
}
|
||||
|
||||
// merger rekap luar level pada pilihan level 2
|
||||
foreach ($arek2 as $key2 => $val2) {
|
||||
$ba = false;
|
||||
foreach ($arek as $key => $val) {
|
||||
if ($val2['Kode_Induk'] == $val['Kode_Rek']) {
|
||||
$ba = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ba === false) {
|
||||
foreach ($arek2 as $key3 => $val3) {
|
||||
if ($val2['Kode_Induk'] == $val3['Kode_Rek']) {
|
||||
$arek2[$key3]['Jumlah'] += $val2['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// merger rekap luar level kedalam batas level
|
||||
foreach ($arek2 as $key2 => $val2) {
|
||||
foreach ($arek as $key => $val) {
|
||||
if ($val2['Kode_Induk'] == $val['Kode_Rek']) {
|
||||
$arek[$key]['Jumlah'] += $val2['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($tipe === "N") {
|
||||
// rekap pendapatan + biaya kedalam rekening SHU tahun berjalan
|
||||
$dshu = $this->db->fetchField("SELECT SUM(".$tjml.") Jumlah FROM gel_rekap R LEFT JOIN gel_acc A ON (A.Kode_Rek=R.Kode_Rek) WHERE R.Tahun='".$aper['Tahun']."' AND (A.Grup_RekID BETWEEN 4 AND 5)") ?? 0;
|
||||
foreach ($arek as $key => $val) {
|
||||
if ($val['Kode_Rek'] == $this->rek_shu) {
|
||||
$arek[$key]['Jumlah'] += $dshu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rekap saldo dari level besar ke level 2
|
||||
for ($nlev=$level; $nlev>1; $nlev--) {
|
||||
foreach ($arek as $key => $val) {
|
||||
if ($val['Level'] == $nlev) {
|
||||
foreach ($arek as $key2 => $val2) {
|
||||
if ($val2['Kode_Rek'] == $val['Kode_Induk']) {
|
||||
$arek[$key2]['Jumlah'] += $val['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $arek;
|
||||
}
|
||||
|
||||
public function RugiLaba(int $bln1=0, int $thn1=0, int $bln2=0, int $thn2=0, int $level=3): array {
|
||||
if ($bln1===0 || $thn1===0 || $bln2===0 || $thn2===0) return [];
|
||||
|
||||
$tjml = "R.Muta_".isi_nol($bln2, 2);
|
||||
if ($bln1 !== $bln2) {
|
||||
$tjml = ($bln1 === 1)? "R.Open" : "0";
|
||||
for ($i=$bln1; $i<=$bln2; $i++) {
|
||||
$tjml .= "+R.Muta_".isi_nol($i, 2);
|
||||
}
|
||||
}
|
||||
$taddsql = " ";
|
||||
|
||||
// ambil rekening yang sesuai batas level
|
||||
$arek = $this->db->fetchAll("SELECT A.*, ".$tjml." Jumlah FROM (SELECT * FROM gel_acc WHERE Level<=? AND (Grup_RekID BETWEEN 4 AND 5)) A LEFT JOIN gel_rekap R ON (A.Kode_Rek=R.Kode_Rek AND R.Tahun='".$thn2."') ORDER BY A.Kode_Rek", [$level]);
|
||||
|
||||
// ambil rekening diluar batas level laporan dan mode group
|
||||
$arek2 = $this->db->fetchAll("SELECT *,0 Jumlah FROM gel_acc WHERE Level>? AND (Grup_RekID BETWEEN 4 AND 5) AND Mode='G'", [$level]);
|
||||
|
||||
// ambil rekap diluar batas level laporan dan mode detail
|
||||
$aluar = $this->db->fetchAll("SELECT A.Kode_Induk Kode_Rek,SUM(".$tjml.") Jumlah FROM (SELECT * FROM gel_acc WHERE Level>? AND (Grup_RekID BETWEEN 4 AND 5) AND Mode='D') A LEFT JOIN gel_rekap R ON (A.Kode_Rek=R.Kode_Rek AND R.Tahun='".$thn2."') GROUP BY A.Kode_Induk", [$level]);
|
||||
foreach ($aluar as $row) {
|
||||
//cek batas luar
|
||||
$ba = false;
|
||||
foreach ($arek2 as $key => $val) {
|
||||
if ($val['Kode_Rek'] === $row['Kode_Rek']) {
|
||||
$ba = true;
|
||||
$arek2[$key]['Jumlah'] += $row['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ba === false) {
|
||||
foreach ($arek as $key => $val) {
|
||||
if ($val['Kode_Rek'] === $row['Kode_Rek']) {
|
||||
$ba = true;
|
||||
$arek[$key]['Jumlah'] += $row['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($ba === false) echo $row['Kode_Rek']; //debug
|
||||
}
|
||||
// merger rekap luar level pada pilihan level 2
|
||||
foreach ($arek2 as $key2 => $val2) {
|
||||
$ba = false;
|
||||
foreach ($arek as $key => $val) {
|
||||
if ($val2['Kode_Induk'] === $val['Kode_Rek']) {
|
||||
$ba = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ba === false) {
|
||||
foreach ($arek2 as $key3 => $val3) {
|
||||
if ($val2['Kode_Induk'] === $val3['Kode_Rek']) {
|
||||
$arek2[$key3]['Jumlah'] += $val2['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// merger rekap luar level kedalam batas level
|
||||
foreach ($arek2 as $key2 => $val2) {
|
||||
foreach ($arek as $key => $val) {
|
||||
if ($val2['Kode_Induk'] == $val['Kode_Rek']) {
|
||||
$arek[$key]['Jumlah'] += $val2['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// rekap saldo dari level besar ke level 2
|
||||
for ($nlev=$level; $nlev>1; $nlev--) {
|
||||
foreach ($arek as $key => $val) {
|
||||
if ($val['Level'] == $nlev) {
|
||||
foreach ($arek as $key2 => $val2) {
|
||||
if ($val2['Kode_Rek'] == $val['Kode_Induk']) {
|
||||
$arek[$key2]['Jumlah'] += $val['Jumlah'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $arek;
|
||||
}
|
||||
|
||||
public function Tahun_Array(): array {
|
||||
$ahasil = $this->db->fetchPairs("SELECT Tahun,Tahun FROM gel_rekap GROUP BY Tahun ASC") ?? [];
|
||||
return $ahasil;
|
||||
}
|
||||
|
||||
public function Karya_Baru(string $tgl): string {
|
||||
if (empty($tgl)) {
|
||||
$tno = date("\T\Kym\-");
|
||||
} else {
|
||||
$tmp = explode("-", $tgl);
|
||||
$tno = "TK".substr($tmp[2],2,2).$tmp[1]."-";
|
||||
}
|
||||
$ino = (int) $this->db->fetchField("SELECT MAX(RIGHT(No_Trans,4)) FROM gel_karyawan WHERE No_Trans LIKE ?", [$tno.'%']) ?? 0;
|
||||
$tno .= isi_nol(++$ino, 4);
|
||||
return $tno;
|
||||
}
|
||||
|
||||
public function Update_Lunas_Sup(int $id_sup): bool {
|
||||
if (empty($id_sup)) return false;
|
||||
$isup = $this->db->fetchField("SELECT COUNT(*) FROM inv_beli WHERE Lunas=0 AND SupID=?", [$id_sup]) ?? 0;
|
||||
$this->db->runQuery("UPDATE para_supplier SET Lunas=? WHERE SupID=?", [$isup, $id_sup]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function Update_Lunas_Buy(int $id_member): bool {
|
||||
if (empty($id_member)) return false;
|
||||
$imem = $this->db->fetchField("SELECT COUNT(*) FROM inv_jual WHERE Lunas=0 AND MemberID=?", [$id_member]) ?? 0;
|
||||
$this->db->runQuery("UPDATE para_member SET Lunas=? WHERE MemberID=?", [$imem, $id_member]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function Periode(string $tgl=""): array {
|
||||
$tgl = (empty($tgl)) ? tgl_dmy() : $tgl;
|
||||
$atgl = explode("-", $tgl);
|
||||
return [ 'Tahun' => $atgl[2], 'Bulan' => $atgl[1] ];
|
||||
}
|
||||
|
||||
public static function DK(int $grup=0): string {
|
||||
return (in_array($grup, [1,5])) ? "D" : "K";
|
||||
}
|
||||
|
||||
public static function Level_Array(int $level = -1, int $max_level = 5): array|string {
|
||||
//$_SESSION['__APP_GEL']['MAX_LEVEL']
|
||||
$_alevel = []; for ($i=1; $i<=$max_level; $i++) $_alevel[$i] = "Level ".$i;
|
||||
return ($level === -1) ? $_alevel : $_alevel[$level];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
|
||||
/*******************************************************************
|
||||
|
||||
PHP Class : /Utils/Inv
|
||||
Description : Class khusus untuk Inventori
|
||||
Created By : Siau Pheng ([email protected]).
|
||||
Created Date : February 10th, 2012.
|
||||
Last Modified : September 17th, 2026.
|
||||
|
||||
(c) 3FONIA Software; WWW.3FONIA.COM.
|
||||
|
||||
*******************************************************************/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use flight\Engine;
|
||||
use flight\database\SimplePdo;
|
||||
|
||||
final class Inv {
|
||||
|
||||
private int $id_server;
|
||||
private int $id_cab;
|
||||
private string $kd_pos;
|
||||
protected Engine $app;
|
||||
protected SimplePdo $db;
|
||||
|
||||
public function __construct(Engine $app) {
|
||||
$this->app = $app;
|
||||
$this->db = $app->db();
|
||||
$this->id_server = (int) $app->session()->get('WEB_APP')['SERVER_ID'];
|
||||
$this->id_cab = (int) $app->session()->get('APP_INV')['CABANGID'];
|
||||
$this->kd_pos = $app->session()->get('APP_INV')['KD_POS'];
|
||||
}
|
||||
|
||||
public function Nomor_POS(string $kd_pos = "", string $tgl = "", bool $save = false): string {
|
||||
if (empty($kd_pos)) $kd_pos = $this->kd_pos;
|
||||
if (empty($tgl)) {
|
||||
$tno = $kd_pos . date("y") . abjad(date("n"));
|
||||
} else {
|
||||
$tmp = explode("-", $tgl);
|
||||
$tno = $kd_pos . substr($tmp[2],2,2) . abjad((int) $tmp[1]);
|
||||
}
|
||||
$tno .= (string) $this->id_server;
|
||||
$ino = (int) $this->db->fetchField("SELECT MAX(RIGHT(No_Jual,5)) Nomor FROM inv_jual WHERE No_Jual LIKE ?", [$tno.'%']) ?? 0;
|
||||
$tno .= isi_nol(++$ino, 5);
|
||||
if ($save === true) $this->db->runQuery("INSERT INTO inv_jual SET No_Jual=?", [$tno]);
|
||||
return $tno;
|
||||
}
|
||||
|
||||
public function Nomor_Baru(string $tipe = "B", string $tgl = "", bool $save = false): string {
|
||||
$atipe = ['O'=>["No_Opname","inv_opname"],'M'=>["No_Mutasi","inv_mutasi"],'B'=>["No_Beli","inv_beli"],'R'=>["No_Beli","inv_beli"],'Z'=>["No_Jual","inv_jual"]];
|
||||
if (empty($tgl)) {
|
||||
$tno = $tipe . date("y") . abjad(date("n"));
|
||||
} else {
|
||||
$tmp = explode("-", $tgl);
|
||||
$tno = $tipe . substr($tmp[2],2,2) . abjad((int) $tmp[1]);
|
||||
}
|
||||
$tno .= (string) $this->id_server;
|
||||
$ino = (int) $this->db->fetchField("SELECT MAX(RIGHT(".$atipe[$tipe][0].",4)) Nomor FROM ".$atipe[$tipe][1]." WHERE ".$atipe[$tipe][0]." LIKE ?", [$tno.'%']) ?? 0;
|
||||
$tno .= isi_nol(++$ino, 4);
|
||||
if ($save === true) $this->db->runQuery("INSERT INTO ".$atipe[$tipe][1]." SET ".$atipe[$tipe][0]."=?", [$tno]);
|
||||
return $tno;
|
||||
}
|
||||
|
||||
public function Detail_Copy(string $no_ref = "", bool $unix = false, bool $mutasi = false): void {
|
||||
if (empty($no_ref)) return;
|
||||
$this->Detail_Cancel($no_ref);
|
||||
$tsql = ($mutasi === false) ? "" : " AND Qty>0";
|
||||
$this->db->runQuery("INSERT INTO tmp_inv_detail (No_Ref,ID,NotaID,CabID,GudangID,Tgl_Ref,InvID,SatuanID,VarianID,Qty,Stok,Harga_Beli,Harga_Jual,Disk_Rupiah,Disk_Persen1,Disk_Persen2,Disk_Persen3,Disk_Persen4,Disk_Persen5,Total_Diskon,Point,Promo,Unix,UserID) SELECT No_Ref,ID,NotaID,CabID,GudangID,Tgl_Ref,InvID,SatuanID,VarianID,Qty,Stok,Harga_Beli,Harga_Jual,Disk_Rupiah,Disk_Persen1,Disk_Persen2,Disk_Persen3,Disk_Persen4,Disk_Persen5,Total_Diskon,Point,Promo,'".(($unix)?$this->app->user()->unix():0)."',UserID FROM inv_detail WHERE No_Ref='".$no_ref."'".$tsql);
|
||||
}
|
||||
|
||||
public function Detail_Save(string $no_ref = "", int $nota = 0, int $cab = 0, string $tgl = "", bool $baru = false, string $no_lama = "", int $ppn = 0, bool $mutasi = false, int $cab2 = 0): void {
|
||||
if (empty($no_ref)) return;
|
||||
$this->db->runQuery("DELETE FROM inv_detail WHERE No_Ref=?", [$no_ref]);
|
||||
if ($baru) {
|
||||
$this->Detail_Cancel($no_ref);
|
||||
$this->db->runQuery("UPDATE tmp_inv_detail SET No_Ref=? WHERE No_Ref=? AND Unix=?", [$no_ref, $no_lama, $this->app->user()->unix()]);
|
||||
}
|
||||
//todo: hapus setelah tidak pakai gudang !
|
||||
$agudang = [2=>1, 3=>4, 4=>2];
|
||||
$gudang = $agudang[$cab] ?? 0;
|
||||
$gudang2 = $agudang[$cab2] ?? 0;
|
||||
// --> hapus sampai sini
|
||||
$this->db->runQuery("UPDATE tmp_inv_detail D,inv_produk_kemasan K SET D.Stok=(D.Qty*IFNULL(K.Qty,1)) WHERE D.InvID=K.InvID AND D.SatuanID=K.SatuanID AND D.No_Ref=?", [$no_ref]);
|
||||
if ($ppn === 0) {
|
||||
$this->db->runQuery("UPDATE tmp_inv_detail D,inv_produk P SET D.PPN=P.PPN WHERE D.InvID=P.InvID AND D.No_Ref=?", [$no_ref]);
|
||||
} else {
|
||||
$this->db->runQuery("UPDATE tmp_inv_detail SET PPN=? WHERE No_Ref=?", [$ppn, $no_ref]);
|
||||
}
|
||||
$qtgl = (empty($tgl)) ? "0000-00-00" : tgl_sql2str($tgl);
|
||||
$tsql = ($mutasi === true) ? " AND Qty>0" : "";
|
||||
$this->db->runQuery("INSERT INTO inv_detail (No_Ref,NotaID,CabID,GudangID,Tgl_Ref,InvID,SatuanID,VarianID,Qty,Stok,Harga_Beli,Harga_Jual,Disk_Rupiah,Disk_Persen1,Disk_Persen2,Disk_Persen3,Disk_Persen4,Disk_Persen5,Total_Diskon,Point,Promo,PPN,UserID) SELECT No_Ref,?,?,?,?,InvID,SatuanID,VarianID,Qty,Stok,Harga_Beli,Harga_Jual,Disk_Rupiah,Disk_Persen1,Disk_Persen2,Disk_Persen3,Disk_Persen4,Disk_Persen5,Total_Diskon,Point,Promo,PPN,UserID FROM tmp_inv_detail WHERE No_Ref=?".$tsql, [$nota, $cab, $gudang, $qtgl, $no_ref]);
|
||||
if ($mutasi === true) $this->db->runQuery("INSERT INTO inv_detail (No_Ref,NotaID,CabID,GudangID,Tgl_Ref,InvID,SatuanID,VarianID,Qty,Stok,Harga_Beli,Harga_Jual,Disk_Rupiah,Disk_Persen1,Disk_Persen2,Disk_Persen3,Disk_Persen4,Disk_Persen5,Total_Diskon,Point,Promo,PPN,UserID) SELECT No_Ref,?,?,?,?,InvID,SatuanID,VarianID,-Qty,-Stok,Harga_Beli,Harga_Jual,Disk_Rupiah,Disk_Persen1,Disk_Persen2,Disk_Persen3,Disk_Persen4,Disk_Persen5,Total_Diskon,Point,Promo,PPN,UserID FROM tmp_inv_detail WHERE No_Ref=?".$tsql, [$nota, $cab2, $gudang2, $qtgl, $no_ref]);
|
||||
$this->Detail_Cancel($no_ref);
|
||||
}
|
||||
|
||||
public function Detail_Cancel(string $no_ref = ""): void {
|
||||
if (empty($no_ref)) return;
|
||||
$this->db->runQuery("DELETE FROM tmp_inv_detail WHERE No_Ref=?", [$no_ref]);
|
||||
}
|
||||
|
||||
public function Produk_Update(string $no_ref = "", int $sup = 0): void {
|
||||
if (empty($no_ref) && $sup === 0) return;
|
||||
$aitem = $this->db->fetchAll("SELECT *,CEIL((Harga_Beli-(Total_Diskon/Qty))/(Stok/Qty)) HPP FROM inv_detail WHERE No_Ref=?", [$no_ref]);
|
||||
foreach ($aitem as $item) {
|
||||
if ((float) $item['Harga_Beli'] > 0) $this->db->runQuery("UPDATE inv_produk SET Harga_Beli=?,Harga_Pokok=Harga_Beli+Harga_Tambah WHERE InvID=?", [$item['HPP'], $item['InvID']]);
|
||||
$this->db->runQuery("INSERT IGNORE INTO inv_produk_sup SET InvID=?,SupID=?", [$item['InvID'], $sup]);
|
||||
}
|
||||
}
|
||||
|
||||
public function Produk_Join(int $id_inv = 0): void {
|
||||
$tsql = ''; $asql = [];
|
||||
if (empty($id_inv)) {
|
||||
$this->db->runQuery("TRUNCATE inv_produk_join");
|
||||
} else {
|
||||
$this->db->delete("inv_produk_join", "InvID=?", [$id_inv]);
|
||||
$tsql = " WHERE P.InvID=?";
|
||||
array_push($asql, $id_inv);
|
||||
}
|
||||
$this->db->runQuery("INSERT INTO inv_produk_join SELECT NULL,P.Grup_InvID,P.InvID,K.SatuanID,IFNULL(V.VarianID,0),H.Grup_MemberID,P.StokID,K.Qty,P.Nama_Produk,CONCAT(P.Nama_Produk,IF(VP.Nama_Varian IS NULL,'',CONCAT(' ',UCASE(VP.Nama_Varian)))),CONCAT(P.Nama_Produk,IF(VP.Nama_Varian IS NULL,'',CONCAT(' ',UCASE(VP.Nama_Varian))),' ',UCASE(SK.Nama_Satuan)),CONCAT(IFNULL(V.Kode_SKU,''),' ',IFNULL(V.Kode_Barcode,''),' ',P.Nama_Produk,' ',UCASE(IFNULL(VP.Nama_Varian,''))),IFNULL(V.Kode_SKU,''),IFNULL(V.Kode_Barcode,''),SP.Nama_Satuan,SK.Nama_Satuan,ROUND(P.Harga_Pokok*K.Qty,2),IFNULL(H.Harga_Jual,0),P.PPN,P.Aktif FROM inv_produk P INNER JOIN inv_produk_kemasan K ON (P.InvID=K.InvID) INNER JOIN inv_produk_varian V ON (K.InvID=V.InvID AND K.SatuanID=V.SatuanID) LEFT JOIN inv_produk_harga H ON (K.InvID=H.InvID AND K.SatuanID=H.SatuanID) LEFT JOIN para_satuan SP ON (P.StokID=SP.SatuanID) LEFT JOIN para_satuan SK ON (K.SatuanID=SK.SatuanID) LEFT JOIN para_varian VP ON (V.VarianID=VP.VarianID)".$tsql, $asql);
|
||||
}
|
||||
|
||||
/************************* TRANSAKSI PENDING *************************/
|
||||
|
||||
public function Pending_Edit(string $nomor = ""): void {
|
||||
$this->db->runQuery("UPDATE tmp_inv_detail SET Unix=? WHERE No_Ref=?", [$this->app->user()->unix(), $nomor]);
|
||||
}
|
||||
|
||||
public function Pending_Save(string $no_lama = "", string $no_baru = ""): void {
|
||||
$this->db->runQuery("UPDATE tmp_inv_detail SET No_Ref=? WHERE No_Ref=? AND Unix=?", [$no_baru, $no_lama, $this->app->user()->unix()]);
|
||||
}
|
||||
|
||||
public function Pending_Delete(string $nomor = ""): void {
|
||||
$this->db->runQuery("DELETE FROM tmp_inv_detail WHERE No_Ref=?", [$nomor]);
|
||||
}
|
||||
|
||||
/************************* POINT MEMBER *************************/
|
||||
|
||||
public function Point_Baru(string $tipe = "T", string $tgl = ""): string {
|
||||
if (empty($tgl)) {
|
||||
$tno = $tipe . date("y") . abjad(date("n"));
|
||||
} else {
|
||||
$tmp = explode("-", $tgl);
|
||||
$tno = $tipe . substr($tmp[2],2,2) . abjad((int) $tmp[1]);
|
||||
}
|
||||
$tno .= (string) $this->id_server;
|
||||
$ino = (int) $this->db->fetchField("SELECT MAX(RIGHT(No_TX,4)) Nomor FROM inv_point_tx WHERE No_TX LIKE ?", [$tno.'%']) ?? 0;
|
||||
$tno .= isi_nol(++$ino, 4);
|
||||
return $tno;
|
||||
}
|
||||
|
||||
public function Point_Hitung(string $no_nota="", string $tgl_nota="", int $grup_member=0, int $id_kartu=0, int $id_tipe=1): int {
|
||||
if (empty($no_nota)) return 0;
|
||||
$tgl_nota = empty($tgl_nota) ? tgl_ymd() : tgl_sql2str($tgl_nota);
|
||||
|
||||
// cek tidak perkenankan hitung point utk grup tertentu
|
||||
//if (in_array($grup_member, array(6))) return 0;
|
||||
|
||||
$apoint = [];
|
||||
if ($id_kartu > 0) {
|
||||
$apoint = $this->db->fetchAll("SELECT * FROM inv_point WHERE Aktif=1 AND CabID IN (?) AND Grup_MemberID IN (?) AND Tgl_Mulai<=? AND KartuID=? AND Point_TipeID=? ORDER BY Nilai_Jual DESC", [[0,$this->id_cab], [0,$grup_member], $tgl_nota, $id_kartu, $id_tipe]);
|
||||
}
|
||||
if (count($apoint) === 0) {
|
||||
$apoint = $this->db->fetchAll("SELECT * FROM inv_point WHERE Aktif=1 AND CabID IN (?) AND Grup_MemberID IN (?) AND Tgl_Mulai<=? AND KartuID=? AND Point_TipeID=? ORDER BY Nilai_Jual DESC", [[0,$this->id_cab], [0,$grup_member], $tgl_nota, 0, $id_tipe]);
|
||||
}
|
||||
if (count($apoint) === 0) return 0;
|
||||
|
||||
$tot_point = 0;
|
||||
foreach ($apoint as $point) {
|
||||
$isproc = false; $tsql = "";
|
||||
if (!empty($point['Grup_Barang'])) {
|
||||
$tsql = "SELECT SUM((-D.Qty*D.Harga_Jual)+D.Total_Diskon) Total FROM (SELECT * FROM inv_detail WHERE No_Ref='".$no_nota."' ORDER BY InvID) D LEFT JOIN (SELECT InvID,Grup_InvID FROM inv_produk WHERE Grup_InvID IN (".$point['Grup_Barang'].")) P ON (P.InvID=D.InvID) WHERE P.Grup_InvID IN (".$point['Grup_Barang'].")";
|
||||
$isproc = true;
|
||||
} else if (!empty($point['Kode_Barang'])) {
|
||||
$tsql = "SELECT SUM((-D.Qty*D.Harga_Jual)+D.Total_Diskon) Total FROM (SELECT * FROM inv_detail WHERE No_Ref='".$no_nota."' AND InvID IN (".$point['Kode_Barang'].")) D";
|
||||
$isproc = true;
|
||||
}
|
||||
if ($isproc == false) continue;
|
||||
$tot_jual = (float) $this->db->fetchField($tsql) ?? 0; $pone = 0;
|
||||
if ($tot_jual !== 0) {
|
||||
$tot_jual = abs($tot_jual);
|
||||
$pone = ($tot_jual > 0) ? 1 : -1;
|
||||
}
|
||||
if ($tot_jual < (float) $point['Nilai_Jual'] || $tot_jual === 0) continue;
|
||||
$tot_point += (floor($tot_jual / (float) $point['Nilai_Jual']) * $pone);
|
||||
}
|
||||
return $tot_point;
|
||||
}
|
||||
|
||||
/************************* PROMO *************************/
|
||||
|
||||
function Promo_Hapus(string $no_nota="", string $tgl_nota="", int $grup_member=0): void {
|
||||
if (empty($no_nota)) return;
|
||||
$tgl_nota = (empty($tgl_nota)) ? tgl_ymd() : tgl_sql2str($tgl_nota);
|
||||
|
||||
$arow = $this->db->fetchAll("SELECT * FROM inv_detail WHERE No_Ref=? AND Promo IN (1,2,3)", [$no_nota]);
|
||||
foreach ($arow as $row) {
|
||||
$this->db->runQuery("UPDATE inv_promo SET Jumlah_Proses=Jumlah_Proses-".abs($row['Qty'] ?? 0)." WHERE Promo_InvID=? AND Promo_SatuanID=? AND CabID IN (?) AND Grup_MemberID=? AND Tipe_Promo=? AND Tgl_Mulai<=? AND Tgl_Akhir>=?", [$row['InvID'], $row['SatuanID'], [0,$this->id_cab], $grup_member, $row['Promo'], $tgl_nota, $tgl_nota]);
|
||||
}
|
||||
}
|
||||
|
||||
public function Promo_Barang(string $no_nota="", string $tgl_nota="", int $grup_member=0): void {
|
||||
if (empty($no_nota)) return;
|
||||
$tgl_nota = (empty($tgl_nota)) ? tgl_ymd() : tgl_sql2str($tgl_nota);
|
||||
|
||||
$apromo = $this->db->fetchAll("SELECT * FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND Promo IN (1,2)", [$no_nota, $this->app->user()->unix()]);
|
||||
foreach ($apromo as $promo) {
|
||||
$this->db->runQuery("UPDATE inv_promo SET Jumlah_Proses=Jumlah_Proses-'".abs($promo['Qty'])."' WHERE Promo_InvID=? AND Promo_SatuanID=? AND CabID IN (?) AND Grup_MemberID=? AND Tipe_Promo=? AND Tgl_Mulai<=? AND Tgl_Akhir>=?", [$promo['InvID'], $promo['SatuanID'], [0,$this->id_cab], $grup_member, $promo['Promo'], $tgl_nota, $tgl_nota]);
|
||||
if ($promo['Promo'] === 1) $this->db->runQuery("UPDATE tmp_inv_detail SET Disk_Rupiah=0,Total_Diskon=0,Promo=0 WHERE No_Ref=? AND Unix=? AND ID=?", [$no_nota, $this->app->user()->unix(), $promo['ID']]);
|
||||
if ($promo['Promo'] === 2) $this->db->runQuery("DELETE FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND ID=?", [$no_nota, $this->app->user()->unix(), $promo['ID']]);
|
||||
}
|
||||
|
||||
$apromo = $this->db->fetchAll("SELECT *,(Jumlah_Promo-Jumlah_Proses) Jumlah_Sisa FROM inv_promo WHERE Aktif=1 AND CabID IN (?) AND Grup_MemberID=? AND Tipe_Promo IN (1,2) AND (Jumlah_Promo-Jumlah_Proses)>0 AND Tgl_Mulai<=? AND Tgl_Akhir>=? AND CONCAT(InvID,':',SatuanID) IN (SELECT CONCAT(InvID,':',SatuanID) FROM tmp_inv_detail WHERE No_Ref=? AND Unix=?)", [[0,$this->id_cab], $grup_member, $tgl_nota, $tgl_nota, $no_nota, $this->app->user()->unix()]);
|
||||
if (count($apromo) === 0) return;
|
||||
|
||||
foreach ($apromo as $promo) {
|
||||
if ($promo['Tipe_Promo'] === 1) {//1 potongan diskon
|
||||
$adetail = $this->db->fetchRow("SELECT COUNT(*) Item,SUM(-Qty) Qty,ID FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND InvID=? AND SatuanID=? ORDER BY ID", [$no_nota, $this->app->user()->unix(), $promo['InvID'], $promo['SatuanID']]) ?? ['Item'=>0, 'Qty'=>0, 'ID'=>0];
|
||||
$aitem = $this->db->fetchRow("SELECT * FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND InvID=? AND SatuanID=? AND ID=?", [$no_nota, $this->app->user()->unix(), $promo['InvID'], $promo['SatuanID'], $adetail['ID']]);
|
||||
$bpromo = false;
|
||||
if ($promo['Pilih_Qty'] === 1 && $promo['Qty_Min'] <= $adetail['Qty']) {//minimum & masuk limit
|
||||
$bpromo = true;
|
||||
$qty_promo = ($adetail['Qty'] <= $promo['Jumlah_Sisa']) ? $adetail['Qty'] : $promo['Jumlah_Sisa'];
|
||||
} else if ($promo['Pilih_Qty'] === 2 && $promo['Qty_Kali'] <= $adetail['Qty']) {//kelipatan
|
||||
$bpromo = true;
|
||||
$qty_promo = floor($adetail['Qty'] / $promo['Qty_Kali']) * $promo['Qty_Kali'];
|
||||
$qty_promo = ($qty_promo <= $promo['Jumlah_Sisa']) ? $qty_promo : $promo['Jumlah_Sisa'];
|
||||
}
|
||||
if ($bpromo == true) {
|
||||
if ($adetail['Item']>1) {//hapus duplikasi
|
||||
$this->db->runQuery("DELETE FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND InvID=? AND SatuanID=? AND ID<>?", [$no_nota, $this->app->user()->unix(), $promo['InvID'], $promo['SatuanID'], $adetail['ID']]);
|
||||
}
|
||||
$istok = $aitem['Stok'] / $aitem['Qty'];
|
||||
if ($qty_promo === $adetail['Qty']) {
|
||||
$aitem['Qty'] = $qty_promo * -1;
|
||||
$aitem['Stok'] = $qty_promo * $istok * -1;
|
||||
$aitem['Disk_Rupiah'] = $promo['Promo_Disc'];
|
||||
$aitem['Total_Diskon'] = $qty_promo * $promo['Promo_Disc'];
|
||||
$aitem['Promo'] = 1;
|
||||
$this->db->update("tmp_inv_detail", $aitem, "No_Ref=? AND Unix=? AND ID=?", [$aitem['No_Ref'], $aitem['Unix'], $aitem['ID']]);
|
||||
} else {
|
||||
$adisc = $aitem;
|
||||
$aitem['Qty'] = ($adetail['Qty']-$qty_promo) * -1;
|
||||
$aitem['Stok'] = ($adetail['Qty']-$qty_promo) * $istok * -1;
|
||||
$this->db->update("tmp_inv_detail", $aitem, "No_Ref=? AND Unix=? AND ID=?", [$aitem['No_Ref'], $aitem['Unix'], $aitem['ID']]);
|
||||
|
||||
$adisc['Qty'] = $qty_promo * -1;
|
||||
$adisc['Stok'] = $qty_promo * $istok * -1;
|
||||
$adisc['Disk_Rupiah'] = $promo['Promo_Disc'];
|
||||
$adisc['Total_Diskon'] = $qty_promo * $promo['Promo_Disc'];
|
||||
$adisc['Promo'] = 1;
|
||||
unset($adisc['ID']);
|
||||
$this->db->insert("tmp_inv_detail", $adisc);
|
||||
}
|
||||
$this->db->runQuery("UPDATE inv_promo SET Jumlah_Proses=Jumlah_Proses+".($qty_promo ?? 0)." WHERE PromoID=?", [$promo['PromoID']]);
|
||||
}
|
||||
} else {//2 promo barang
|
||||
$adetail = $this->db->fetchRow("SELECT COUNT(*) Item,SUM(-Qty) Qty,ID FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND InvID=? AND SatuanID=? ORDER BY ID", [$no_nota, $this->app->user()->unix(), $promo['InvID'], $promo['SatuanID']]) ?? ['Item'=>0, 'Qty'=>0, 'ID'=>0];
|
||||
$adisc = $this->db->fetchRow("SELECT * FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND InvID=? AND SatuanID=? AND ID=?", [$no_nota, $this->app->user()->unix(), $promo['InvID'], $promo['SatuanID'], $adetail['ID']]);
|
||||
|
||||
$bpromo = false;
|
||||
if ($promo['Pilih_Qty'] === 1 && $promo['Qty_Min'] <= $adetail['Qty']) {//minimum & masuk limit
|
||||
$bpromo = true;
|
||||
$qty_promo = ($promo['Promo_Qty'] <= $promo['Jumlah_Sisa']) ? $promo['Promo_Qty'] : $promo['Jumlah_Sisa'];
|
||||
} else if ($promo['Pilih_Qty'] === 2 && $promo['Qty_Kali'] <= $adetail['Qty']) {//kelipatan
|
||||
$bpromo = true;
|
||||
$qty_promo = floor($adetail['Qty'] / $promo['Qty_Kali']) * $promo['Promo_Qty'];
|
||||
$qty_promo = ($qty_promo <= $promo['Jumlah_Sisa']) ? $qty_promo : $promo['Jumlah_Sisa'];
|
||||
}
|
||||
if ($bpromo === true && $qty_promo > 0) {
|
||||
$_row = $this->db->fetchRow("SELECT H.Harga_Jual,P.* FROM inv_produk_harga H LEFT JOIN (SELECT P.InvID,P.SatuanID,P.Qty,P.Harga_Pokok FROM vi_inv_produk P WHERE P.InvID=? AND P.SatuanID=?) P ON (P.InvID=H.InvID AND P.SatuanID=H.SatuanID) WHERE H.InvID=? AND H.SatuanID=? AND H.Grup_MemberID=?", [$promo['Promo_InvID'], $promo['Promo_SatuanID'], $promo['Promo_InvID'], $promo['Promo_SatuanID'], $grup_member]);
|
||||
$adisc['InvID'] = $promo['Promo_InvID'];
|
||||
$adisc['SatuanID'] = $promo['Promo_SatuanID'];
|
||||
$adisc['VarianID'] = 0;
|
||||
$adisc['Qty'] = $qty_promo * -1;
|
||||
$adisc['Stok'] = $qty_promo * ($_row['Qty'] ?? 1) * -1;
|
||||
$adisc['Harga_Beli'] = ($_row['Harga_Pokok'] ?? 0);
|
||||
$adisc['Harga_Jual'] = ($_row['Harga_Jual'] ?? 0);
|
||||
$adisc['Disk_Rupiah'] = $adisc['Harga_Jual'];
|
||||
$adisc['Total_Diskon'] = $qty_promo * $adisc['Harga_Jual'];
|
||||
$adisc['Promo'] = 2;
|
||||
unset($adisc['ID']);
|
||||
$this->db->insert("tmp_inv_detail", $adisc);
|
||||
$this->db->runQuery("UPDATE inv_promo SET Jumlah_Proses=Jumlah_Proses+".($qty_promo ?? 0)." WHERE PromoID=?", [$promo['PromoID']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function Promo_Total(string $no_nota="", string $tgl_nota="", int $grup_member=0): float {
|
||||
if (empty($no_nota)) return 0;
|
||||
$tgl_nota = (empty($tgl_nota)) ? tgl_ymd() : tgl_sql2str($tgl_nota);
|
||||
|
||||
$apromo = $this->db->fetchAll("SELECT * FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND Promo IN (3,4)", [$no_nota, $this->app->user()->unix()]);
|
||||
foreach ($apromo as $promo) {
|
||||
$this->db->runQuery("UPDATE inv_promo SET Jumlah_Proses=Jumlah_Proses-'".abs($promo['Qty'] ?? 0)."' WHERE Promo_InvID=? AND Promo_SatuanID=? AND CabID IN (?) AND Grup_MemberID=? AND Tipe_Promo=? AND Tgl_Mulai<=? AND Tgl_Akhir>=?", [$promo['InvID'], $promo['SatuanID'], [0,$this->id_cab], $grup_member, $promo['Promo'], $tgl_nota, $tgl_nota]);
|
||||
if ($promo['Promo'] === 3) $this->db->runQuery("DELETE FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND ID=?", [$no_nota, $this->app->user()->unix(), $promo['ID']]);
|
||||
}
|
||||
|
||||
$apromo = $this->db->fetchAll("SELECT *,(Jumlah_Promo-Jumlah_Proses) Jumlah_Sisa FROM inv_promo WHERE Aktif=1 AND CabID IN (?) AND Grup_MemberID=? AND Tipe_Promo IN (3,4) AND Tgl_Mulai<=? AND Tgl_Akhir>=? ORDER BY Jual_Min DESC", [[0,$this->id_cab], $grup_member, $tgl_nota, $tgl_nota]);
|
||||
if (count($apromo) === 0) return 0;
|
||||
|
||||
$tot_potong = 0;
|
||||
foreach ($apromo as $promo) {
|
||||
$tot_jual = (float) $this->db->fetchField("SELECT SUM((-D.Qty*D.Harga_Jual)+D.Total_Diskon) Total FROM tmp_inv_detail D LEFT JOIN (SELECT P.InvID,P.Grup_InvID FROM inv_produk P WHERE P.Grup_InvID IN (?)) P ON (P.InvID=D.InvID) WHERE D.No_Ref=? AND D.Unix=? AND P.Grup_InvID IN (?)", [$promo['Jual_Grup'], $no_nota, $this->app->user()->unix(), $promo['Jual_Grup']]) ?? 0;
|
||||
if ($tot_jual < $promo['Jual_Min']) continue;
|
||||
|
||||
if ($promo['Tipe_Promo'] === 4) {//4 potongan diskon
|
||||
$tot_potong += $promo['Promo_Jual'];
|
||||
} else {//3 promo barang
|
||||
$qty_promo = ($promo['Promo_Qty'] <= $promo['Jumlah_Sisa']) ? $promo['Promo_Qty'] : $promo['Jumlah_Sisa'];
|
||||
if ($qty_promo === 0) continue;
|
||||
|
||||
$adisc = $this->db->fetchRow("SELECT * FROM tmp_inv_detail WHERE No_Ref=? AND Unix=?", [$no_nota, $this->app->user()->unix()]);
|
||||
|
||||
$_row = $this->db->fetchRow("SELECT H.Harga_Jual,P.* FROM inv_produk_harga H LEFT JOIN (SELECT P.InvID,P.SatuanID,P.Qty,P.Harga_Pokok FROM vi_inv_produk P WHERE P.InvID=? AND P.SatuanID=?) P ON (P.InvID=H.InvID AND P.SatuanID=H.SatuanID) WHERE H.InvID=? AND H.SatuanID=? AND H.Grup_MemberID=?", [$promo['Promo_InvID'], $promo['Promo_SatuanID'], $promo['Promo_InvID'], $promo['Promo_SatuanID'], $grup_member]);
|
||||
|
||||
$adisc['InvID'] = $promo['Promo_InvID'];
|
||||
$adisc['SatuanID'] = $promo['Promo_SatuanID'];
|
||||
$adisc['VarianID'] = 0;
|
||||
$adisc['Qty'] = $qty_promo * -1;
|
||||
$adisc['Stok'] = $qty_promo * ($_row['Qty'] ?? 0) * -1;
|
||||
$adisc['Harga_Beli'] = ($_row['Harga_Pokok'] ?? 0);
|
||||
$adisc['Harga_Jual'] = ($_row['Harga_Jual'] ?? 0);
|
||||
$adisc['Disk_Rupiah'] = $adisc['Harga_Jual'];
|
||||
$adisc['Total_Diskon'] = $qty_promo * $adisc['Harga_Jual'];
|
||||
$adisc['Promo'] = 3;
|
||||
unset($adisc['ID']);
|
||||
$this->db->insert("tmp_inv_detail", $adisc);
|
||||
$this->db->runQuery("UPDATE inv_promo SET Jumlah_Proses=Jumlah_Proses+".($qty_promo ?? 0)." WHERE PromoID=?", [$promo['PromoID']]);
|
||||
}
|
||||
break;//todo: pastikan jika beda group barang di pisahkan!!
|
||||
}
|
||||
return $tot_potong;
|
||||
}
|
||||
|
||||
public function Promo_Hitung(string $no_nota="", string $tgl_nota="", int $grup_member=0): float {
|
||||
if (empty($no_nota)) return 0;
|
||||
$tgl_nota = (empty($tgl_nota)) ? tgl_ymd() : tgl_sql2str($tgl_nota);
|
||||
$tot_promo = 0;
|
||||
|
||||
$apromo = $this->db->fetchAll("SELECT *,(Jumlah_Promo-Jumlah_Proses) Jumlah_Sisa FROM inv_promo WHERE Aktif=1 AND CabID IN (?) AND Grup_MemberID=? AND Tipe_Promo=1 AND (Jumlah_Promo-Jumlah_Proses)>0 AND Tgl_Mulai<=? AND Tgl_Akhir>=? AND CONCAT(InvID,':',SatuanID) IN (SELECT CONCAT(InvID,':',SatuanID) FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND Promo NOT IN (2,3))", [[0,$this->id_cab], $grup_member, $tgl_nota, $tgl_nota, $no_nota, $this->app->user()->unix()]);
|
||||
if (count($apromo) > 0) {
|
||||
foreach ($apromo as $promo) {
|
||||
$adetail = $this->db->fetchRow("SELECT COUNT(*) Item,SUM(-Qty) Qty,ID FROM tmp_inv_detail WHERE No_Ref=? AND Unix=? AND InvID=? AND SatuanID=? ORDER BY ID", [$no_nota, $this->app->user()->unix(), $promo['InvID'], $promo['SatuanID']]) ?? ['Item'=>0, 'Qty'=>0, 'ID'=>0];
|
||||
|
||||
$bpromo = false;
|
||||
if ($promo['Pilih_Qty'] === 1 && $promo['Qty_Min'] <= $adetail['Qty']) {//minimum & masuk limit
|
||||
$bpromo = true;
|
||||
$qty_promo = ($adetail['Qty'] <= $promo['Jumlah_Sisa']) ? $adetail['Qty'] : $promo['Jumlah_Sisa'];
|
||||
} else if ($promo['Pilih_Qty'] === 2 && $promo['Qty_Kali'] <= $adetail['Qty']) {//kelipatan
|
||||
$bpromo = true;
|
||||
$qty_promo = floor($adetail['Qty'] / $promo['Qty_Kali']) * $promo['Qty_Kali'];
|
||||
$qty_promo = ($qty_promo <= $promo['Jumlah_Sisa']) ? $qty_promo : $promo['Jumlah_Sisa'];
|
||||
}
|
||||
$tot_promo += ($bpromo === true) ? ($qty_promo * $promo['Promo_Disc']) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
$apromo = $this->db->fetchAll("SELECT *,(Jumlah_Promo-Jumlah_Proses) Jumlah_Sisa FROM inv_promo WHERE Aktif=1 AND CabID IN (?) AND Grup_MemberID=? AND Tipe_Promo=4 AND Tgl_Mulai<=? AND Tgl_Akhir>=? ORDER BY Jual_Min DESC", [[0,$this->id_cab], $grup_member, $tgl_nota, $tgl_nota]);
|
||||
if (count($apromo) > 0) {
|
||||
foreach ($apromo as $promo) {
|
||||
$tot_jual = (float) $this->db->fetchField("SELECT SUM((-D.Qty*D.Harga_Jual)+D.Total_Diskon) Total FROM tmp_inv_detail D LEFT JOIN (SELECT P.InvID,P.Grup_InvID FROM inv_produk P WHERE P.Grup_InvID IN (?)) P ON (P.InvID=D.InvID) WHERE D.No_Ref=? AND D.Unix=? AND P.Grup_InvID IN (?)", [$promo['Jual_Grup'], $no_nota, $this->app->user()->unix(), $promo['Jual_Grup']]) ?? 0;
|
||||
if ($tot_jual < $promo['Jual_Min']) continue;
|
||||
$tot_promo += $promo['Promo_Jual'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $tot_promo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
/*******************************************************************
|
||||
|
||||
PHP Class : /Utils/Invesa
|
||||
Description : Class umum untuk Aplikasi Invesa
|
||||
Created By : Siau Pheng ([email protected]).
|
||||
Created Date : February 10th, 2012.
|
||||
Last Modified : September 1st, 2026.
|
||||
|
||||
(c) 3FONIA Software; WWW.3FONIA.COM.
|
||||
|
||||
*******************************************************************/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use flight\Session;
|
||||
|
||||
final class Invesa {
|
||||
|
||||
public const ppn = [0=>"Tanpa PPN", 1=>"PPN", 3=>"Spesial 1", 4=>"Spesial 2", 5=>"Spesial 3"];
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public static function PPN_Array(int $item = -1): mixed {
|
||||
return ($item === -1) ? self::ppn : self::ppn[$item];
|
||||
}
|
||||
|
||||
public static function Form_Info(string $pesan=""): string {
|
||||
return '<button class="btn btn-outline-secondary shadow-none text-info" data-toggle="tooltip" title="'.$pesan.'"><i class="bi-lightbulb"></i></button>';
|
||||
}
|
||||
|
||||
/************************* BARANG / PRODUK *************************/
|
||||
|
||||
public static function genEAN8(string $pref = '0', int $numb = 1): string {
|
||||
$code = str_pad($pref, 5, '0', STR_PAD_LEFT) . str_pad((string) $numb, 2, '0', STR_PAD_LEFT);
|
||||
$bflag = true;
|
||||
$sum = 0;
|
||||
for ($i = strlen($code) - 1; $i >= 0; $i--) {
|
||||
$sum += (int) $code[$i] * ($bflag?3:1);
|
||||
$bflag = !$bflag;
|
||||
}
|
||||
$code .= (string) (10 - ($sum % 10)) % 10;
|
||||
return $code;
|
||||
}
|
||||
|
||||
public static function genEAN13(string $pref = "98000", int $numb = 1): string {
|
||||
$code = str_pad($pref, 5, '0', STR_PAD_LEFT) . str_pad((string) $numb, 7, '0', STR_PAD_LEFT);
|
||||
$weightflag = true;
|
||||
$sum = 0;
|
||||
for ($i = strlen($code) - 1; $i >= 0; $i--) {
|
||||
$sum += (int) $code[$i] * ($weightflag?3:1);
|
||||
$weightflag = !$weightflag;
|
||||
}
|
||||
$code .= (string) (10 - ($sum % 10)) % 10;
|
||||
return $code;
|
||||
}
|
||||
|
||||
/************************* DEFAULT ARRAY TABLE *************************/
|
||||
|
||||
public static function A_Iklan(): array {
|
||||
return [
|
||||
'PromoID' => 0,
|
||||
'CabID' => 0,
|
||||
'Tanggal' => tgl_ymd(),
|
||||
'Info' => "",
|
||||
'Warna' => "#FFFFFF",
|
||||
'Font' => 40,
|
||||
'Aktif' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
public static function A_Member_Member(): array {
|
||||
return [
|
||||
'MemberID' => 0,
|
||||
'Grup_MemberID' => 0,
|
||||
'Kode_Member' => "",
|
||||
'Nama_Member' => "",
|
||||
'Alamat_Member' => "",
|
||||
'Kota_Member' => "",
|
||||
'Telp_Member' => "",
|
||||
'KTP_Member' => "",
|
||||
'Tgl_Lahir' => "0000-00-00",
|
||||
'Tgl_Daftar' => tgl_ymd(),
|
||||
'IM' => true,
|
||||
'Aktif' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
public static function A_Member_Grup(): array {
|
||||
return [
|
||||
'Grup_MemberID' => 0,
|
||||
'Kode_Grup' => "",
|
||||
'Nama_Grup' => "",
|
||||
'Aktif' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
public static function A_Promo(int $tipe_promo = 1): array {
|
||||
return [
|
||||
'PromoID' => 0,
|
||||
'Nama_Promo' => "",
|
||||
'InvID' => 0,
|
||||
'SatuanID' => 0,
|
||||
'CabID' => 0,
|
||||
'Grup_MemberID' => 0,
|
||||
'Pilih_Qty' => 1,
|
||||
'Tipe_Promo' => $tipe_promo,
|
||||
'Promo_InvID' => 0,
|
||||
'Promo_SatuanID' => 0,
|
||||
'Promo_Qty' => 0,
|
||||
'Promo_Disc' => 0,
|
||||
'Nama_Produk' => "",
|
||||
'Promo_Produk' => "",
|
||||
'Jual_Min' => 0,
|
||||
'Jual_Grup' => "",
|
||||
'Promo_Jual' => 0,
|
||||
'Qty_Min' => 0,
|
||||
'Qty_Kali' => 0,
|
||||
'Jumlah_Promo' => 0,
|
||||
'Tgl_Mulai' => tgl_ymd(),
|
||||
'Tgl_Akhir' => tgl_sql2str(tgl_operasi(tgl_dmy(),0,1,0)),
|
||||
'Aktif' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
/************************* VOUCHER *************************/
|
||||
|
||||
public static function getVoucherBG(string $root_dir): array {
|
||||
$voudir = $root_dir . DIRECTORY_SEPARATOR . "public" . DIRECTORY_SEPARATOR . "assets" . DIRECTORY_SEPARATOR . "images" . DIRECTORY_SEPARATOR . "voucher";
|
||||
$afile = [];
|
||||
$iterator = new \FilesystemIterator($voudir, \FilesystemIterator::SKIP_DOTS);
|
||||
foreach ($iterator as $_file) {
|
||||
if ($_file->isFile() && str_contains($_file->getFilename(), "vou")) {
|
||||
$afile[] = $_file->getFilename();
|
||||
}
|
||||
}
|
||||
return $afile;
|
||||
}
|
||||
|
||||
/************************* FORMAT TAMPILAN DATA *************************/
|
||||
|
||||
public static function Format_Stok(int|float $total=0, int $kali=1, string $sat_norm="", string $sat_stok=""): string {
|
||||
if ($kali > 0) {
|
||||
$norm = floor($total / $kali);
|
||||
$sisa = $total - ($norm * $kali);
|
||||
} else {
|
||||
$norm = 0;
|
||||
$sisa = $total;
|
||||
}
|
||||
$stok = rupiah($norm,0)." ".$sat_norm;
|
||||
if ($sat_norm !== $sat_stok) $stok .= ", ".rupiah($sisa,0)." ".$sat_stok;
|
||||
return $stok;
|
||||
}
|
||||
|
||||
/************************* PRINTER THERMAL *************************/
|
||||
|
||||
public static function Printer_Thermal(Session $session, string $printer): bool {
|
||||
$is_thermal = true;
|
||||
foreach($session->get('APP_INV',['OLD_PRINTER'=>[]])['OLD_PRINTER'] as $old) {
|
||||
if (str_contains($printer, $old)) {
|
||||
$is_thermal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $is_thermal;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
/*******************************************************************
|
||||
|
||||
PHP Class : /Utils/PPOBClient
|
||||
Description : Class khusus untuk Client PPOB
|
||||
Created By : Siau Pheng ([email protected]).
|
||||
Created Date : February 10th, 2012.
|
||||
Last Modified : October 22nd, 2026.
|
||||
|
||||
(c) 3FONIA Software; WWW.3FONIA.COM.
|
||||
|
||||
*******************************************************************/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use flight\Engine;
|
||||
use flight\Session;
|
||||
use flight\database\SimplePdo;
|
||||
|
||||
final class PPOBClient {
|
||||
|
||||
private string $ppob_server = "https://api.trifonia.com/";
|
||||
private array $agen;
|
||||
private int $id_user;
|
||||
private int $id_server;
|
||||
private int $id_cabang;
|
||||
protected SimplePdo $db;
|
||||
|
||||
public function __construct(Engine $app) {
|
||||
$this->db = $app->db();
|
||||
$this->agen = $app->session()->get('APP_PPOB');
|
||||
$this->id_user = (int) $app->session()->get('WEB_USER', ['UserID'=>0])['UserID'];
|
||||
$this->id_server = (int) $app->session()->get('WEB_APP')['SERVER_ID'];
|
||||
$this->id_cabang = (int) $app->session()->get('APP_INV')['CABANGID'];
|
||||
}
|
||||
|
||||
public function Depo(string $bank, float $jumlah): array {
|
||||
$sign = $this->CreateSign($bank, "", "");
|
||||
$url = $this->ppob_server.$this->agen['API_AGENID']."/depo&bank=".$bank."&jumlah=".(string) $jumlah."&sign=".$sign;
|
||||
$tdata = file_get_contents($url);
|
||||
$adata = json_decode($tdata, true);
|
||||
if (count($adata) > 0 && (int) $adata['Status'] === 4) {
|
||||
$copy = ['No_Depo','Tgl_Depo','Tgl_Exp','Jumlah','Bank','Catatan'];
|
||||
$insert = array_intersect_key($adata, array_flip($copy));
|
||||
$insert['UserID'] = $this->id_user;
|
||||
$this->db->insert("ppob_depo", $insert);
|
||||
}
|
||||
return $adata;
|
||||
}
|
||||
|
||||
public function TopUp(string $kode, string $tujuan, string $no_tx, ?string $no_hp=""): array {
|
||||
$sign = $this->CreateSign($kode, $tujuan, $no_tx);
|
||||
$url = $this->ppob_server.$this->agen['API_AGENID']."/topup&kode=".$kode."&tujuan=".$tujuan."&agentx=".$no_tx."&hppel=".$no_hp."&sign=".$sign;
|
||||
$tdata = file_get_contents($url);
|
||||
$adata = json_decode($tdata, true);
|
||||
$this->db->update("ppob_tx", $this->Generate_SQL($adata), "No_TX=?", [$adata['No_TX']]);
|
||||
return ['Status'=>$adata['Status'], 'Message'=>$adata['Message']];
|
||||
}
|
||||
|
||||
public function CheckTrxByID(string $no_tx): array {
|
||||
$sign = $this->CreateSign("", "", $no_tx);
|
||||
$url = $this->ppob_server.$this->agen['API_AGENID']."/cek&agentx=".$no_tx."&sign=".$sign;
|
||||
$tdata = file_get_contents($url);
|
||||
$adata = json_decode($tdata, true);
|
||||
$this->Save_TX($adata);
|
||||
return ['Status'=>$adata['Status'], 'Message'=>$adata['Message']];
|
||||
}
|
||||
|
||||
public function CheckTrx(): array {
|
||||
$sign = $this->CreateSign("", "", "");
|
||||
$url = $this->ppob_server.$this->agen['API_AGENID']."/cek-all&sign=".$sign;
|
||||
$tdata = file_get_contents($url);
|
||||
$adata = json_decode($tdata, true);
|
||||
if ((int) $adata['Status'] === 1) {
|
||||
if (count($adata['Depo']) > 0) {
|
||||
foreach ($adata['Depo'] as $_row) {
|
||||
$this->Save_Depo($_row);
|
||||
}
|
||||
}
|
||||
if (count($adata['TX']) > 0) {
|
||||
foreach ($adata['TX'] as $_row) {
|
||||
$this->Save_TX($_row);
|
||||
}
|
||||
}
|
||||
if ($adata['Saldo']) {
|
||||
$this->db->runQuery("UPDATE ppob_config SET API_SALDO=? WHERE REG_ID=?", [$adata['Saldo'], $this->agen['REG_ID']]);
|
||||
}
|
||||
}
|
||||
return ['Status'=>$adata['Status'], 'Message'=>$adata['Message']];
|
||||
}
|
||||
|
||||
public function Balance(): array {
|
||||
$sign = $this->CreateSign("", "", "");
|
||||
$url = $this->ppob_server.$this->agen['API_AGENID']."/saldo&sign=".$sign;
|
||||
$tdata = file_get_contents($url);
|
||||
$adata = json_decode($tdata, true);
|
||||
if (count($adata) > 0 && (int) $adata['Status'] === 4) $this->db->runQuery("UPDATE ppob_config SET API_SALDO=? WHERE REG_ID=?", [$adata['Saldo'], $this->agen['REG_ID']]);
|
||||
return $adata;
|
||||
}
|
||||
|
||||
public function Produk(string $kate, string $ope): array {
|
||||
$sign = $this->CreateSign("", "", "");
|
||||
$url = $this->ppob_server.$this->agen['API_AGENID']."/produk&kategori=".$kate."&operator=".$ope."&sign=".$sign;
|
||||
$tdata = file_get_contents($url);
|
||||
$adata = json_decode($tdata, true);
|
||||
if (count($adata) > 0 && (int) $adata['Status'] === 4) {
|
||||
$this->db->runQuery("TRUNCATE TABLE import_produk");
|
||||
foreach ($adata['Data'] as $_row) {
|
||||
$copy = ['Kode','Operator','Kategori','Nama_Produk','Harga_Pokok','Status'];
|
||||
$insert = array_intersect_key($_row, array_flip($copy));
|
||||
$this->db->insert("import_produk", $insert);
|
||||
}
|
||||
$this->db->runQuery("UPDATE import_produk A, ppob_kategori B SET A.KateID=B.KateID WHERE A.Kategori=B.K_Kate");
|
||||
$this->db->runQuery("UPDATE import_produk A, ppob_operator B SET A.OpeID=B.OpeID WHERE A.Operator=B.K_Ope");
|
||||
$this->db->runQuery("INSERT IGNORE INTO ppob_produk (OpeID,KateID,Kode,Operator,Kategori,Nama_Produk,Harga_Pokok,Status) SELECT OpeID,KateID,Kode,Operator,Kategori,Nama_Produk,Harga_Pokok,Status FROM import_produk WHERE Kode NOT IN (SELECT Kode FROM ppob_produk)");
|
||||
$this->db->runQuery("UPDATE ppob_produk A,import_produk B SET A.Harga_Pokok=B.Harga_Pokok,A.Status=B.Status WHERE A.Kode=B.Kode");
|
||||
$this->db->runQuery("TRUNCATE TABLE import_produk");
|
||||
}
|
||||
return ['Status'=>$adata['Status'], 'Message'=>$adata['Message']];
|
||||
}
|
||||
|
||||
public function Save_Depo(array $adata): void {
|
||||
if (!isset($adata['No_Depo'])) return;
|
||||
$this->db->runQuery("UPDATE ppob_depo SET Proses=? WHERE No_Depo=?", [$adata['Proses'], $adata['No_Depo']]);
|
||||
}
|
||||
|
||||
public function Save_TX(array $adata): void {
|
||||
if (!isset($adata['No_TX'])) return;
|
||||
$this->db->update("ppob_tx", $this->Generate_SQL($adata), "No_TX=?", [$adata['No_TX']]);
|
||||
}
|
||||
|
||||
private function Generate_SQL($adata = []): array {
|
||||
$asql = [];
|
||||
foreach ($adata as $_key => $_val) {
|
||||
if ($_key === "Svr_TX") $asql['Svr_TX'] = $_val;
|
||||
if ($_key === "Entri_Stamp") $asql['Svr_Entri'] = $_val;
|
||||
if ($_key === "Update_Stamp") $asql['Svr_Update'] = $_val;
|
||||
if ($_key === "Status") {
|
||||
$asql['Status'] = $_val;
|
||||
$asql['Proses'] = ((int) $_val > 0) ? 1 : 0;
|
||||
}
|
||||
if ($_key === "Catatan") $asql['Catatan'] = $_val;
|
||||
if ($_key === "SN") $asql['SN'] = $_val;
|
||||
if ($_key === "Harga") $asql['Harga_Beli'] = $_val;
|
||||
if ($_key === "Saldo") {
|
||||
$this->db->runQuery("UPDATE ppob_config SET API_SALDO=? WHERE API_AGENID=?", [$_val, $this->agen['API_AGENID']]);
|
||||
}
|
||||
}
|
||||
return $asql;
|
||||
}
|
||||
|
||||
private function CreateSign(?string $kode, ?string $tujuan, ?string $no_tx): string {
|
||||
$templ = base64_encode(sha1("3FSP|".$this->agen['API_AGENID']."|".$kode."|".$tujuan."|".$no_tx."|".(string) $this->agen['API_PIN']."|".$this->agen['API_SECRET'], true));
|
||||
$sign = str_replace("/", "_", str_replace("+", "-", rtrim($templ, "=")));
|
||||
return $sign;
|
||||
}
|
||||
|
||||
public function Nomor_Baru(string $tipe = "P", string $tgl = ""): string {
|
||||
$atipe = ['P'=>["No_TX","ppob_tx"]];
|
||||
if (empty($tgl)) {
|
||||
$tno = $tipe . date("y") . abjad((int) date("n"));
|
||||
} else {
|
||||
$tmp = explode("-", $tgl);
|
||||
$tno = $tipe . substr((string) $tmp[2],2,2) . abjad((int) $tmp[1]);
|
||||
}
|
||||
$tno .= $this->id_server . $this->id_cabang;
|
||||
$ino = (int) $this->db->fetchField("SELECT MAX(RIGHT(".$atipe[$tipe][0].",4)) Nomor FROM ".$atipe[$tipe][1]." WHERE ".$atipe[$tipe][0]." LIKE '".$tno."%'") ?? 0;
|
||||
$tno .= isi_nol(++$ino, 4);
|
||||
return $tno;
|
||||
}
|
||||
|
||||
public static function SN_PLN(string $kode): array {
|
||||
$ahasil = ['Token'=>"", 'Nama'=>"", 'Daya'=>"", 'Kwh'=>""];
|
||||
if (!empty($kode)) {
|
||||
$atmp = explode("/", $kode);
|
||||
$ahasil = ['Token'=>$atmp[0], 'Nama'=>$atmp[1], 'Daya'=>$atmp[2]." / ".$atmp[3], 'Kwh'=>$atmp[4]];
|
||||
}
|
||||
return $ahasil;
|
||||
}
|
||||
|
||||
public static function Icon_Kate(?int $id): string {
|
||||
$aicon = [0=>"question-circle", 1=>"phone", 2=>"tablet", 3=>"envelope", 4=>"phone", 5=>"lightbulb", 6=>"wallet", 7=>"wallet", 8=>"ticket"];
|
||||
return '<i class="bi-'. $aicon[$id] .'"></i>';
|
||||
}
|
||||
|
||||
public static function Badge_Status(?int $id): string {
|
||||
$hasil = "";
|
||||
if ($id <= 1) $hasil = '<span class="badge rounded-pill bg-warning"><i class="bi-upload me-1"></i>Proses</span>';
|
||||
if ($id == 2) $hasil = '<span class="badge rounded-pill bg-danger"><i class="bi-x-lg me-1"></i>Gagal</span>';
|
||||
if ($id == 3) $hasil = '<span class="badge rounded-pill bg-secondary"><i class="bi-arrow-clockwise me-1"></i>Refund</span>';
|
||||
if ($id == 4) $hasil = '<span class="badge rounded-pill bg-success"><i class="bi-check-all me-1"></i>Sukses</span>';
|
||||
return $hasil;
|
||||
}
|
||||
|
||||
public static function makeWAText(?string $msg): string {
|
||||
$styles = [ '*' => 'strong', '_' => 'i', '~' => 'strike' ];
|
||||
return preg_replace_callback('/(?<!\w)([*~_])(.+?)\1(?!\w)/',
|
||||
function($m) use($styles) {
|
||||
return '<'. $styles[$m[1]]. '>'. $m[2]. '</'. $styles[$m[1]]. '>';
|
||||
},
|
||||
$msg);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user