aichadigital/larabill
Composer 安装命令:
composer require aichadigital/larabill
包简介
Professional billing & invoicing package for Laravel with UUID v7, VAT verification, tax calculation for Spain/EU/worldwide, and EU compliance
关键字:
README 文档
README
ℹ️ Schema upgrade policy — Larabill does not promise in-place schema upgrades between versions. Install fresh and seed with
larabill:install(ormigrate:fresh) rather than migrating an existing schema across major versions. See ADR-006.
Larabill is a professional, UUID-first billing and invoicing package for Laravel applications. It provides comprehensive VAT verification, tax calculation for Spain/EU/worldwide, and flexible invoice generation with immutability protection. The consumer app's users.id MUST be UUID v7 char(36) — see docs/setup-uuid.md and ADR-006.
🎯 Features
Core Functionality
- Invoice Management: UUID-based IDs, sequential numbering, proforma invoices, immutable records
- Tax Calculation: Spanish (IVA), Canary Islands (IGIC), Ceuta/Melilla (IPSI), EU reverse charge, worldwide
- VAT/Tax Code Verification: Integration with AbstractAPI and APILayer for real-time validation
- Fiscal Data Management: Company and customer fiscal configurations with temporal validity
- PDF Generation: Built-in invoice PDF generation using DomPDF
- EU Compliance: Full support for EU B2B reverse charge and destination VAT rules
Technical Excellence
- String UUID v7: Ordered UUIDs for invoices and the consumer's
users.id(ADR-006) - FixedDecimal money: Precise monetary value objects backed by base-100 integers (no floating-point errors)
- Preflight check:
larabill:installaborts cleanly whenusers.idis not UUID-compatible - Temporal Validity: Fiscal configurations with
valid_from/valid_untildates - Invoice Immutability: Protection against modifications after issuance
📦 Requirements
- PHP ^8.3
- Laravel ^12.0 | ^13.0
users.idUUID v7 char(36) — seedocs/setup-uuid.md
🚀 Installation
Via Composer
composer require aichadigital/larabill
Publish Configuration
php artisan vendor:publish --tag="larabill-config"
Run the Installer
php artisan larabill:install
This will:
- Publish migrations
- Run database migrations
- Seed default tax categories and rates
Manual Installation (if preferred)
# Publish migrations php artisan vendor:publish --tag="larabill-migrations" # Run migrations php artisan migrate # Seed default data php artisan db:seed --class="AichaDigital\Larabill\Database\Seeders\TaxCategoriesSeeder" php artisan db:seed --class="AichaDigital\Larabill\Database\Seeders\TaxRatesSeeder"
⚙️ Configuration
Environment Variables
Add these to your .env file:
# Tax Code Verification APIs LARABILL_ABSTRACTAPI_KEY="your_abstractapi_key" LARABILL_APILAYER_KEY="your_apilayer_key" LARABILL_VAT_PREFERRED_API="abstractapi" LARABILL_VAT_CACHE_DAYS=30 # Invoice Numbering LARABILL_INVOICE_PREFIX="FAC" LARABILL_PROFORMA_PREFIX="PRO" # Optional: override the User model class. Must use UUID v7 char(36) ids. LARABILL_USER_MODEL="App\\Models\\User"
Model Configuration
Configure your user model in config/larabill.php:
'models' => [ 'user' => \App\Models\User::class, 'invoice' => \AichaDigital\Larabill\Models\Invoice::class, 'invoice_item' => \AichaDigital\Larabill\Models\InvoiceItem::class, // ... ],
🏗️ Architecture
Fiscal Data Model
Larabill separates company and customer fiscal data with temporal validity:
CompanyFiscalConfig → Issuer fiscal settings (one active at a time)
UserTaxProfile → Customer fiscal data, temporally versioned per user
Invoice → Immutable invoice with fiscal snapshot
Key principles:
- The customer is a
User(ADR-003); businesses and sub-accounts are modelled withparent_user_id. The legacyCustomerFiscalDatamodel was removed. - Company config changes apply from a specific date forward
UserTaxProfilerecords are temporally versioned (valid_from/valid_until) — never modify past records- Invoices capture a fiscal snapshot at creation time
- Invoices are absolutely immutable once issued
UUID Strategy
Larabill uses string UUID v7 for invoices:
// Model with UUID use AichaDigital\Larabill\Concerns\HasUuid; class Invoice extends Model { use HasUuid; } // Migration $table->uuid('id')->primary();
Monetary Values (FixedDecimal)
Money is stored as base-100 integers and exposed as FixedDecimal value objects (from lara100), so there are no floating-point errors. You assign the unscaled base-100 integer; reading the attribute returns a FixedDecimal:
// Assign the base-100 integer (€12.34 → 1234): $invoice->total_amount = 1234; // Reading the attribute returns a FixedDecimal value object // (base-100 backed, scale 2) — not a raw int: $money = $invoice->total_amount; // FixedDecimal
Invoice and invoice-item money attributes use the FixedDecimalCast (scale 2) from the lara100 package. Note: query-builder access (->value(), ->sum(), ->where()) returns the raw integer, while Eloquent attribute access returns a FixedDecimal.
📖 Usage
Creating an Invoice
use AichaDigital\Larabill\Services\BillingService; $billingService = app(BillingService::class); $invoice = $billingService->createInvoice([ 'user_id' => $user->id, // UUID v7 of the customer (a User; ADR-003) 'items' => [ [ 'description' => 'Professional Service', 'quantity' => 100, // base-100: 100 = 1.0 unit 'unit_price' => 10000, // base-100: 10000 = €100.00 'tax_group_id' => $taxGroup->id, // resolves the applicable VAT/IGIC/IPSI ], ], ]);
Tax Calculation
use AichaDigital\Larabill\Services\TaxCalculationService; $taxService = app(TaxCalculationService::class); // Calculate taxes for a single line. Amounts are base-100 integers. The // applicable rate (Spanish IVA, Canary IGIC, Ceuta/Melilla IPSI, EU reverse // charge or destination VAT) is resolved from the TaxGroup and the customer's // fiscal profile — not passed in directly. $result = $taxService->calculateForInvoiceItem([ 'quantity' => 100, // base-100: 1.0 unit 'base_price' => 10000, // base-100: €100.00 'tax_group_id' => $taxGroup->id, 'billable_user_id' => $user->id, // optional: drives B2B / destination rules ]); // $result keys (base-100 integers + breakdown): // taxable_amount, total_tax_amount, total_amount, tax_group_id, taxes_applied
VAT Verification
use AichaDigital\Larabill\Services\VatVerificationService; $vatService = app(VatVerificationService::class); $result = $vatService->verifyVatNumber('ESB12345678', 'ES'); if ($result['is_valid']) { echo "Valid VAT for: " . $result['company_name']; }
Company Fiscal Configuration
use AichaDigital\Larabill\Models\CompanyFiscalConfig; // Get current active config $config = CompanyFiscalConfig::getActive(); // Create new config (the previous active one is auto-closed) $newConfig = CompanyFiscalConfig::createNew([ 'tax_id' => 'ESB12345678', 'business_name' => 'Your Company S.L.', 'address' => 'Calle Test 123', 'city' => 'Madrid', 'zip_code' => '28001', 'country_code' => 'ES', 'is_oss' => true, 'valid_from' => now(), ]);
User Tax Profile
The customer is a User (ADR-003); their fiscal data lives in UserTaxProfile, temporally versioned. This replaces the removed CustomerFiscalData model.
use AichaDigital\Larabill\Models\UserTaxProfile; // Get the active fiscal profile for a user $profile = UserTaxProfile::getActiveForOwner($user->id); // Create a new profile (previous stays as history) $newProfile = UserTaxProfile::createForOwner($user->id, [ 'fiscal_name' => 'Client SARL', 'tax_id' => 'FR12345678901', 'country_code' => 'FR', 'is_company' => true, ]);
🧪 Testing
# Run all tests composer test # Run specific tests composer test -- --filter=Invoice # Run with coverage composer test-coverage # Static analysis vendor/bin/phpstan analyse
Current status (v3.1.1): 1021 tests passing on SQLite, plus MySQL 8 integration tests (real column types and unique constraints) and fork-based concurrency tests. The UUID-first contract is demonstrated on MySQL 8.
📚 Documentation
| Document | Description |
|---|---|
| ARCHITECTURE.md | Core architecture and domain model |
| setup-uuid.md | UUID-first onboarding for the consumer app |
| ADR-006 | UUID-first decision (supersedes the agnostic id contract) |
| TAX_RATES_MIGRATION_GUIDE.md | Tax rates migration guide |
| CHANGELOG.md | Version history and breaking changes |
For AI agents working with this package, see .claude/project.md.
🗺️ Roadmap
Shipped
- ✅ Core invoice management (immutable records, UUID v7, sequential numbering, proforma)
- ✅ Spanish tax system (IVA, IGIC, IPSI)
- ✅ EU reverse charge (B2B) and destination VAT
- ✅ Fiscal data with temporal validity (
CompanyFiscalConfig,UserTaxProfile) - ✅
FixedDecimalmoney type (base-100, no floating-point errors) - ✅ VeriFACTU integration (Spain AEAT) via
lara-verifactu - ✅ Grouped payments
- ✅ Legal-retention contract (
LegallyRetainable) for GDPR tooling
Under consideration
- Subscription billing
- Payment gateway integration (Stripe, PayPal, Redsys)
- Advanced reporting
See the CHANGELOG for the full release history.
🤝 Contributing
Please see CONTRIBUTING for details.
🔒 Security
Please review our security policy on how to report security vulnerabilities.
📄 License
GNU Affero General Public License v3.0 (AGPL-3.0-or-later). See LICENSE.md for details.
This means:
- ✅ You can use, modify, and distribute this software
- ✅ You must share any modifications under the same license
- ⚠️ If you run this as a network service, you must provide the source code to users
- ⚠️ You must preserve copyright and attribution notices
👥 Credits
统计信息
- 总下载量: 106
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 27
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: AGPL-3.0-or-later
- 更新时间: 2026-01-08