Files
InvoiceShelf/CLAUDE.md

3.8 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

InvoiceShelf is an open-source invoicing and expense tracking application built with Laravel 13 (PHP 8.4) and Vue 3. It supports multi-company tenancy, customer portals, recurring invoices, and PDF generation.

Common Commands

Development

composer run dev          # Starts PHP server, queue listener, log tail, and Vite dev server concurrently
npm run dev               # Vite dev server only
npm run build             # Production frontend build

Testing

php artisan test --compact                        # Run all tests
php artisan test --compact --filter=testName       # Run specific test
./vendor/bin/pest --stop-on-failure                # Run via Pest directly
make test                                          # Makefile shortcut

Tests use SQLite in-memory DB, configured in phpunit.xml. Tests seed via DatabaseSeeder + DemoSeeder in beforeEach. Authenticate with Sanctum::actingAs() and set the company header.

Code Style

vendor/bin/pint --dirty --format agent    # Fix style on modified PHP files
vendor/bin/pint --test                    # Check style without fixing (CI uses this)

Artisan Generators

Always use php artisan make:* with --no-interaction to create new files (models, controllers, migrations, tests, etc.).

Architecture

Multi-Tenancy

Every major model has a company_id foreign key. The CompanyMiddleware sets the active company from the company request header. Bouncer authorization is scoped to the company level via DefaultScope (app/Bouncer/Scopes/DefaultScope.php).

Authentication

Three guards: web (session), api (Sanctum tokens for /api/v1/), customer (session for customer portal). API routes use auth:sanctum middleware; customer portal uses auth:customer.

Routing

  • API: All endpoints under /api/v1/ in routes/api.php, grouped with auth:sanctum, company, and bouncer middleware
  • Web: routes/web.php serves PDF endpoints, auth pages, and catch-all SPA routes (/admin/{vue?}, /{company:slug}/customer/{vue?})

Frontend

  • Entry point: resources/scripts/main.js
  • Vue Router: resources/scripts/admin/admin-router.js (admin), resources/scripts/customer/customer-router.js (customer portal)
  • State: Pinia stores in resources/scripts/admin/stores/
  • Path aliases: @ = resources/, $fonts, $images for static assets
  • Vite dev server expects invoiceshelf.test hostname

Backend Patterns

  • Authorization: Silber/Bouncer with policies in app/Policies/. Controllers use $this->authorize().
  • Validation: Form Request classes, never inline validation
  • API responses: Eloquent API Resources in app/Http/Resources/
  • PDF generation: DomPDF (GeneratesPdfTrait) or Gotenberg
  • Email: Mailable classes with EmailLog tracking
  • File storage: Spatie MediaLibrary, supports local/S3/Dropbox
  • Serial numbers: SerialNumberService service
  • Company settings: CompanySetting model (key-value per company)

Database

Supports MySQL, PostgreSQL, and SQLite. Prefer Eloquent over raw queries. Use Model::query() instead of DB::. Use eager loading to prevent N+1 queries.

Code Conventions

  • PHP: snake_case, constructor property promotion, explicit return types, PHPDoc blocks over inline comments
  • JS: camelCase
  • Always check sibling files for patterns before creating new ones
  • Use config() helper, never env() outside config files
  • Every change must have tests (feature tests preferred over unit tests)
  • Run vendor/bin/pint --dirty --format agent after modifying PHP files

CI Pipeline

GitHub Actions (check.yaml): runs Pint style check, then builds frontend and runs Pest tests on PHP 8.4.