From cac7c60415803961cdd483f0d7ce4b91a0ce5504 Mon Sep 17 00:00:00 2001 From: isaacclad Date: Wed, 8 Jul 2026 05:48:40 +0000 Subject: [PATCH] Add receipt logo upload and redesign thermal receipt header. Per-location logo in settings with remove/replace support; receipt template reserves space at the top when a logo is set. Co-authored-by: Cursor --- .../Controllers/Pos/SettingsController.php | 20 ++++++ app/Models/PosLocation.php | 11 +++ ...add_receipt_logo_path_to_pos_locations.php | 22 ++++++ resources/views/pos/receipts/print.blade.php | 54 +++++++++++++-- resources/views/pos/settings.blade.php | 25 ++++++- tests/Feature/PosHardwareTest.php | 68 +++++++++++++++++++ 6 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 database/migrations/2026_07_08_130000_add_receipt_logo_path_to_pos_locations.php diff --git a/app/Http/Controllers/Pos/SettingsController.php b/app/Http/Controllers/Pos/SettingsController.php index 1ce2b1b..1a451ef 100644 --- a/app/Http/Controllers/Pos/SettingsController.php +++ b/app/Http/Controllers/Pos/SettingsController.php @@ -11,6 +11,7 @@ use App\Services\Import\MerchantCatalogImportService; use App\Services\Pos\PosLocationService; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Storage; use Illuminate\View\View; use RuntimeException; @@ -43,6 +44,8 @@ class SettingsController extends Controller 'service_style' => ['required', 'in:retail,restaurant'], 'receipt_footer' => ['nullable', 'string', 'max:1000'], 'receipt_header' => ['nullable', 'string', 'max:500'], + 'receipt_logo' => ['nullable', 'image', 'mimes:jpeg,jpg,png,gif,webp', 'max:2048'], + 'remove_receipt_logo' => ['sometimes', 'boolean'], 'printer_paper_mm' => ['required', 'in:58,80'], 'printer_auto_print' => ['sometimes', 'boolean'], ]); @@ -54,6 +57,22 @@ class SettingsController extends Controller } $location = $this->locations->ensureDefault($this->ownerRef($request)); + + if ($request->boolean('remove_receipt_logo') && $location->receipt_logo_path) { + Storage::disk('public')->delete($location->receipt_logo_path); + $location->receipt_logo_path = null; + } + + if ($request->hasFile('receipt_logo')) { + if ($location->receipt_logo_path) { + Storage::disk('public')->delete($location->receipt_logo_path); + } + $location->receipt_logo_path = $request->file('receipt_logo')->store( + 'pos/receipt-logos/'.$this->ownerRef($request), + 'public' + ); + } + $location->update([ 'name' => $data['name'], 'currency' => strtoupper($data['currency']), @@ -62,6 +81,7 @@ class SettingsController extends Controller 'receipt_header' => $data['receipt_header'] ?? null, 'printer_paper_mm' => (int) $data['printer_paper_mm'], 'printer_auto_print' => $request->boolean('printer_auto_print'), + 'receipt_logo_path' => $location->receipt_logo_path, ]); return back()->with('success', 'Settings saved.'); diff --git a/app/Models/PosLocation.php b/app/Models/PosLocation.php index 8ecc0f1..b6b7238 100644 --- a/app/Models/PosLocation.php +++ b/app/Models/PosLocation.php @@ -5,6 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Support\Facades\Storage; class PosLocation extends Model { @@ -19,6 +20,7 @@ class PosLocation extends Model 'service_style', 'receipt_footer', 'receipt_header', + 'receipt_logo_path', 'printer_paper_mm', 'printer_auto_print', ]; @@ -36,6 +38,15 @@ class PosLocation extends Model return $this->service_style === self::STYLE_RESTAURANT; } + public function receiptLogoUrl(): ?string + { + if (! $this->receipt_logo_path) { + return null; + } + + return Storage::disk('public')->url($this->receipt_logo_path); + } + public function products(): HasMany { return $this->hasMany(PosProduct::class, 'location_id'); diff --git a/database/migrations/2026_07_08_130000_add_receipt_logo_path_to_pos_locations.php b/database/migrations/2026_07_08_130000_add_receipt_logo_path_to_pos_locations.php new file mode 100644 index 0000000..442f990 --- /dev/null +++ b/database/migrations/2026_07_08_130000_add_receipt_logo_path_to_pos_locations.php @@ -0,0 +1,22 @@ +string('receipt_logo_path', 500)->nullable()->after('receipt_header'); + }); + } + + public function down(): void + { + Schema::table('pos_locations', function (Blueprint $table) { + $table->dropColumn('receipt_logo_path'); + }); + } +}; diff --git a/resources/views/pos/receipts/print.blade.php b/resources/views/pos/receipts/print.blade.php index 596ba24..7d3d7a4 100644 --- a/resources/views/pos/receipts/print.blade.php +++ b/resources/views/pos/receipts/print.blade.php @@ -9,6 +9,8 @@ $paper = in_array((int) ($location?->printer_paper_mm ?? 80), [58, 80], true) ? (int) $location->printer_paper_mm : 80; + $logoUrl = $location?->receiptLogoUrl(); + $hasLogo = filled($logoUrl); @endphp