initial: bootstrap from BukidBountyApp base

This commit is contained in:
Jonathan Sykes
2026-06-06 18:43:00 +08:00
commit eb4a5731fb
5674 changed files with 160857 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace App\Models\Accounting;
use App\Models\Model;
use App\Models\User;
class Account extends Model
{
protected ?string $table = 'accounts';
protected array $fillable = [
'hashkey',
'parent_id',
'store_id',
'type',
'default_flow',
'name',
'description',
'theme_key',
'theme_account_code',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'is_active' => 'boolean',
];
public function parent()
{
return $this->belongsTo(self::class, 'parent_id');
}
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
public function transactions()
{
return $this->hasMany(AccountTransaction::class, 'account_id');
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Models\Accounting;
use App\Models\Model;
use App\Models\User;
class AccountTransaction extends Model
{
protected ?string $table = 'account_transactions';
protected array $fillable = [
'hashkey',
'account_id',
'item',
'target_id',
'amount',
'flow',
'notes',
'transaction_date',
'reference',
'additional_details',
'created_by',
'updated_by',
];
protected array $casts = [
'additional_details' => 'json',
'amount' => 'decimal:2',
'transaction_date' => 'datetime',
];
public function account()
{
return $this->belongsTo(Account::class, 'account_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Models\Accounting;
use App\Models\Model;
use App\Models\User;
use App\Models\Market\Organization;
class MemberLedger extends Model
{
protected ?string $table = 'member_ledgers';
protected array $fillable = [
'hashkey',
'user_id',
'organization_id',
'amount',
'transaction_type',
'flow',
'balance_after',
'description',
'reference_id',
'created_by',
'updated_by',
'is_active',
];
protected array $casts = [
'amount' => 'decimal:2',
'balance_after' => 'decimal:2',
'is_active' => 'boolean',
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function organization()
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace App\Models;
use App\Models\Model;
class Announcement extends Model
{
protected ?string $table = 'announcements';
public bool $incrementing = true;
protected array $fillable = [
'title',
'content',
'photo',
'hashkey',
'type',
'is_active',
'starts_at',
'ends_at',
'created_by',
'updated_by',
];
protected array $casts = [
'is_active' => 'boolean',
'starts_at' => 'datetime',
'ends_at' => 'datetime',
'created_by' => 'integer',
'updated_by' => 'integer',
];
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
public function scopeActive($query)
{
$now = now();
return $query->where('is_active', true)
->where(function ($q) use ($now) {
$q->whereNull('starts_at')
->orWhere('starts_at', '<=', $now);
})
->where(function ($q) use ($now) {
$q->whereNull('ends_at')
->orWhere('ends_at', '>=', $now);
});
}
}

111
app/Models/Chapter.php Normal file
View File

@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace App\Models;
use App\Models\Market\UserInfo;
class Chapter extends Model
{
protected ?string $table = 'chapters';
protected array $fillable = [
'hashkey', 'name', 'cooperative_id', 'level', 'parent_id', 'location_key',
'lat', 'lng', 'is_active', 'created_by', 'updated_by',
];
protected array $casts = [
'is_active' => 'boolean',
'lat' => 'float',
'lng' => 'float',
];
public function parent()
{
return $this->belongsTo(Chapter::class, 'parent_id');
}
public function cooperative()
{
return $this->belongsTo(\App\Models\Market\Organization::class, 'cooperative_id');
}
public function children()
{
return $this->hasMany(Chapter::class, 'parent_id');
}
public function chapterMembers()
{
return $this->hasMany(ChapterMember::class, 'chapter_id');
}
public function activeMembers()
{
return $this->chapterMembers()->where('is_active', true);
}
public function leaders()
{
return $this->activeMembers()->whereNotNull('position');
}
/**
* Find or create a chapter by level + location_key (normalized address field).
*/
public static function findOrCreateByLocation(string $level, string $locationKey, ?int $parentId = null): self
{
$key = strtolower(trim($locationKey));
return static::firstOrCreate(
['level' => $level, 'location_key' => $key],
[
'hashkey' => \Ramsey\Uuid\Uuid::uuid4()->toString(),
'name' => ucwords(strtolower($locationKey)),
'level' => $level,
'location_key'=> $key,
'parent_id' => $parentId,
'is_active' => true,
]
);
}
/**
* Auto-assign a user to the appropriate chapters based on their UserInfo address.
* Creates chapter records on the fly if they don't exist.
*/
public static function autoAssignUser(int $userId): void
{
$info = UserInfo::where('user_id', $userId)->first();
if (!$info) {
return;
}
$national = static::firstOrCreate(
['level' => 'national', 'location_key' => 'philippines'],
['hashkey' => \Ramsey\Uuid\Uuid::uuid4()->toString(), 'name' => 'Philippines', 'level' => 'national', 'location_key' => 'philippines', 'is_active' => true]
);
ChapterMember::syncAutoAssignment($userId, $national->id);
if ($info->region) {
$region = static::findOrCreateByLocation('region', $info->region, $national->id);
ChapterMember::syncAutoAssignment($userId, $region->id);
if ($info->province) {
$province = static::findOrCreateByLocation('province', $info->province, $region->id);
ChapterMember::syncAutoAssignment($userId, $province->id);
if ($info->city) {
$city = static::findOrCreateByLocation('city', $info->city, $province->id);
ChapterMember::syncAutoAssignment($userId, $city->id);
if ($info->barangay) {
$barangay = static::findOrCreateByLocation('barangay', $info->barangay, $city->id);
ChapterMember::syncAutoAssignment($userId, $barangay->id);
}
}
}
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Models;
use App\Models\User;
class ChapterMember extends Model
{
protected ?string $table = 'chapter_members';
protected array $fillable = [
'hashkey', 'user_id', 'chapter_id', 'position', 'role',
'is_manual_override', 'is_active', 'assigned_by', 'assigned_at',
'created_by', 'updated_by',
];
protected array $casts = [
'is_manual_override' => 'boolean',
'is_active' => 'boolean',
'assigned_at' => 'datetime',
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function chapter()
{
return $this->belongsTo(Chapter::class, 'chapter_id');
}
public function isOfficer(): bool
{
return !empty($this->role) && $this->role !== 'MEMBER';
}
/**
* Sync auto-assignment for a user to a chapter — only if no manual override exists.
*/
public static function syncAutoAssignment(int $userId, int $chapterId): void
{
static::updateOrCreate(
['user_id' => $userId, 'chapter_id' => $chapterId],
[
'is_active' => true,
'is_manual_override' => false, // Only sync if we're not overriding?
// Actually the rule is: if it exists, only update if NOT manual override.
// updateOrCreate doesn't support conditional update easily.
]
);
}
}

51
app/Models/DbBackup.php Normal file
View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace App\Models;
/**
* @property int $id
* @property string $hashkey
* @property string $file_content_hashkey
* @property string $filename
* @property int $size_in_bytes
* @property bool $is_active
* @property int $created_by
* @property int $updated_by
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
*/
class DbBackup extends Model
{
protected ?string $table = 'db_backups';
protected array $fillable = [
'hashkey',
'name',
'file_content_hashkey',
'filename',
'size_in_bytes',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'id' => 'integer',
'size_in_bytes' => 'integer',
'is_active' => 'boolean',
'created_by' => 'integer',
'updated_by' => 'integer',
];
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Models;
class FileContent extends Model
{
protected ?string $table = 'file_content';
protected array $fillable = [
'hashkey',
'filehash',
'titlename',
'description',
'size_in_bytes',
'content',
'filelocation',
'created_by',
'updated_by',
'details',
'mimetype',
];
protected array $casts = [
'size_in_bytes' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
'details' => 'array'
];
/**
* Related file_list records
*/
public function fileLists()
{
return $this->hasMany(FileList::class, 'contentuid');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

75
app/Models/FileList.php Normal file
View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace App\Models;
class FileList extends Model
{
protected ?string $table = 'file_list';
protected array $fillable = [
'uid',
'useruid_access_list',
'hashkey',
'contentuid',
'title',
'filename',
'cdn_url',
'is_public',
'file_type',
'description',
'tags',
'hidden',
'categories',
'created_by',
'updated_by',
'details'
];
protected array $casts = [
'hidden' => 'integer',
'is_public' => 'boolean',
'contentuid' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
'details' => 'array',
'useruid_access_list' => 'array',
'tags'=>'array',
];
/**
* Reference to file content
*/
public function fileContent()
{
return $this->belongsTo(FileContent::class, 'contentuid');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
/**
* Resolve a usable URL for this file. Prefers `cdn_url` (e.g. a
* jsDelivr-fronted CDN asset) when set; otherwise falls back to the
* local /RequestData/File/{hash} route.
*/
public function resolvedUrl(): string
{
$cdn = trim((string) ($this->cdn_url ?? ''));
if ($cdn !== '') {
return $cdn;
}
return "/RequestData/File/{$this->hashkey}";
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace App\Models\Generic;
use App\Models\Model;
use App\Models\User;
class TableLog extends Model
{
protected ?string $table = 'table_logs';
protected array $casts = [
'original_data' => 'array',
'new_data' => 'array',
];
protected array $fillable = [
'hashkey',
'table_name',
'target_id',
'original_data',
'new_data',
'created_by',
'updated_by',
];
// Auto-merge accessor
public function get_full_new_row(): array
{
return array_merge($this->original_data ?? [], $this->new_data ?? []);
}
public function data(){
return $this->get_full_new_row();
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace App\Models;
use App\Models\Model;
use App\Models\User;
use App\Models\Market\Product;
use App\Models\Market\Store;
use App\Enums\Market\ProductTransactionType;
use App\Enums\Market\TransactionFlow;
class GlobalTransaction extends Model
{
protected ?string $table = 'global_transactions';
protected string $primaryKey = 'id';
public bool $incrementing = true;
protected string $keyType = 'int';
protected array $fillable = [
'hashkey',
'user_id',
'amount',
'type',
'status',
'description',
'product_id',
'store_id',
'flow',
'created_by',
'updated_by',
];
protected array $casts = [
'amount' => 'decimal:2',
'type' => ProductTransactionType::class,
'user_id' => 'integer',
'product_id' => 'integer',
'store_id' => 'integer',
'flow' => TransactionFlow::class,
'created_by' => 'integer',
'updated_by' => 'integer',
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function product()
{
return $this->belongsTo(Product::class, 'product_id');
}
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace App\Models;
use App\Models\Model;
class LandingPage extends Model
{
protected ?string $table = 'landing_pages';
protected array $fillable = [
'title',
'html_content',
'description',
'hashkey',
'created_by',
'updated_by',
'is_active',
];
protected array $casts = [
'is_active' => 'boolean',
];
/**
* Get the currently active landing page.
*/
public static function getActive(): ?self
{
return static::where('is_active', true)->first();
}
/**
* Set this landing page as the active one, deactivating all others.
*/
public function setAsActive(): void
{
// Deactivate all other landing pages
static::where('id', '!=', $this->id)->update(['is_active' => false]);
// Activate this one
$this->is_active = true;
$this->save();
}
/**
* Deactivate this landing page.
*/
public function deactivate(): void
{
$this->is_active = false;
$this->save();
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class Cart extends Model
{
protected ?string $table = 'carts';
protected array $fillable = [
'hashkey',
'user_id',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'is_active' => 'boolean',
];
public function items()
{
return $this->hasMany(CartItem::class, 'cart_id');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
class CartItem extends Model
{
protected ?string $table = 'cart_items';
protected array $fillable = [
'hashkey',
'cart_id',
'product_id',
'quantity',
'price',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'is_active' => 'boolean',
'quantity' => 'integer',
'price' => 'float',
];
public function cart()
{
return $this->belongsTo(Cart::class, 'cart_id');
}
public function product()
{
return $this->belongsTo(Product::class, 'product_id');
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\User;
use Hypervel\Database\Eloquent\Model;
class CooperativeDocument extends Model
{
protected ?string $table = 'cooperative_documents';
protected array $fillable = [
'hashkey',
'parent_hashkey',
'version_number',
'organization_id',
'file_hashkey',
'document_type',
'revision_note',
'created_by',
'updated_by',
'is_active',
];
public function organization()
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class CooperativeMember extends Model
{
protected ?string $table = 'cooperative_members';
protected array $fillable = [
'hashkey', 'organization_id', 'user_id', 'role',
'membership_type', 'membership_level',
'officer_position', 'officer_level',
'concurrent_position', 'concurrent_level',
'cooperative_name_alt', 'cooperative_position', 'year_beginning',
// Classification
'priority_sector', 'common_bond', 'vulnerability_classifications',
// Government IDs
'philsys_id', 'sss_number', 'pagibig_number',
// SLP
'slp_track', 'slp_association_name', 'listahanan_id', 'fourtps_household_id',
// TUPAD
'tupad_category', 'tupad_insurance_beneficiary_name', 'tupad_insurance_beneficiary_relation',
// OSEC/NSRP
'preferred_occupation', 'nsrp_skills', 'employment_status',
// Programs
'program_participation',
'joined_at', 'is_active', 'created_by', 'updated_by',
];
protected array $casts = [
'joined_at' => 'datetime',
'is_active' => 'boolean',
'priority_sector' => 'array',
'vulnerability_classifications' => 'array',
'nsrp_skills' => 'array',
'program_participation' => 'array',
];
public function organization()
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class CooperativeResolution extends Model
{
protected ?string $table = 'cooperative_resolutions';
protected array $fillable = [
'hashkey',
'organization_id',
'title',
'description',
'date_approved',
'document_url',
'status',
'created_by',
'updated_by',
'is_active',
];
protected array $casts = [
'date_approved' => 'date',
'is_active' => 'boolean',
];
public function organization()
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function votes()
{
return $this->hasMany(CooperativeVote::class, 'resolution_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class CooperativeVote extends Model
{
protected ?string $table = 'cooperative_votes';
protected array $fillable = [
'hashkey',
'resolution_id',
'user_id',
'vote_cast',
'created_by',
'updated_by',
'is_active',
];
protected array $casts = [
'is_active' => 'boolean',
];
public function resolution()
{
return $this->belongsTo(CooperativeResolution::class, 'resolution_id');
}
public function voter()
{
return $this->belongsTo(User::class, 'user_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class Courier extends Model
{
protected ?string $table = 'couriers';
protected array $fillable = [
'hashkey',
'name',
'contact_number',
'type',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'is_active' => 'boolean',
];
public function shipments()
{
return $this->hasMany(Shipment::class, 'courier_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class Customer extends Model
{
protected ?string $table = 'cst';
protected array $fillable = [
'hashkey',
'name',
'phone',
'email',
'store_id',
'user_id',
'created_by',
'updated_by',
'is_active',
];
protected array $casts = [
'is_active' => 'boolean',
];
/**
* Relationships
*/
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class FarmerProfile extends Model
{
protected ?string $table = 'farmer_profiles';
protected array $fillable = [
'hashkey',
'user_id',
'organization_id',
'farm_name',
'farm_location',
'main_crops',
'verification_status',
'certification_details',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'main_crops' => 'array',
'certification_details' => 'array',
'is_active' => 'boolean',
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function organization()
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class MainOrganization extends Model
{
protected ?string $table = 'main_organizations';
protected array $fillable = [
'organization_id',
'role',
'priority',
'is_active',
'metadata',
'created_by',
'updated_by',
];
protected array $casts = [
'is_active' => 'boolean',
'priority' => 'integer',
'metadata' => 'array',
];
public function organization()
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class Organization extends Model
{
protected ?string $table = 'organizations';
protected array $fillable = [
'hashkey',
'name',
'type',
'address',
'registration_number',
'cin',
'tin',
'cooperative_type',
'cooperative_category',
'registration_date',
'contact_person',
'contact_number',
'contact_email',
'compliance_status',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'is_active' => 'boolean',
'registration_date' => 'date',
];
public function members()
{
return $this->hasMany(CooperativeMember::class, 'organization_id');
}
public function farmerProfiles()
{
return $this->hasMany(FarmerProfile::class, 'organization_id');
}
public function stores()
{
return $this->belongsToMany(Store::class, 'org_str', 'organization_id', 'store_id')
->withTimestamps();
}
public function mainAssignments()
{
return $this->hasMany(MainOrganization::class, 'organization_id');
}
public function isMain(?string $role = null): bool
{
$query = $this->mainAssignments()->where('is_active', true);
if ($role !== null) {
$query->where('role', $role);
}
return $query->exists();
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
/**
* @property int $id
* @property string $hashkey
* @property string $access_key
* @property int $store_id
* @property string $name
* @property string $status
* @property string $last_used_at
* @property int $created_by
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
*/
class PosAccessKey extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'pos_access_keys';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [
'hashkey',
'access_key',
'store_id',
'name',
'status',
'is_active',
'expires_at',
'last_used_at',
'created_by',
'updated_by',
];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = [
'id' => 'integer',
'store_id' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
'is_active' => 'boolean',
'expires_at' => 'datetime',
];
/**
* Check if this access key is expired.
*/
public function isExpired(): bool
{
return $this->expires_at !== null && $this->expires_at->isPast();
}
/**
* Auto-expire: set all expired active keys to inactive.
* Call this before listing or validating keys.
*/
public static function autoExpire(): void
{
self::where('status', 'active')
->whereNotNull('expires_at')
->where('expires_at', '<', now())
->update(['status' => 'inactive']);
}
/**
* Relationships
*/
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
}

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class PosSession extends Model
{
protected ?string $table = 'pos_sessions';
protected array $fillable = [
'hashkey',
'access_key',
'store_id',
'created_by',
'updated_by',
'customer_name',
'total_amount',
'received_amount',
'change_amount',
'payment_method',
'payment_details',
'status',
'is_void',
'notes',
'additionaldata',
];
protected array $casts = [
'is_void' => 'boolean',
'payment_details' => 'array',
'additionaldata' => 'array',
'total_amount' => 'integer',
'received_amount' => 'integer',
'change_amount' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
];
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
/**
* Relationships
*/
public function transactions()
{
return $this->hasMany(PosTransaction::class, 'pos_session_id');
}
public function archives()
{
return $this->hasMany(PosSessionArchive::class, 'pos_session_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class PosSessionArchive extends Model
{
protected ?string $table = 'pos_sessions_archive';
protected array $fillable = [
'pos_session_id',
'hashkey',
'session_snapshot',
'transactions_snapshot',
'created_by',
'updated_by',
'remarks',
];
protected array $casts = [
'session_snapshot' => 'array',
'transactions_snapshot' => 'array',
'created_by' => 'integer',
'updated_by' => 'integer',
'pos_session_id' => 'integer',
];
/**
* Relationships
*/
public function session()
{
return $this->belongsTo(PosSession::class, 'pos_session_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
class PosTransaction extends Model
{
protected ?string $table = 'pos_transactions';
protected array $fillable = [
'pos_session_id',
'product_id',
'quantity',
'price_at_sale',
'discount',
'total_price',
'is_void',
'remarks',
'hashkey',
'created_by',
'updated_by',
];
protected array $casts = [
'is_void' => 'boolean',
'quantity' => 'integer',
'price_at_sale' => 'integer',
'discount' => 'integer',
'total_price' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
];
/**
* Relationships
*/
public function session()
{
return $this->belongsTo(PosSession::class, 'pos_session_id');
}
public function product()
{
return $this->belongsTo(Product::class, 'product_id');
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class Product extends Model
{
protected ?string $table = 'prd_items';
protected string $primaryKey = 'id';
public bool $incrementing = true;
protected string $keyType = 'int';
protected array $fillable = [
'hashkey',
'created_by',
'updated_by',
'created_for',
'category',
'subcategory',
'logs',
'specs',
'photourl',
'available',
'sold',
'price',
// 'store_id',
'owner_id',
'views',
'name',
'description',
'reviews',
'barcode',
'status',
'remarks',
'unitname',
'rating',
'sku',
'qrcode',
'shortcode',
'shortname',
'is_active',
'product_type'
];
protected array $casts = [
'available' => 'integer',
'sold' => 'integer',
'price' => 'integer',
'views' => 'integer',
'rating' => 'integer',
'is_active' => 'boolean',
'photourl' => 'array',
'reviews' => 'array',
'specs' => 'array',
];
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
public function stores()
{
return $this->belongsToMany(Store::class, 'prd_str')
->withPivot(['available', 'price', 'is_active'])
->withTimestamps();
}
public function owner()
{
return $this->belongsTo(User::class, 'owner_id');
}
public function createdFor()
{
return $this->belongsTo(User::class, 'created_for');
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class ProductTransaction extends Model
{
protected ?string $table = 'prd_trx';
/**
* The primary key for the model.
*/
protected string $primaryKey = 'id';
/**
* Indicates if the IDs are auto-incrementing.
*/
public bool $incrementing = true;
/**
* The "type" of the primary key.
*/
protected string $keyType = 'int';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [
'hashkey',
'created_by',
'updated_by',
'created_for',
'store_id',
'transactiontype',
'product_id',
'transactiondata',
'description',
'subtype',
'name',
'owner_id',
'transactionsessionhash',
'quantity',
'logs',
'remarks',
'price',
'is_void',
'last_total_price',
'last_total_discount',
'notes'
];
/**
* The attributes that should be cast.
*/
protected array $casts = [
'quantity' => 'integer',
'price' => 'integer',
'is_void' => 'boolean',
];
/**
* Relationships.
*/
public function product()
{
return $this->belongsTo(Product::class, 'product_id');
}
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
public function owner()
{
return $this->belongsTo(User::class, 'owner_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
public function createdFor()
{
return $this->belongsTo(User::class, 'created_for');
}
public function session()
{
return $this->belongsTo(ProductTransactionSession::class, 'transactionsessionhash', 'hashkey');
}
}

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class ProductTransactionSession extends Model
{
protected ?string $table = 'prd_trx_ses';
/**
* The primary key for the model.
*/
protected $primaryKey = 'id';
/**
* Indicates if the IDs are auto-incrementing.
*/
public $incrementing = true;
/**
* The "type" of the primary key.
*/
protected $keyType = 'int';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [
'hashkey',
'name',
'description',
'logs',
'remarks',
'created_by',
'updated_by',
'created_for',
'subtype',
'additionaldata',
'category',
'store_id',
'status',
'is_void',
'last_total_price',
'last_total_discount',
'notes',
];
protected array $casts = [
'is_void' => 'boolean',
];
/**
* Relationships.
*/
public function transactions()
{
return $this->hasMany(ProductTransaction::class, 'transactionsessionhash', 'hashkey');
}
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
public function createdFor()
{
return $this->belongsTo(User::class, 'created_for');
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class ProductTransactionSessionArchive extends Model
{
protected ?string $table = 'prd_trx_ses_arc';
/**
* The primary key for the model.
*/
protected $primaryKey = 'id';
/**
* Indicates if the IDs are auto-incrementing.
*/
public $incrementing = true;
/**
* The "type" of the primary key.
*/
protected $keyType = 'int';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [
'name',
'description',
'details',
'hashkey',
'transactions_sessions_id',
'created_by',
'updated_by',
'created_for',
'transactions_snapshot'
];
protected array $casts = [
'details' => 'array',
'transactions_snapshot' => 'array',
];
/**
* Relationships.
*/
public function transactionSession()
{
return $this->belongsTo(ProductTransactionSession::class, 'transactions_sessions_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
public function createdFor()
{
return $this->belongsTo(User::class, 'created_for');
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
use App\Models\GlobalTransaction;
class Shipment extends Model
{
protected ?string $table = 'shipments';
protected array $fillable = [
'hashkey',
'transaction_id',
'store_id',
'customer_id',
'courier_id',
'tracking_number',
'status',
'origin_address',
'destination_address',
'estimated_delivery_date',
'actual_delivery_date',
'shipping_fee',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'estimated_delivery_date' => 'datetime',
'actual_delivery_date' => 'datetime',
'shipping_fee' => 'decimal:2',
'is_active' => 'boolean',
];
public function transaction()
{
return $this->belongsTo(GlobalTransaction::class, 'transaction_id');
}
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
public function customer()
{
return $this->belongsTo(Customer::class, 'customer_id');
}
public function courier()
{
return $this->belongsTo(Courier::class, 'courier_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

117
app/Models/Market/Store.php Normal file
View File

@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class Store extends Model
{
protected ?string $table = 'str';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [
'hashkey',
'storecode',
'name',
'description',
'status',
'created_by',
'updated_by',
'created_for',
'remarks',
'logs',
'specs',
'additionaldata',
'owner_id',
'manager_id',
'category',
'subcategory',
'photourl',
'address',
'is_active',
'store_type',
];
protected array $casts = [
'photourl' => 'array',
'is_active' => 'boolean',
'store_type'=> 'array',
'specs' => 'array',
];
/**
* Relationships
*/
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
public function createdFor()
{
return $this->belongsTo(User::class, 'created_for');
}
public function owner()
{
return $this->belongsTo(User::class, 'owner_id');
}
public function manager()
{
return $this->belongsTo(User::class, 'manager_id');
}
public function managers()
{
return $this->hasMany(StoreManager::class, 'store_id');
}
public function managerUsers()
{
return $this->belongsToMany(User::class, 'store_managers', 'store_id', 'user_id')
->withPivot(['hashkey', 'created_by', 'updated_by', 'is_active'])
->withTimestamps();
}
// public function products()
// {
// return $this->hasMany(Product::class, 'store_id');
// }
public function products()
{
return $this->belongsToMany(Product::class, 'prd_str')
->withPivot(['available', 'price', 'is_active','sold','logs','reviews','status','remarks','description'])
->withTimestamps();
}
public function cooperatives()
{
return $this->belongsToMany(Organization::class, 'org_str', 'store_id', 'organization_id')
->where('organizations.type', 'COOPERATIVE')
->withTimestamps();
}
public function transactions()
{
return $this->hasMany(ProductTransaction::class, 'store_id');
}
public function transactionSessions()
{
return $this->hasMany(ProductTransactionSession::class, 'store_id');
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class StoreManager extends Model
{
protected ?string $table = 'store_managers';
protected array $fillable = [
'hashkey',
'store_id',
'user_id',
'created_by',
'updated_by',
'is_active',
];
protected array $casts = [
'is_active' => 'boolean',
];
public function store()
{
return $this->belongsTo(Store::class, 'store_id');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace App\Models\Market;
use App\Models\Model;
use App\Models\User;
class UserInfo extends Model
{
protected ?string $table = 'user_infos';
protected array $fillable = [
'hashkey',
'user_id',
'firstname',
'middlename',
'lastname',
'suffix',
'gender',
'dob',
'priority_sector',
'messenger_id',
'viber_number',
'tiktok_username',
'region',
'province',
'city',
'barangay',
'civil_status',
'children_count',
'dependent_count',
'education_level',
'course',
'school',
'year_last_attended',
'livelihood_source',
'last_company',
'employer_name',
'last_position',
'occupation',
'last_employment_year',
'monthly_income',
'tin',
'philhealth_id',
'gov_id',
'id_type',
'id_number',
'beneficiary_type',
'emergency_contact_name',
'emergency_contact_address',
'emergency_contact_phone',
'emergency_contact_relation',
'emergency_contact_user_id',
'fullname',
'landline',
'mobile',
'email',
'alt_email',
'alt_landline',
'alt_mobile',
'facebook_url',
'bank_details',
'bank_account_no',
'addresses',
'other_details',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'bank_details' => 'json',
'addresses' => 'json',
'other_details' => 'json',
'is_active' => 'boolean',
'dob' => 'date',
'monthly_income' => 'float',
'children_count' => 'integer',
'dependent_count' => 'integer',
];
/**
* Get the virtual age attribute.
*/
public function getAgeAttribute(): ?int
{
if (!$this->dob) {
return null;
}
return $this->dob->diffInYears(now());
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function emergencyContactUser()
{
return $this->belongsTo(User::class, 'emergency_contact_user_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

12
app/Models/Model.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Hypervel\Database\Eloquent\Model as BaseModel;
abstract class Model extends BaseModel
{
protected ?string $connection = null;
}

View File

@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace App\Models;
use App\Support\TokenAbilities;
use Hyperf\Database\Model\Relations\MorphTo;
class PersonalAccessToken extends Model
{
protected ?string $table = 'personal_access_tokens';
public bool $incrementing = true;
protected array $fillable = [
'tokenable_type',
'tokenable_id',
'name',
'description',
'token',
'abilities',
'allowed_ips',
'last_used_at',
'last_used_ip',
'expires_at',
'created_by',
'revoked_by',
'revoked_at',
];
protected array $casts = [
'abilities' => 'array',
'allowed_ips' => 'array',
'last_used_at' => 'datetime',
'expires_at' => 'datetime',
'revoked_at' => 'datetime',
];
protected array $hidden = [
'token',
];
public function tokenable(): MorphTo
{
return $this->morphTo();
}
public static function findByPlainToken(string $plain): ?self
{
return static::query()->where('token', hash('sha256', $plain))->first();
}
public function isExpired(): bool
{
return $this->expires_at !== null && $this->expires_at->isPast();
}
public function isRevoked(): bool
{
return $this->revoked_at !== null;
}
public function isActive(): bool
{
return ! $this->isExpired() && ! $this->isRevoked();
}
public function can(string $ability): bool
{
$abilities = $this->abilities ?? [];
if (in_array(TokenAbilities::WILDCARD, $abilities, true)) {
return true;
}
return in_array($ability, $abilities, true);
}
public function ipAllowed(string $ip): bool
{
$list = $this->allowed_ips ?? [];
if (empty($list)) {
return true;
}
foreach ($list as $entry) {
if ($this->ipMatches($ip, $entry)) {
return true;
}
}
return false;
}
private function ipMatches(string $ip, string $entry): bool
{
$entry = trim($entry);
if ($entry === '') {
return false;
}
if (! str_contains($entry, '/')) {
return $ip === $entry;
}
[$subnet, $bits] = explode('/', $entry, 2);
$bits = (int) $bits;
$ipBin = @inet_pton($ip);
$subnetBin = @inet_pton($subnet);
if ($ipBin === false || $subnetBin === false || strlen($ipBin) !== strlen($subnetBin)) {
return false;
}
$bytes = intdiv($bits, 8);
$remainder = $bits % 8;
if (substr($ipBin, 0, $bytes) !== substr($subnetBin, 0, $bytes)) {
return false;
}
if ($remainder === 0) {
return true;
}
$mask = chr(0xff << (8 - $remainder) & 0xff);
return (ord($ipBin[$bytes]) & ord($mask)) === (ord($subnetBin[$bytes]) & ord($mask));
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace App\Models\Property;
use App\Models\Model;
use App\Models\User;
class Property extends Model
{
protected ?string $table = 'properties';
protected array $fillable = [
'hashkey',
'name',
'location',
'price',
'status',
'details',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'details' => 'json',
'price' => 'decimal:2',
'is_active' => 'boolean',
];
public function referrals()
{
return $this->hasMany(Referral::class, 'property_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace App\Models\Property;
use App\Models\Model;
use App\Models\User;
class Referral extends Model
{
protected ?string $table = 'referrals';
protected array $fillable = [
'hashkey',
'property_id',
'referrer_id',
'referred_id',
'referred_name',
'referred_contact',
'status',
'details',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'details' => 'json',
'is_active' => 'boolean',
];
public function property()
{
return $this->belongsTo(Property::class, 'property_id');
}
public function referrer()
{
return $this->belongsTo(User::class, 'referrer_id');
}
public function referred()
{
return $this->belongsTo(User::class, 'referred_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Models\Property;
use App\Models\Model;
use App\Models\User;
class ReferralKey extends Model
{
protected ?string $table = 'referral_keys';
protected array $fillable = [
'hashkey',
'user_id',
'key',
'is_active',
'created_by',
'updated_by',
];
protected array $casts = [
'is_active' => 'boolean',
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace App\Models\Subscription;
use Hyperf\DbConnection\Model\Model;
use App\Models\User;
class Subscription extends Model
{
protected ?string $table = 'subscriptions';
public bool $incrementing = true;
protected array $fillable = [
'hashkey',
'user_id',
'plan_id',
'status',
'starts_at',
'expires_at',
'payment_method',
'additional_details',
'created_by',
'updated_by',
];
protected array $casts = [
'starts_at' => 'datetime',
'expires_at' => 'datetime',
'additional_details' => 'array',
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function plan()
{
return $this->belongsTo(SubscriptionPlan::class, 'plan_id');
}
public function invoices()
{
return $this->hasMany(SubscriptionInvoice::class, 'subscription_id');
}
public function isActive(): bool
{
return $this->status === 'active' && $this->expires_at && $this->expires_at->isFuture();
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\Models\Subscription;
use Hyperf\DbConnection\Model\Model;
use App\Models\User;
class SubscriptionInvoice extends Model
{
protected ?string $table = 'subscription_invoices';
public bool $incrementing = true;
protected array $fillable = [
'hashkey',
'subscription_id',
'user_id',
'amount',
'status',
'paid_at',
'payment_method',
'payment_reference',
'additional_details',
'created_by',
'updated_by',
];
protected array $casts = [
'amount' => 'float',
'paid_at' => 'datetime',
'additional_details' => 'array',
];
public function subscription()
{
return $this->belongsTo(Subscription::class, 'subscription_id');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\Models\Subscription;
use Hyperf\DbConnection\Model\Model;
use App\Models\User;
class SubscriptionPlan extends Model
{
protected ?string $table = 'subscription_plans';
public bool $incrementing = true;
protected array $fillable = [
'hashkey',
'name',
'description',
'price',
'duration_days',
'expiry_action',
'active',
'additional_details',
'created_by',
'updated_by',
];
protected array $casts = [
'active' => 'boolean',
'price' => 'float',
'duration_days' => 'integer',
'additional_details' => 'array',
];
public function subscriptions()
{
return $this->hasMany(Subscription::class, 'plan_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
}

View File

@@ -0,0 +1,161 @@
<?php
namespace App\Models;
use App\Models\Model;
use Hypervel\Support\Facades\Cache;
class SystemSetting extends Model
{
protected array $fillable = [
'key',
'value',
'type',
'options',
'group',
'label',
'description',
'hashkey',
'created_by',
'updated_by',
'is_active',
];
/**
* Cache key for settings.
*/
protected static string $cacheKey = 'app_system_settings';
/**
* Get a setting value by key.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public static function getValue(string $key, $default = null)
{
$settings = static::getAllFromCache();
/** @var self|null $setting */
$setting = $settings->where('key', $key)->first();
if ($setting === null) {
return $default;
}
return static::parseValue($setting->value, $setting->type);
}
/**
* Set a setting value.
*
* @param string $key
* @param mixed $value
* @return static
*/
public static function setValue(string $key, $value, $group = 'general', $type = null)
{
/** @var self|null $setting */
$setting = static::where('key', $key)->first();
// Auto-detect JSON if it's an array or a JSON string
if ($type === null) {
if (is_bool($value)) {
$type = 'boolean';
} elseif (is_array($value)) {
$type = 'json';
$value = json_encode($value);
} elseif (is_string($value) && (strpos($value, '[') === 0 || strpos($value, '{') === 0)) {
$decoded = json_decode($value);
if (json_last_error() === JSON_ERROR_NONE) {
$type = 'json';
}
}
} elseif ($type === 'json' && is_array($value)) {
$value = json_encode($value);
}
if ($setting) {
$data = ['value' => is_bool($value) ? ($value ? 'true' : 'false') : (string) $value];
if ($type !== null) $data['type'] = $type;
$setting->update($data);
} else {
$setting = static::create([
'key' => $key,
'value' => is_bool($value) ? ($value ? 'true' : 'false') : (string) $value,
'group' => $group,
'type' => $type ?? 'text'
]);
}
static::clearCache();
return $setting;
}
/**
* Get all settings in a group.
*
* @param string $group
* @return \Hypervel\Support\Collection
*/
public static function getGroup(string $group)
{
return static::getAllFromCache()->where('group', $group);
}
/**
* Clear the settings cache.
*/
public static function clearCache()
{
if (class_exists(\Hypervel\Support\Facades\Cache::class)) {
\Hypervel\Support\Facades\Cache::forget(static::$cacheKey);
}
}
/**
* Get all settings from cache or database.
*
* @return \Hypervel\Support\Collection|\Hyperf\Database\Model\Collection
*/
protected static function getAllFromCache()
{
if (class_exists(\Hypervel\Support\Facades\Cache::class)) {
return \Hypervel\Support\Facades\Cache::rememberForever(static::$cacheKey, function () {
return static::all();
});
}
return static::all();
}
/**
* Parse value based on type.
*
* @param mixed $value
* @param string|null $type
* @return mixed
*/
protected static function parseValue($value, ?string $type)
{
switch ($type) {
case 'boolean':
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
case 'number':
return is_numeric($value) ? (float) $value : $value;
case 'json':
return is_string($value) ? json_decode($value, true) : $value;
default:
return $value;
}
}
/**
* Accessor for parsed value.
*/
public function getParsedValueAttribute()
{
return static::parseValue($this->value, $this->type);
}
}

192
app/Models/User.php Normal file
View File

@@ -0,0 +1,192 @@
<?php
declare(strict_types=1);
namespace App\Models;
use App\Enums\UserActions;
use App\Enums\UserTypes;
use App\Models\PersonalAccessToken;
use App\Support\HasApiTokens;
use Hypervel\Foundation\Auth\User as Authenticatable;
use Hypervel\Support\Collection;
class User extends Authenticatable
{
use HasApiTokens;
public ?PersonalAccessToken $accessToken = null;
protected ?string $table = 'users';
public bool $incrementing = true;
protected array $attributes = [
'active' => true,
];
protected array $hidden = [
'password',
'remember_token',
// add anything else you dont want exposed
];
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [
'name',
'fullname',
'hashkey',
'mobile_number',
'landline',
'nickname',
'username',
'email',
'email_verified_at',
'mobile_verified_at',
'password',
'acct_type',
'total_balance',
'total_credit',
'created_by',
'updated_by',
'active',
'parentuid',
'targetuids',
'notes',
'exec_command',
'settings',
'multiple_logins',
'referralcode',
'photourl',
'logs',
'cart',
'details',
'additional_roles',
'denied_roles',
];
protected array $casts = [
'email_verified_at' => 'datetime',
'mobile_verified_at' => 'datetime',
'targetuids' => 'array',
'settings' => 'array',
'photourl' => 'array',
'logs' => 'array',
'cart' => 'array',
'multiple_logins' => 'boolean',
'active' => 'boolean',
'details' => 'array',
'acct_type' => UserTypes::class ,
'additional_roles' => \App\Casts\UserActionsArrayCast::class ,
'denied_roles' => \App\Casts\UserActionsArrayCast::class ,
];
public function userInfo()
{
return $this->hasOne(\App\Models\Market\UserInfo::class, 'user_id');
}
public function cooperativeMemberships()
{
return $this->hasMany(\App\Models\Market\CooperativeMember::class, 'user_id');
}
public function creator()
{
return $this->belongsTo(User::class , 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class , 'updated_by');
}
public function parent()
{
return $this->belongsTo(User::class , 'parentuid');
}
public function getAllDescendants(): Collection
{
$descendants = collect();
$children = self::where('parentuid', $this->id)->get();
foreach ($children as $child) {
$descendants->push($child);
// Recursively get each child's descendants
$descendants = $descendants->merge($child->getAllDescendants());
}
return $descendants;
}
public function children()
{
return $this->hasMany(User::class , 'parentuid');
}
/**
* Check if the user has a specific role.
*/
public function hasRole(UserTypes|string|array $role): bool
{
if (is_array($role)) {
foreach ($role as $r) {
if ($this->hasRole($r)) {
return true;
}
}
return false;
}
$roleValue = $role instanceof UserTypes ? $role->value : $role;
return $this->acct_type->value === $roleValue;
}
/**
* Convenience methods for common roles.
*/
public function isUltimate(): bool
{
return $this->acct_type === UserTypes::ULTIMATE;
}
public function isSuperOperator(): bool
{
return $this->acct_type === UserTypes::SUPER_OPERATOR;
}
public function isOperator(): bool
{
return $this->acct_type === UserTypes::OPERATOR;
}
/**
* Get cooperatives from settings.
*/
public function getCooperativesAttribute(): array
{
return $this->settings['cooperatives'] ?? [];
}
/**
* Check if the user has joined a specific cooperative via settings.
*/
public function hasJoinedCooperative(string $cooperativeHash): bool
{
return in_array($cooperativeHash, $this->cooperatives);
}
/**
* Check if the user has a specific permission (action).
*/
public function canDo(\App\Enums\UserActions $action, string|int|UserTypes $target = null): bool
{
return \App\Http\Controllers\Helpers\Permissions\UserPermissions::isActionPermitted($target ?? $this->acct_type, $action);
}
}