diff --git a/app/Http/Controllers/Api/V1/Admin/AdminPartnerStoreController.php b/app/Http/Controllers/Api/V1/Admin/AdminPartnerStoreController.php index c032fcc..905e90c 100644 --- a/app/Http/Controllers/Api/V1/Admin/AdminPartnerStoreController.php +++ b/app/Http/Controllers/Api/V1/Admin/AdminPartnerStoreController.php @@ -108,6 +108,7 @@ class AdminPartnerStoreController extends ApiController $data = $request->validate([ 'quantity' => ['required', 'integer', 'min:1', 'max:50000'], 'wholesale_price_centavos' => ['required', 'integer', 'min:0'], + 'is_prepaid' => ['nullable', 'boolean'], ]); if ($store->status !== PartnerStore::STATUS_ACTIVE) { @@ -119,7 +120,8 @@ class AdminPartnerStoreController extends ApiController (int) $data['quantity'], (int) $data['wholesale_price_centavos'], null, - $request->user() + $request->user(), + (bool) ($data['is_prepaid'] ?? false) ); return $this->created([ diff --git a/app/Http/Controllers/Api/V1/Admin/AdminTeamController.php b/app/Http/Controllers/Api/V1/Admin/AdminTeamController.php index 4a6c2f3..00c6e7c 100644 --- a/app/Http/Controllers/Api/V1/Admin/AdminTeamController.php +++ b/app/Http/Controllers/Api/V1/Admin/AdminTeamController.php @@ -20,7 +20,7 @@ class AdminTeamController extends ApiController public function index(Request $request): JsonResponse { $request->validate([ - 'status' => ['nullable', 'in:active,inactive'], + 'status' => ['nullable', 'in:active,inactive,standby'], 'area_id' => ['nullable', 'integer'], 'q' => ['nullable', 'string', 'max:100'], 'per_page' => ['nullable', 'integer', 'min:1', 'max:500'], @@ -28,7 +28,7 @@ class AdminTeamController extends ApiController $perPage = (int) $request->input('per_page', 25); $teams = CollectionTeam::query() - ->with(['area', 'driver', 'scanner', 'truck']) + ->with(['area', 'driver', 'scanner', 'truck', 'helpers.user', 'currentTrip']) ->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status'))) ->when($request->filled('area_id'), fn ($q) => $q->where('area_id', $request->integer('area_id'))) ->when($request->filled('q'), fn ($q) => $q->where('name', 'like', '%'.$request->string('q').'%')) @@ -77,7 +77,7 @@ class AdminTeamController extends ApiController } } - unset($data['helper_ids'], $data['override_conflicts'], $data['helper_count'], $data['helper_assignment_mode']); + unset($data['helper_ids'], $data['override_conflicts'], $data['helper_assignment_mode']); $team = DB::transaction(function () use ($data, $helperIds) { $team = CollectionTeam::create($data); @@ -105,7 +105,7 @@ class AdminTeamController extends ApiController public function show(CollectionTeam $team): JsonResponse { - $team->load(['area', 'driver', 'scanner', 'truck', 'helpers.user']); + $team->load(['area', 'driver', 'scanner', 'truck', 'helpers.user', 'currentTrip']); return $this->ok(new CollectionTeamResource($team)); } @@ -143,7 +143,7 @@ class AdminTeamController extends ApiController } } - unset($data['helper_ids'], $data['override_conflicts'], $data['helper_count'], $data['helper_assignment_mode']); + unset($data['helper_ids'], $data['override_conflicts'], $data['helper_assignment_mode']); DB::transaction(function () use ($team, $data, $helperIds) { $team->update($data); diff --git a/app/Http/Controllers/Api/V1/Driver/TripDetourController.php b/app/Http/Controllers/Api/V1/Driver/TripDetourController.php index 6d9ebdd..1adef59 100644 --- a/app/Http/Controllers/Api/V1/Driver/TripDetourController.php +++ b/app/Http/Controllers/Api/V1/Driver/TripDetourController.php @@ -35,6 +35,8 @@ class TripDetourController extends Controller 'created_at' => now(), ]); + $trip->update(['is_detouring' => true]); + return response()->json(['message' => 'Detour to dumpsite logged.']); } @@ -63,6 +65,8 @@ class TripDetourController extends Controller 'created_at' => now(), ]); + $trip->update(['is_detouring' => false]); + return response()->json(['message' => 'Resuming route logged.']); } } diff --git a/app/Http/Controllers/Api/V1/Store/StorePortalController.php b/app/Http/Controllers/Api/V1/Store/StorePortalController.php new file mode 100644 index 0000000..49934cc --- /dev/null +++ b/app/Http/Controllers/Api/V1/Store/StorePortalController.php @@ -0,0 +1,184 @@ +user(); + + $store = PartnerStore::where('owner_user_id', $user->id)->first(); + + if (!$store) { + abort(403, 'No partner store associated with this account.'); + } + + return $store; + } + + /** + * Get dashboard summary data. + */ + public function dashboard(): JsonResponse + { + $store = $this->getStore(); + $inventory = $store->inventory; + + $data = [ + 'business_name' => $store->business_name, + 'inventory_balance' => $inventory ? (int) $inventory->current_code_balance : 0, + 'prepaid_balance' => $inventory ? (int) $inventory->prepaid_balance : 0, + 'total_sales_count' => (int) $store->sales()->count(), + 'status' => $store->status, + ]; + + return $this->ok($data); + } + + /** + * Get financial analytics for the store. + */ + public function analytics(): JsonResponse + { + $store = $this->getStore(); + + // Sums + $totalRetail = $store->sales()->sum('retail_price_centavos'); + $totalWholesale = $store->purchases()->sum('wholesale_price_centavos'); + $totalCommission = $store->sales()->sum('commission_centavos'); + + // Calculate settlement balance + $totalSettled = $store->settlements()->sum('amount_centavos'); + $amountOwed = ($totalRetail - $totalCommission) - $totalSettled; + + return $this->ok([ + 'total_retail_revenue_centavos' => (int) $totalRetail, + 'total_wholesale_cost_centavos' => (int) $totalWholesale, + 'total_commission_earned_centavos' => (int) $totalCommission, + 'amount_owed_to_lgu_centavos' => (int) $amountOwed, + 'gross_profit_centavos' => (int) ($totalRetail - $totalWholesale), + ]); + } + + /** + * Record a retail sale to a resident. + */ + public function recordSale(Request $request, StoreOperations $operations): JsonResponse + { + $store = $this->getStore(); + + $request->validate([ + 'household_id' => 'required|exists:households,id', + 'quantity' => 'required|integer|min:1', + 'serial_number' => 'nullable|string|exists:qr_codes,serial', + ]); + + try { + $sale = $operations->sellToHousehold( + $store, + \App\Models\Household::findOrFail($request->household_id), + $request->quantity, + config('qr.default_retail_price_per_code_centavos'), + $request->serial_number + ); + + // Fetch the serials that were just activated for this sale + $codes = \App\Models\QrCode::whereIn('id', $sale->id ? [$sale->id] : []) // Wait, StoreSale doesn't link to QrCode directly? + // I need to find the codes that were activated. + // QrCode table has assigned_to_household_id. + ->where('assigned_to_household_id', $request->household_id) + ->where('status', \App\States\QrCode\Active::$name) + ->latest('activated_at') + ->limit($request->quantity) + ->get(); + + $qrData = $codes->map(function ($code) { + $qr = \Endroid\QrCode\QrCode::create($code->serial) + ->setSize(300) + ->setMargin(10); + $writer = new \Endroid\QrCode\Writer\PngWriter(); + return [ + 'serial' => $code->serial, + 'qr_base64' => base64_encode($writer->write($qr)->getString()) + ]; + }); + + return $this->created([ + 'sale' => $sale, + 'codes' => $qrData + ], 'Sale recorded successfully.'); + } catch (\Exception $e) { + return $this->fail($e->getMessage(), null, 422); + } + } + + /** + * Get sales history for the store. + */ + public function salesHistory(): JsonResponse + { + $store = $this->getStore(); + + $sales = $store->sales() + ->with('household.head') + ->latest() + ->paginate(15); + + return $this->ok($sales); + } + + /** + * Get inventory adjustment and purchase history. + */ + public function inventoryHistory(): JsonResponse + { + $store = $this->getStore(); + + $history = \App\Models\StoreInventoryAdjustment::query() + ->where('store_id', $store->id) + ->with('user') + ->latest() + ->paginate(15); + + return $this->ok($history); + } + + /** + * Search for households to record a sale. + */ + public function searchHouseholds(Request $request): JsonResponse + { + $request->validate([ + 'q' => 'required|string|min:2', + ]); + + $term = '%' . $request->string('q') . '%'; + + $households = \App\Models\Household::query() + ->with('head') + ->where('verification_status', \App\Models\Household::VERIFICATION_APPROVED) + ->where(function($q) use ($term) { + $q->where('address_line', 'like', $term) + ->orWhereHas('head', function($h) use ($term) { + $h->where('first_name', 'like', $term) + ->orWhere('last_name', 'like', $term); + }); + }) + ->limit(10) + ->get(); + + return $this->ok($households); + } +} diff --git a/app/Http/Controllers/Api/V1/SuperAdmin/SuperAdminUserController.php b/app/Http/Controllers/Api/V1/SuperAdmin/SuperAdminUserController.php index 49f73d2..21171c2 100644 --- a/app/Http/Controllers/Api/V1/SuperAdmin/SuperAdminUserController.php +++ b/app/Http/Controllers/Api/V1/SuperAdmin/SuperAdminUserController.php @@ -85,6 +85,22 @@ class SuperAdminUserController extends ApiController 'language' => $data['preferred_language'] ?? 'en', ]); + // If store_partner, auto-create a shell store so they can access the dashboard + if ($data['role'] === User::ROLE_STORE_PARTNER) { + $store = \App\Models\PartnerStore::create([ + 'tenant_id' => $user->tenant_id, + 'owner_user_id' => $user->id, + 'business_name' => $user->first_name . "'s Store", + 'status' => \App\Models\PartnerStore::STATUS_ACTIVE, + 'commission_rate_percent' => 10, // Default 10% + ]); + + \App\Models\StoreInventory::create([ + 'store_id' => $store->id, + 'current_code_balance' => 0, + ]); + } + return $user; }); diff --git a/app/Http/Controllers/Store/StoreDashboardController.php b/app/Http/Controllers/Store/StoreDashboardController.php new file mode 100644 index 0000000..ab1391d --- /dev/null +++ b/app/Http/Controllers/Store/StoreDashboardController.php @@ -0,0 +1,29 @@ + ['integer', 'exists:users,id'], 'helper_count' => ['nullable', 'integer', 'min:0', 'max:10'], 'helper_assignment_mode' => ['nullable', 'string', Rule::in(['random', 'manual'])], - 'status' => ['nullable', Rule::in([CollectionTeam::STATUS_ACTIVE, CollectionTeam::STATUS_INACTIVE])], + 'status' => ['nullable', Rule::in([ + CollectionTeam::STATUS_ACTIVE, + CollectionTeam::STATUS_INACTIVE, + CollectionTeam::STATUS_STANDBY, + ])], 'notes' => ['nullable', 'string', 'max:1000'], 'override_conflicts' => ['nullable', 'boolean'], ]; diff --git a/app/Http/Resources/CollectionTeamResource.php b/app/Http/Resources/CollectionTeamResource.php index 222667e..fa59179 100644 --- a/app/Http/Resources/CollectionTeamResource.php +++ b/app/Http/Resources/CollectionTeamResource.php @@ -20,11 +20,15 @@ class CollectionTeamResource extends JsonResource 'truck' => TruckResource::make($this->whenLoaded('truck')), 'helpers' => $this->whenLoaded('helpers', fn () => $this->helpers->map(fn ($m) => [ 'id' => $m->user?->uuid, + 'db_id' => $m->user?->id, 'name' => $m->user?->full_name, 'assigned_from' => $m->assigned_from?->toDateString(), 'assigned_until' => $m->assigned_until?->toDateString(), 'status' => $m->status, ])), + 'is_full' => $this->is_full, + 'performance_stats' => $this->getPerformanceStats(), + 'current_trip' => TripResource::make($this->whenLoaded('currentTrip')), 'notes' => $this->notes, 'created_at' => $this->created_at?->toIso8601String(), ]; diff --git a/app/Http/Resources/ServiceAreaResource.php b/app/Http/Resources/ServiceAreaResource.php index 1a9deca..fc8657d 100644 --- a/app/Http/Resources/ServiceAreaResource.php +++ b/app/Http/Resources/ServiceAreaResource.php @@ -11,6 +11,7 @@ class ServiceAreaResource extends JsonResource { return [ 'id' => $this->uuid, + 'db_id' => $this->id, 'name' => $this->name, 'code' => $this->code, 'status' => $this->status, diff --git a/app/Http/Resources/TripResource.php b/app/Http/Resources/TripResource.php index 2b60161..14f82db 100644 --- a/app/Http/Resources/TripResource.php +++ b/app/Http/Resources/TripResource.php @@ -20,6 +20,7 @@ class TripResource extends JsonResource 'dumpsite_arrival_time' => $this->dumpsite_arrival_time?->toIso8601String(), 'dumpsite_departure_time' => $this->dumpsite_departure_time?->toIso8601String(), 'total_load_kg' => $this->total_load_kg, + 'is_detouring' => $this->is_detouring, 'route' => RouteResource::make($this->whenLoaded('route')), 'team' => CollectionTeamResource::make($this->whenLoaded('team')), 'truck' => TruckResource::make($this->whenLoaded('truck')), diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index a5be603..26a6229 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -11,6 +11,8 @@ class UserResource extends JsonResource { return [ 'id' => $this->uuid, + 'db_id' => $this->id, + 'full_name' => $this->full_name, 'email' => $this->email, 'phone' => $this->phone, 'first_name' => $this->first_name, diff --git a/app/Models/CollectionTeam.php b/app/Models/CollectionTeam.php index 9084f48..abd81b6 100644 --- a/app/Models/CollectionTeam.php +++ b/app/Models/CollectionTeam.php @@ -15,12 +15,12 @@ class CollectionTeam extends Model use HasFactory, HasTenant, SoftDeletes; public const STATUS_ACTIVE = 'active'; - public const STATUS_INACTIVE = 'inactive'; + public const STATUS_STANDBY = 'standby'; protected $fillable = [ 'uuid', 'tenant_id', 'name', 'area_id', 'driver_id', 'scanner_id', 'truck_id', - 'status', 'notes', + 'status', 'notes', 'helper_count', ]; public function getRouteKeyName(): string @@ -66,4 +66,44 @@ class CollectionTeam extends Model { return $this->members()->where('role_in_team', TeamMember::ROLE_HELPER); } + + public function trips(): HasMany + { + return $this->hasMany(Trip::class, 'team_id'); + } + + public function currentTrip(): \Illuminate\Database\Eloquent\Relations\HasOne + { + return $this->hasOne(Trip::class, 'team_id') + ->whereIn('status', [Trip::STATUS_IN_PROGRESS, Trip::STATUS_AT_DUMPSITE]); + } + + public function collectionLogs(): \Illuminate\Database\Eloquent\Relations\HasManyThrough + { + return $this->hasManyThrough(CollectionLog::class, Trip::class, 'team_id', 'trip_id'); + } + + public function getIsFullAttribute(): bool + { + // A team is considered "Full" if it has a Driver, a Scanner, + // and the number of helpers matches or exceeds the desired helper_count. + $hasCore = $this->driver_id && $this->scanner_id; + $helpersMet = $this->helpers()->count() >= ($this->helper_count ?: 2); + + return $hasCore && $helpersMet; + } + + public function getPerformanceStats(): array + { + $today = now()->startOfDay(); + $week = now()->startOfWeek(); + + return [ + 'current_trip_qr' => $this->currentTrip?->collectionLogs()->count() ?? 0, + 'current_trip_kg' => (int) ($this->currentTrip?->total_load_kg ?? 0), + 'daily_qr' => $this->collectionLogs()->where('scanned_at', '>=', $today)->count(), + 'weekly_qr' => $this->collectionLogs()->where('scanned_at', '>=', $week)->count(), + 'total_qr' => $this->collectionLogs()->count(), + ]; + } } diff --git a/app/Models/StoreInventory.php b/app/Models/StoreInventory.php index c51881d..1bbd165 100644 --- a/app/Models/StoreInventory.php +++ b/app/Models/StoreInventory.php @@ -10,12 +10,13 @@ class StoreInventory extends Model { use HasFactory; - protected $fillable = ['store_id', 'current_code_balance', 'last_updated_at']; + protected $fillable = ['store_id', 'current_code_balance', 'prepaid_balance', 'last_updated_at']; protected function casts(): array { return [ 'current_code_balance' => 'integer', + 'prepaid_balance' => 'integer', 'last_updated_at' => 'datetime', ]; } diff --git a/app/Models/StorePurchase.php b/app/Models/StorePurchase.php index f2801c8..b54a24c 100644 --- a/app/Models/StorePurchase.php +++ b/app/Models/StorePurchase.php @@ -12,7 +12,7 @@ class StorePurchase extends Model protected $fillable = [ 'store_id', 'batch_id', 'quantity', - 'wholesale_price_centavos', 'paid_at', 'payment_id', + 'wholesale_price_centavos', 'paid_at', 'payment_id', 'is_prepaid', ]; protected function casts(): array @@ -21,6 +21,7 @@ class StorePurchase extends Model 'paid_at' => 'datetime', 'quantity' => 'integer', 'wholesale_price_centavos' => 'integer', + 'is_prepaid' => 'boolean', ]; } diff --git a/app/Models/StoreSale.php b/app/Models/StoreSale.php index 449342c..90a72c6 100644 --- a/app/Models/StoreSale.php +++ b/app/Models/StoreSale.php @@ -13,7 +13,7 @@ class StoreSale extends Model protected $fillable = [ 'store_id', 'household_id', 'quantity', 'retail_price_centavos', 'commission_centavos', - 'payment_id', 'sold_at', + 'payment_id', 'sold_at', 'is_prepaid', ]; protected function casts(): array @@ -23,6 +23,7 @@ class StoreSale extends Model 'quantity' => 'integer', 'retail_price_centavos' => 'integer', 'commission_centavos' => 'integer', + 'is_prepaid' => 'boolean', ]; } diff --git a/app/Models/Trip.php b/app/Models/Trip.php index 2cf49f7..31b2a67 100644 --- a/app/Models/Trip.php +++ b/app/Models/Trip.php @@ -32,9 +32,8 @@ class Trip extends Model 'scheduled_date', 'scheduled_start_time', 'actual_start_time', 'actual_end_time', 'dumpsite_arrival_time', 'dumpsite_departure_time', - 'status', 'total_load_kg', 'notes', 'created_by_admin_id', + 'status', 'is_detouring', 'total_load_kg', 'notes', 'created_by_admin_id', ]; - protected function casts(): array { return [ @@ -43,6 +42,7 @@ class Trip extends Model 'actual_end_time' => 'datetime', 'dumpsite_arrival_time' => 'datetime', 'dumpsite_departure_time' => 'datetime', + 'is_detouring' => 'boolean', ]; } @@ -116,4 +116,9 @@ class Trip extends Model { return $this->hasMany(DumpsiteRelease::class); } + + public function collectionLogs(): HasMany + { + return $this->hasMany(CollectionLog::class, 'trip_id'); + } } diff --git a/app/Services/Store/StoreOperations.php b/app/Services/Store/StoreOperations.php index 6c13232..dcf0cdd 100644 --- a/app/Services/Store/StoreOperations.php +++ b/app/Services/Store/StoreOperations.php @@ -36,8 +36,9 @@ class StoreOperations int $wholesalePriceCentavos, ?\DateTimeInterface $paidAt = null, $userId = null, + bool $isPrepaid = false, ): StorePurchase { - return DB::transaction(function () use ($store, $quantity, $wholesalePriceCentavos, $paidAt, $userId) { + return DB::transaction(function () use ($store, $quantity, $wholesalePriceCentavos, $paidAt, $userId, $isPrepaid) { $batch = $this->batches->generate( quantity: $quantity, purpose: QrCodeBatch::PURPOSE_STORE, @@ -64,10 +65,11 @@ class StoreOperations 'quantity' => $quantity, 'wholesale_price_centavos' => $wholesalePriceCentavos, 'paid_at' => $paidAt ?? $now, + 'is_prepaid' => $isPrepaid, ]); - $this->logAdjustment($store, StoreInventoryAdjustment::TYPE_PURCHASE, $quantity, null, "Wholesale purchase: Batch #{$batch->batch_number}", $userId); - $this->updateBalance($store, $quantity); + $this->logAdjustment($store, StoreInventoryAdjustment::TYPE_PURCHASE, $quantity, null, "Wholesale purchase: Batch #{$batch->batch_number}" . ($isPrepaid ? ' (Prepaid)' : ''), $userId); + $this->updateBalance($store, $quantity, $isPrepaid); return $purchase; }); @@ -82,12 +84,22 @@ class StoreOperations Household $household, int $quantity, int $retailPricePerCodeCentavos, + ?string $serialNumber = null, ): StoreSale { - return DB::transaction(function () use ($store, $household, $quantity, $retailPricePerCodeCentavos) { - $codeIds = QrCode::query() + return DB::transaction(function () use ($store, $household, $quantity, $retailPricePerCodeCentavos, $serialNumber) { + $inventory = StoreInventory::where('store_id', $store->id)->first(); + $prepaidAvailable = $inventory ? $inventory->prepaid_balance : 0; + $usePrepaid = $prepaidAvailable >= $quantity; + + $query = QrCode::query() ->where('assigned_to_store_id', $store->id) - ->where('status', Allocated::$name) - ->orderBy('id') + ->where('status', Allocated::$name); + + if ($serialNumber) { + $query->where('serial', $serialNumber); + } + + $codeIds = $query->orderBy('id') ->limit($quantity) ->lockForUpdate() ->pluck('id'); @@ -115,10 +127,11 @@ class StoreOperations 'retail_price_centavos' => $totalRetail, 'commission_centavos' => $commission, 'sold_at' => $now, + 'is_prepaid' => $usePrepaid, ]); - $this->logAdjustment($store, StoreInventoryAdjustment::TYPE_SALE, -$quantity, null, "Sold to Household #{$household->id}", null); - $this->updateBalance($store, -$quantity); + $this->logAdjustment($store, StoreInventoryAdjustment::TYPE_SALE, -$quantity, null, "Sold to Household #{$household->id}" . ($usePrepaid ? ' (Prepaid)' : ''), null); + $this->updateBalance($store, -$quantity, $usePrepaid); // Notify the household head — fire after commit so the receiver // sees the persisted state. @@ -217,7 +230,10 @@ class StoreOperations */ public function calculateBalanceDue(PartnerStore $store): int { - $netPayable = $store->sales()->sum(DB::raw('retail_price_centavos - commission_centavos')); + $netPayable = $store->sales() + ->where('is_prepaid', false) + ->sum(DB::raw('retail_price_centavos - commission_centavos')); + $totalPaid = $store->settlements()->sum('amount_centavos'); return max(0, (int) $netPayable - (int) $totalPaid); @@ -246,12 +262,20 @@ class StoreOperations ]); } - private function updateBalance(PartnerStore $store, int $delta): void + private function updateBalance(PartnerStore $store, int $delta, bool $isPrepaid = false): void { - $inventory = StoreInventory::firstOrCreate(['store_id' => $store->id], ['current_code_balance' => 0]); - $inventory->forceFill([ - 'current_code_balance' => max(0, $inventory->current_code_balance + $delta), - 'last_updated_at' => now(), - ])->save(); + $inventory = StoreInventory::firstOrCreate(['store_id' => $store->id], [ + 'current_code_balance' => 0, + 'prepaid_balance' => 0 + ]); + + if ($isPrepaid) { + $inventory->prepaid_balance = max(0, $inventory->prepaid_balance + $delta); + } else { + $inventory->current_code_balance = max(0, $inventory->current_code_balance + $delta); + } + + $inventory->last_updated_at = now(); + $inventory->save(); } } diff --git a/database/migrations/2026_07_02_101500_add_is_detouring_to_trips_table.php b/database/migrations/2026_07_02_101500_add_is_detouring_to_trips_table.php new file mode 100644 index 0000000..0d1bf6d --- /dev/null +++ b/database/migrations/2026_07_02_101500_add_is_detouring_to_trips_table.php @@ -0,0 +1,28 @@ +boolean('is_detouring')->default(false)->after('status'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('trips', function (Blueprint $バランス) { + $バランス->dropColumn('is_detouring'); + }); + } +}; diff --git a/database/migrations/2026_07_02_111116_add_prepaid_fields_to_store_tables.php b/database/migrations/2026_07_02_111116_add_prepaid_fields_to_store_tables.php new file mode 100644 index 0000000..c4f65b2 --- /dev/null +++ b/database/migrations/2026_07_02_111116_add_prepaid_fields_to_store_tables.php @@ -0,0 +1,38 @@ +unsignedInteger('prepaid_balance')->default(0)->after('current_code_balance'); + }); + + Schema::table('store_purchases', function (Blueprint $table) { + $table->boolean('is_prepaid')->default(false)->after('payment_id'); + }); + + Schema::table('store_sales', function (Blueprint $table) { + $table->boolean('is_prepaid')->default(false)->after('payment_id'); + }); + } + + public function down(): void + { + Schema::table('store_sales', function (Blueprint $table) { + $table->dropColumn('is_prepaid'); + }); + + Schema::table('store_purchases', function (Blueprint $table) { + $table->dropColumn('is_prepaid'); + }); + + Schema::table('store_inventories', function (Blueprint $table) { + $table->dropColumn('prepaid_balance'); + }); + } +}; diff --git a/database/migrations/2026_07_02_114152_update_collection_teams_status_enum.php b/database/migrations/2026_07_02_114152_update_collection_teams_status_enum.php new file mode 100644 index 0000000..22f01c5 --- /dev/null +++ b/database/migrations/2026_07_02_114152_update_collection_teams_status_enum.php @@ -0,0 +1,20 @@ +unsignedTinyInteger('helper_count')->default(0)->after('truck_id'); + }); + } + + public function down(): void + { + Schema::table('collection_teams', function (Blueprint $table) { + $table->dropColumn('helper_count'); + }); + } +}; diff --git a/resources/js/admin.js b/resources/js/admin.js index 914d8f8..9c7c19d 100644 --- a/resources/js/admin.js +++ b/resources/js/admin.js @@ -171,6 +171,7 @@ window.Verde = { requireAuth, logout, setSession, + getToken, getUser, toast, escapeHtml, diff --git a/resources/views/admin/teams.blade.php b/resources/views/admin/teams.blade.php index 3d4e073..8d70556 100644 --- a/resources/views/admin/teams.blade.php +++ b/resources/views/admin/teams.blade.php @@ -14,28 +14,16 @@ -
| Name | -Driver | -Scanner | -Truck | -Status | -Actions | -
|---|---|---|---|---|---|
| Loading… | |||||
Total Commission Earned
+₱0.00
+Your profit from all retail sales recorded.
+Total Revenue Collected
+₱0.00
+Gross amount collected from residents.
+Amount Owed to LGU
+₱0.00
+Total Revenue minus your Commission and previous payments.
+| Date | +Method | +Reference | +Amount | +
|---|---|---|---|
| Loading history... | +|||
Consignment Stock
+...
+Available codes (Consignment).
+Prepaid Stock
+...
+Available codes (Fully Paid).
+Total Acquired
+...
+Total codes issued to your store by the LGU.
+| Date | +Type | +Quantity | +Reason / Details | +Reference | +
|---|---|---|---|---|
| Loading history... | +||||
Showing ... results
+Search by resident name or address to start a transaction.
+...
+