feat: implement barangay system phases 2-14
Complete adaptation from BukidBountyApp to Philippine barangay governance: - Barangay models: Resident, Household, HouseholdMember, Blotter, BlotterHearing, DocumentRequest, RequestPayment, RequestType, BarangayProject, BarangayBudget - Controllers: ResidentController, HouseholdController, BlotterController, BlotterHearingController, DocumentRequestController, RequestTypeController, ProjectController, BudgetController, QRPHController, AdminConsoleController, UserController, FileController, ChapterController, LoginController - Vue pages: Home, ManageResidents, ResidentProfile, ManageHouseholds, ManageBlotters, BlotterDetail, RequestDocument, ManageDocumentRequests, DocumentRequestDetail, ManageRequestTypes, ManageProjects, BudgetLedger, AdminConsole - Barangay roles: PunongBarangay, Kagawad, Secretary, Treasurer, SK, Tanod, BHW, Staff, Resident - UserPermissions matrix rewritten with barangay-specific permission mappings - VueRouteMap replaced with barangay SPA routes - UserActions enum references corrected across all controllers - Removed all market/cooperative/POS/subscription code and models
This commit is contained in:
42
app/Models/Barangay/BarangayBudget.php
Normal file
42
app/Models/Barangay/BarangayBudget.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
|
||||
class BarangayBudget extends Model
|
||||
{
|
||||
protected ?string $table = 'barangay_budget';
|
||||
|
||||
protected array $fillable = [
|
||||
'hashkey', 'fiscal_year', 'category', 'source',
|
||||
'amount', 'description', 'date', 'reference', 'encoded_by',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'amount' => 'decimal:2',
|
||||
'date' => 'date',
|
||||
];
|
||||
|
||||
public function encodedBy()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'encoded_by');
|
||||
}
|
||||
|
||||
public function scopeIncome($query)
|
||||
{
|
||||
return $query->where('category', 'INCOME');
|
||||
}
|
||||
|
||||
public function scopeExpense($query)
|
||||
{
|
||||
return $query->where('category', 'EXPENSE');
|
||||
}
|
||||
|
||||
public function scopeByYear($query, int $year)
|
||||
{
|
||||
return $query->where('fiscal_year', $year);
|
||||
}
|
||||
}
|
||||
44
app/Models/Barangay/BarangayProject.php
Normal file
44
app/Models/Barangay/BarangayProject.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
use Hypervel\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class BarangayProject extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected ?string $table = 'barangay_projects';
|
||||
|
||||
protected array $fillable = [
|
||||
'hashkey', 'project_name', 'description', 'type', 'budget',
|
||||
'fund_source', 'start_date', 'end_date', 'status',
|
||||
'implementing_office', 'contractor', 'location',
|
||||
'beneficiaries_count', 'created_by', 'updated_by',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'budget' => 'decimal:2',
|
||||
'start_date' => 'date',
|
||||
'end_date' => 'date',
|
||||
'beneficiaries_count' => 'integer',
|
||||
];
|
||||
|
||||
public function createdBy()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->whereNotIn('status', ['CANCELLED', 'COMPLETED']);
|
||||
}
|
||||
|
||||
public function scopeByType($query, string $type)
|
||||
{
|
||||
return $query->where('type', $type);
|
||||
}
|
||||
}
|
||||
70
app/Models/Barangay/Blotter.php
Normal file
70
app/Models/Barangay/Blotter.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Enums\Barangay\BlotterStatus;
|
||||
use Hypervel\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Blotter extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected ?string $table = 'barangay_blotters';
|
||||
|
||||
protected array $fillable = [
|
||||
'hashkey', 'blotter_no',
|
||||
'complainant_user_id', 'complainant_name', 'complainant_contact', 'complainant_address',
|
||||
'respondent_user_id', 'respondent_name', 'respondent_contact', 'respondent_address',
|
||||
'incident_type', 'incident_date', 'incident_location', 'narrative',
|
||||
'status', 'complaint_date', 'filed_by', 'assigned_officer_id',
|
||||
'resolution', 'settlement_type', 'endorsed_to', 'is_active',
|
||||
'created_by', 'updated_by',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'incident_date' => 'date',
|
||||
'complaint_date' => 'date',
|
||||
'is_active' => 'boolean',
|
||||
'status' => BlotterStatus::class,
|
||||
];
|
||||
|
||||
public function complainant()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'complainant_user_id');
|
||||
}
|
||||
|
||||
public function respondent()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'respondent_user_id');
|
||||
}
|
||||
|
||||
public function assignedOfficer()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'assigned_officer_id');
|
||||
}
|
||||
|
||||
public function hearings()
|
||||
{
|
||||
return $this->hasMany(BlotterHearing::class, 'blotter_id');
|
||||
}
|
||||
|
||||
public function nextHearing()
|
||||
{
|
||||
return $this->hearings()->where('status', 'SCHEDULED')->orderBy('hearing_date')->first();
|
||||
}
|
||||
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true);
|
||||
}
|
||||
|
||||
public static function generateBlotterNo(): string
|
||||
{
|
||||
$year = date('Y');
|
||||
$count = static::whereYear('created_at', $year)->count() + 1;
|
||||
return sprintf('BLT-%s-%04d', $year, $count);
|
||||
}
|
||||
}
|
||||
32
app/Models/Barangay/BlotterHearing.php
Normal file
32
app/Models/Barangay/BlotterHearing.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
|
||||
class BlotterHearing extends Model
|
||||
{
|
||||
protected ?string $table = 'barangay_blotter_hearings';
|
||||
|
||||
protected array $fillable = [
|
||||
'blotter_id', 'hearing_date', 'status', 'officer_id',
|
||||
'notes', 'resolution', 'next_hearing_date',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'hearing_date' => 'datetime',
|
||||
'next_hearing_date' => 'datetime',
|
||||
];
|
||||
|
||||
public function blotter()
|
||||
{
|
||||
return $this->belongsTo(Blotter::class, 'blotter_id');
|
||||
}
|
||||
|
||||
public function officer()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'officer_id');
|
||||
}
|
||||
}
|
||||
78
app/Models/Barangay/DocumentRequest.php
Normal file
78
app/Models/Barangay/DocumentRequest.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Enums\Barangay\DocumentStatus;
|
||||
use App\Enums\Barangay\PaymentStatus;
|
||||
use Hypervel\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class DocumentRequest extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected ?string $table = 'barangay_document_requests';
|
||||
|
||||
protected array $fillable = [
|
||||
'hashkey', 'request_no', 'resident_user_id', 'request_type_id',
|
||||
'purpose', 'fee_amount', 'payment_status', 'payment_method',
|
||||
'payment_ref', 'qrph_code', 'status',
|
||||
'requested_by', 'processed_by', 'claimed_at', 'notes',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'fee_amount' => 'decimal:2',
|
||||
'status' => DocumentStatus::class,
|
||||
'payment_status' => PaymentStatus::class,
|
||||
'claimed_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function requestType()
|
||||
{
|
||||
return $this->belongsTo(RequestType::class, 'request_type_id');
|
||||
}
|
||||
|
||||
public function resident()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'resident_user_id');
|
||||
}
|
||||
|
||||
public function processedBy()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'processed_by');
|
||||
}
|
||||
|
||||
public function requestedBy()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'requested_by');
|
||||
}
|
||||
|
||||
public function payments()
|
||||
{
|
||||
return $this->hasMany(RequestPayment::class, 'request_id');
|
||||
}
|
||||
|
||||
public function latestPayment()
|
||||
{
|
||||
return $this->payments()->latest()->first();
|
||||
}
|
||||
|
||||
public static function generateRequestNo(): string
|
||||
{
|
||||
$year = date('Y');
|
||||
$count = static::whereYear('created_at', $year)->count() + 1;
|
||||
return sprintf('REQ-%s-%05d', $year, $count);
|
||||
}
|
||||
|
||||
public function scopePending($query)
|
||||
{
|
||||
return $query->whereIn('status', [DocumentStatus::DRAFT, DocumentStatus::PENDING_PAYMENT]);
|
||||
}
|
||||
|
||||
public function scopeForProcessing($query)
|
||||
{
|
||||
return $query->where('status', DocumentStatus::PAID);
|
||||
}
|
||||
}
|
||||
51
app/Models/Barangay/Household.php
Normal file
51
app/Models/Barangay/Household.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
use Hypervel\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Household extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected ?string $table = 'barangay_households';
|
||||
|
||||
protected array $fillable = [
|
||||
'hashkey', 'household_no', 'head_resident_id',
|
||||
'address', 'purok', 'barangay', 'city', 'province',
|
||||
'member_count', 'ownership_type', 'monthly_rental',
|
||||
'has_electricity', 'has_water', 'housing_material',
|
||||
'is_active', 'created_by', 'updated_by',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'monthly_rental' => 'decimal:2',
|
||||
'has_electricity' => 'boolean',
|
||||
'has_water' => 'boolean',
|
||||
'is_active' => 'boolean',
|
||||
'member_count' => 'integer',
|
||||
];
|
||||
|
||||
public function head()
|
||||
{
|
||||
return $this->belongsTo(Resident::class, 'head_resident_id');
|
||||
}
|
||||
|
||||
public function members()
|
||||
{
|
||||
return $this->hasMany(HouseholdMember::class, 'household_id');
|
||||
}
|
||||
|
||||
public function activeMembers()
|
||||
{
|
||||
return $this->members()->where('is_active', true);
|
||||
}
|
||||
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true);
|
||||
}
|
||||
}
|
||||
30
app/Models/Barangay/HouseholdMember.php
Normal file
30
app/Models/Barangay/HouseholdMember.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
|
||||
class HouseholdMember extends Model
|
||||
{
|
||||
protected ?string $table = 'barangay_household_members';
|
||||
|
||||
protected array $fillable = [
|
||||
'household_id', 'resident_id', 'relationship_to_head', 'is_active',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
public function household()
|
||||
{
|
||||
return $this->belongsTo(Household::class, 'household_id');
|
||||
}
|
||||
|
||||
public function resident()
|
||||
{
|
||||
return $this->belongsTo(Resident::class, 'resident_id');
|
||||
}
|
||||
}
|
||||
32
app/Models/Barangay/RequestPayment.php
Normal file
32
app/Models/Barangay/RequestPayment.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
|
||||
class RequestPayment extends Model
|
||||
{
|
||||
protected ?string $table = 'barangay_request_payments';
|
||||
|
||||
protected array $fillable = [
|
||||
'request_id', 'amount', 'method', 'reference',
|
||||
'qrph_raw', 'paid_at', 'verified_by',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'amount' => 'decimal:2',
|
||||
'paid_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function documentRequest()
|
||||
{
|
||||
return $this->belongsTo(DocumentRequest::class, 'request_id');
|
||||
}
|
||||
|
||||
public function verifiedBy()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'verified_by');
|
||||
}
|
||||
}
|
||||
34
app/Models/Barangay/RequestType.php
Normal file
34
app/Models/Barangay/RequestType.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
|
||||
class RequestType extends Model
|
||||
{
|
||||
protected ?string $table = 'barangay_request_types';
|
||||
|
||||
protected array $fillable = [
|
||||
'name', 'code', 'description', 'base_fee',
|
||||
'processing_days', 'is_active', 'requires_clearance',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'base_fee' => 'decimal:2',
|
||||
'processing_days' => 'integer',
|
||||
'is_active' => 'boolean',
|
||||
'requires_clearance' => 'boolean',
|
||||
];
|
||||
|
||||
public function documentRequests()
|
||||
{
|
||||
return $this->hasMany(DocumentRequest::class, 'request_type_id');
|
||||
}
|
||||
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true);
|
||||
}
|
||||
}
|
||||
67
app/Models/Barangay/Resident.php
Normal file
67
app/Models/Barangay/Resident.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Barangay;
|
||||
|
||||
use App\Models\Model;
|
||||
use Hypervel\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Resident extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected ?string $table = 'barangay_residents';
|
||||
|
||||
protected array $fillable = [
|
||||
'hashkey', 'user_id', 'firstname', 'middlename', 'lastname', 'suffix',
|
||||
'dob', 'birthplace', 'gender', 'civil_status', 'citizenship', 'religion',
|
||||
'occupation', 'monthly_income', 'blood_type',
|
||||
'voter_status', 'registered_voter_id', 'voter_precinct', 'head_of_household',
|
||||
'purok', 'street', 'barangay', 'city', 'province', 'region',
|
||||
'philhealth_id', 'sss_id', 'gsis_id', 'tin',
|
||||
'emergency_contact_name', 'emergency_contact_phone', 'emergency_contact_address',
|
||||
'is_active', 'created_by', 'updated_by',
|
||||
];
|
||||
|
||||
protected array $casts = [
|
||||
'dob' => 'date',
|
||||
'monthly_income' => 'decimal:2',
|
||||
'voter_status' => 'boolean',
|
||||
'head_of_household' => 'boolean',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'user_id');
|
||||
}
|
||||
|
||||
public function household()
|
||||
{
|
||||
return $this->hasOne(Household::class, 'head_resident_id');
|
||||
}
|
||||
|
||||
public function householdMemberships()
|
||||
{
|
||||
return $this->hasMany(HouseholdMember::class, 'resident_id');
|
||||
}
|
||||
|
||||
public function documentRequests()
|
||||
{
|
||||
return $this->hasMany(DocumentRequest::class, 'resident_user_id', 'user_id');
|
||||
}
|
||||
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true);
|
||||
}
|
||||
|
||||
public function getFullnameAttribute(): string
|
||||
{
|
||||
$parts = array_filter([$this->firstname, $this->middlename, $this->lastname]);
|
||||
$name = implode(' ', $parts);
|
||||
if ($this->suffix) $name .= ', ' . $this->suffix;
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user