feat: integrate activity log functionality in ProductPrice model and enhance change formatting in ActivityLogService
This commit is contained in:
parent
d74d120a6c
commit
1d5863efaf
@ -3,19 +3,21 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\PriceType;
|
||||
use App\Models\Concerns\InteractsWithActivityLog;
|
||||
use Illuminate\Database\Eloquent\Attributes\Appends;
|
||||
use Illuminate\Database\Eloquent\Attributes\Guarded;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Spatie\Activitylog\Support\LogOptions;
|
||||
|
||||
#[Guarded(['id'])]
|
||||
#[Appends(['price_formatted', 'price_input'])]
|
||||
class ProductPrice extends Model
|
||||
{
|
||||
// 1. Use Trait
|
||||
use HasFactory;
|
||||
use HasFactory, InteractsWithActivityLog;
|
||||
|
||||
// 2. Casting
|
||||
protected function casts(): array
|
||||
@ -26,18 +28,31 @@ protected function casts(): array
|
||||
];
|
||||
}
|
||||
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->logUnguarded()
|
||||
->logOnlyDirty()
|
||||
->dontLogEmptyChanges()
|
||||
->logExcept([
|
||||
'id',
|
||||
'variant_id',
|
||||
'deleted_at',
|
||||
]);
|
||||
}
|
||||
|
||||
// 3. Attribute
|
||||
public function priceFormatted(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => 'Rp '.number_format($this->price, 0, ',', '.'),
|
||||
get: fn() => 'Rp ' . number_format($this->price, 0, ',', '.'),
|
||||
);
|
||||
}
|
||||
|
||||
public function priceInput(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => (string) $this->price,
|
||||
get: fn() => (string) $this->price,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Enums\ActivityEventLabel;
|
||||
use App\Models\User;
|
||||
use App\Support\ActivityLog\FieldLabel;
|
||||
use App\Support\ActivityLog\ModelLabel;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@ -62,18 +63,32 @@ private function formatChanges(?Collection $changes): array
|
||||
$attributes = $changes->get('attributes', []);
|
||||
$old = $changes->get('old', []);
|
||||
|
||||
if (! is_array($attributes)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$formatted = [];
|
||||
|
||||
foreach ($attributes as $field => $newValue) {
|
||||
$formatted[] = [
|
||||
'field' => (string) $field,
|
||||
'old' => is_array($old) ? ($old[$field] ?? null) : null,
|
||||
'new' => $newValue,
|
||||
];
|
||||
if (is_array($attributes) && count($attributes) > 0) {
|
||||
foreach ($attributes as $field => $newValue) {
|
||||
if (! FieldLabel::isFormInput((string) $field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$formatted[] = [
|
||||
'field' => FieldLabel::for((string) $field),
|
||||
'old' => is_array($old) ? ($old[$field] ?? null) : null,
|
||||
'new' => $newValue,
|
||||
];
|
||||
}
|
||||
} elseif (is_array($old) && count($old) > 0) {
|
||||
foreach ($old as $field => $oldValue) {
|
||||
if (! FieldLabel::isFormInput((string) $field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$formatted[] = [
|
||||
'field' => FieldLabel::for((string) $field),
|
||||
'old' => $oldValue,
|
||||
'new' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
|
||||
297
app/Support/ActivityLog/FieldLabel.php
Normal file
297
app/Support/ActivityLog/FieldLabel.php
Normal file
@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support\ActivityLog;
|
||||
|
||||
class FieldLabel
|
||||
{
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private const LABELS = [
|
||||
// Common
|
||||
'name' => 'Nama',
|
||||
'description' => 'Deskripsi',
|
||||
'status' => 'Status',
|
||||
'amount' => 'Jumlah',
|
||||
'quantity' => 'Jumlah',
|
||||
'price' => 'Harga',
|
||||
'balance' => 'Saldo',
|
||||
'discount' => 'Diskon',
|
||||
'notes' => 'Keterangan',
|
||||
'reason' => 'Alasan',
|
||||
'type' => 'Jenis',
|
||||
'unit' => 'Satuan',
|
||||
'stock' => 'Stok',
|
||||
'retail_stock' => 'Stok Ecer',
|
||||
'reject_stock' => 'Stok Reject',
|
||||
|
||||
// Product
|
||||
'category_id' => 'Kategori',
|
||||
'category_ids' => 'Kategori',
|
||||
'product_variant_id' => 'Varian Produk',
|
||||
'variants' => 'Varian',
|
||||
'prices' => 'Harga',
|
||||
'is_active' => 'Status Aktif',
|
||||
|
||||
// Order
|
||||
'customer_id' => 'Pelanggan',
|
||||
'marketing_id' => 'Marketing',
|
||||
'channel' => 'Channel',
|
||||
'price_type' => 'Tipe Harga',
|
||||
'payment_type' => 'Tipe Pembayaran',
|
||||
'is_affiliate' => 'Pesanan Afiliasi',
|
||||
'tiktok_order_id' => 'ID Pesanan TikTok Shop',
|
||||
'shopee_order_id' => 'ID Pesanan Shopee',
|
||||
'nego_price' => 'Harga Nego',
|
||||
'items' => 'Item',
|
||||
'subtotal' => 'Subtotal',
|
||||
'total' => 'Total',
|
||||
'shipping_cost' => 'Ongkir',
|
||||
|
||||
// Purchase
|
||||
'supplier_id' => 'Supplier',
|
||||
|
||||
// Cutting
|
||||
'materials' => 'Bahan Baku',
|
||||
'results' => 'Hasil Produk',
|
||||
'sewing_cost' => 'Jasa Jahit',
|
||||
'other_cost' => 'Biaya Lainnya',
|
||||
'material_usage' => 'Pemakaian',
|
||||
'material_result' => 'Hasil',
|
||||
'combination_material_result' => 'Hasil Kombinasi',
|
||||
'cutting_result' => 'Hasil',
|
||||
'sample' => 'Sample',
|
||||
'original_outside_sample' => 'Diluar Sample',
|
||||
'product_name' => 'Nama Produk',
|
||||
|
||||
// Employee / HR
|
||||
'email' => 'Email',
|
||||
'username' => 'Username',
|
||||
'full_name' => 'Nama Lengkap',
|
||||
'phone_number' => 'Nomor Telepon',
|
||||
'gender' => 'Jenis Kelamin',
|
||||
'birth_date' => 'Tanggal Lahir',
|
||||
'address' => 'Alamat',
|
||||
'role' => 'Role',
|
||||
'join_date' => 'Tanggal Bergabung',
|
||||
'employment_status' => 'Status Kepegawaian',
|
||||
'base_salary' => 'Gaji Pokok',
|
||||
'start_date' => 'Tanggal Mulai',
|
||||
'end_date' => 'Tanggal Selesai',
|
||||
'due_date' => 'Jatuh Tempo',
|
||||
'opname_date' => 'Tanggal Opname',
|
||||
'stock_type' => 'Tipe Stok',
|
||||
'physical_stock' => 'Stok Fisik',
|
||||
|
||||
// Finance
|
||||
'payment' => 'Pembayaran',
|
||||
'paid_amount' => 'Jumlah Bayar',
|
||||
|
||||
// Attendance
|
||||
'photo' => 'Foto',
|
||||
'latitude' => 'Latitude',
|
||||
'longitude' => 'Longitude',
|
||||
'check_in' => 'Jam Masuk',
|
||||
'check_out' => 'Jam Pulang',
|
||||
|
||||
// System
|
||||
'app_name' => 'Nama Aplikasi',
|
||||
'about_app' => 'Tentang Aplikasi',
|
||||
'phone' => 'Nomor Telepon',
|
||||
'logo' => 'Logo',
|
||||
'favicon' => 'Favicon',
|
||||
'login_cover' => 'Cover Login',
|
||||
'permissions' => 'Hak Akses',
|
||||
'appearance' => 'Tampilan',
|
||||
'hero_image' => 'Foto Hero',
|
||||
'about_image' => 'Foto Tentang Kami',
|
||||
'gallery_s3_keys' => 'Koleksi Lookbook',
|
||||
'profile_s3_key' => 'Foto Profil',
|
||||
'profile_photo' => 'Foto Profil',
|
||||
|
||||
// Social media
|
||||
'instagram_url' => 'Instagram',
|
||||
'facebook_url' => 'Facebook',
|
||||
'tiktok_url' => 'TikTok',
|
||||
|
||||
// HR Settings
|
||||
'scheduled_check_in_time' => 'Jam Masuk Kerja',
|
||||
'scheduled_check_out_time' => 'Jam Pulang Kerja',
|
||||
'late_penalty_amount' => 'Denda Keterlambatan',
|
||||
'absent_penalty_amount' => 'Denda Bolos',
|
||||
|
||||
// Marketplace fees
|
||||
'tiktok_shop_platform_commission' => 'TikTok Shop - Komisi Platform',
|
||||
'tiktok_shop_logistics_service_fee' => 'TikTok Shop - Biaya Logistik',
|
||||
'tiktok_shop_dynamic_commission' => 'TikTok Shop - Komisi Dinamis',
|
||||
'tiktok_shop_order_processing_fee' => 'TikTok Shop - Biaya Proses Pesanan',
|
||||
'tiktok_shop_affiliate' => 'TikTok Shop - Komisi Affiliate',
|
||||
'tiktok_shop_pre_order_service_fee' => 'TikTok Shop - Biaya Pre Order',
|
||||
'shopee_admin_fee' => 'Shopee - Biaya Admin',
|
||||
'shopee_program_fee' => 'Shopee - Biaya Program',
|
||||
'shopee_shipping_savings' => 'Shopee - Hemat Biaya Kirim',
|
||||
'shopee_premium' => 'Shopee - Premi',
|
||||
'shopee_service_fee' => 'Shopee - Biaya Layanan',
|
||||
'shopee_order_processing_fee' => 'Shopee - Biaya Proses Pesanan',
|
||||
'shopee_ams_commission_fee' => 'Shopee - Komisi AMS',
|
||||
'shopee_pre_order' => 'Shopee - Pre Order',
|
||||
'shopee_live_extra' => 'Shopee - Live Extra',
|
||||
|
||||
// Media
|
||||
's3_keys' => 'Foto',
|
||||
'remove_media_ids' => 'Media yang Dihapus',
|
||||
];
|
||||
|
||||
/**
|
||||
* Field names that appear as form inputs in CRUD operations.
|
||||
* Auto-generated/computed fields (slug, subtotal, total, etc.) are excluded.
|
||||
*
|
||||
* @var array<string, true>
|
||||
*/
|
||||
private const FORM_INPUT_FIELDS = [
|
||||
// Common
|
||||
'name' => true,
|
||||
'description' => true,
|
||||
'status' => true,
|
||||
'amount' => true,
|
||||
'quantity' => true,
|
||||
'price' => true,
|
||||
'balance' => true,
|
||||
'discount' => true,
|
||||
'notes' => true,
|
||||
'reason' => true,
|
||||
'type' => true,
|
||||
'unit' => true,
|
||||
'stock' => true,
|
||||
'retail_stock' => true,
|
||||
'reject_stock' => true,
|
||||
|
||||
// Product
|
||||
'category_id' => true,
|
||||
'category_ids' => true,
|
||||
'product_variant_id' => true,
|
||||
'variants' => true,
|
||||
'prices' => true,
|
||||
'is_active' => true,
|
||||
|
||||
// Order
|
||||
'customer_id' => true,
|
||||
'marketing_id' => true,
|
||||
'channel' => true,
|
||||
'price_type' => true,
|
||||
'payment_type' => true,
|
||||
'is_affiliate' => true,
|
||||
'tiktok_order_id' => true,
|
||||
'shopee_order_id' => true,
|
||||
'nego_price' => true,
|
||||
'items' => true,
|
||||
'shipping_cost' => true,
|
||||
|
||||
// Purchase
|
||||
'supplier_id' => true,
|
||||
'raw_material_id' => true,
|
||||
'raw_material_price_id' => true,
|
||||
|
||||
// Cutting
|
||||
'materials' => true,
|
||||
'results' => true,
|
||||
'sewing_cost' => true,
|
||||
'other_cost' => true,
|
||||
'material_usage' => true,
|
||||
'material_result' => true,
|
||||
'combination_material_result' => true,
|
||||
'cutting_result' => true,
|
||||
'sample' => true,
|
||||
'original_outside_sample' => true,
|
||||
'product_name' => true,
|
||||
|
||||
// Employee / HR
|
||||
'email' => true,
|
||||
'username' => true,
|
||||
'full_name' => true,
|
||||
'phone_number' => true,
|
||||
'gender' => true,
|
||||
'birth_date' => true,
|
||||
'address' => true,
|
||||
'role' => true,
|
||||
'join_date' => true,
|
||||
'employment_status' => true,
|
||||
'base_salary' => true,
|
||||
'start_date' => true,
|
||||
'end_date' => true,
|
||||
'due_date' => true,
|
||||
'opname_date' => true,
|
||||
'stock_type' => true,
|
||||
'physical_stock' => true,
|
||||
|
||||
// Finance
|
||||
'payment' => true,
|
||||
'paid_amount' => true,
|
||||
|
||||
// Attendance
|
||||
'photo' => true,
|
||||
'latitude' => true,
|
||||
'longitude' => true,
|
||||
'check_in' => true,
|
||||
'check_out' => true,
|
||||
|
||||
// System
|
||||
'app_name' => true,
|
||||
'about_app' => true,
|
||||
'phone' => true,
|
||||
'logo' => true,
|
||||
'favicon' => true,
|
||||
'login_cover' => true,
|
||||
'permissions' => true,
|
||||
'appearance' => true,
|
||||
'hero_image' => true,
|
||||
'about_image' => true,
|
||||
'gallery_s3_keys' => true,
|
||||
'profile_s3_key' => true,
|
||||
'profile_photo' => true,
|
||||
'unit_price' => true,
|
||||
'variant' => true,
|
||||
|
||||
// Social media
|
||||
'instagram_url' => true,
|
||||
'facebook_url' => true,
|
||||
'tiktok_url' => true,
|
||||
|
||||
// HR Settings
|
||||
'scheduled_check_in_time' => true,
|
||||
'scheduled_check_out_time' => true,
|
||||
'late_penalty_amount' => true,
|
||||
'absent_penalty_amount' => true,
|
||||
|
||||
// Marketplace fees
|
||||
'tiktok_shop_platform_commission' => true,
|
||||
'tiktok_shop_logistics_service_fee' => true,
|
||||
'tiktok_shop_dynamic_commission' => true,
|
||||
'tiktok_shop_order_processing_fee' => true,
|
||||
'tiktok_shop_affiliate' => true,
|
||||
'tiktok_shop_pre_order_service_fee' => true,
|
||||
'shopee_admin_fee' => true,
|
||||
'shopee_program_fee' => true,
|
||||
'shopee_shipping_savings' => true,
|
||||
'shopee_premium' => true,
|
||||
'shopee_service_fee' => true,
|
||||
'shopee_order_processing_fee' => true,
|
||||
'shopee_ams_commission_fee' => true,
|
||||
'shopee_pre_order' => true,
|
||||
'shopee_live_extra' => true,
|
||||
|
||||
// Media
|
||||
's3_keys' => true,
|
||||
'remove_media_ids' => true,
|
||||
];
|
||||
|
||||
public static function for(string $field): string
|
||||
{
|
||||
return self::LABELS[$field] ?? ucfirst(str_replace('_', ' ', $field));
|
||||
}
|
||||
|
||||
public static function isFormInput(string $field): bool
|
||||
{
|
||||
return isset(self::FORM_INPUT_FIELDS[$field]);
|
||||
}
|
||||
}
|
||||
@ -20,6 +20,7 @@
|
||||
use App\Models\PayrollAdjustment;
|
||||
use App\Models\PayrollPeriod;
|
||||
use App\Models\Product;
|
||||
use App\Models\ProductPrice;
|
||||
use App\Models\ProductVariant;
|
||||
use App\Models\Purchase;
|
||||
use App\Models\PurchaseItem;
|
||||
@ -58,6 +59,7 @@ class ModelLabel
|
||||
PayrollAdjustment::class => 'Penyesuaian Gaji',
|
||||
PayrollPeriod::class => 'Periode Gaji',
|
||||
Product::class => 'Produk',
|
||||
ProductPrice::class => 'Harga Produk',
|
||||
ProductVariant::class => 'Varian Produk',
|
||||
Purchase::class => 'Belanja',
|
||||
PurchaseItem::class => 'Item Belanja',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user