webrteu / invoicing
Core invoicing abstraction for Laravel (Romania): one API for SmartBill, e-Factura and more
Requires
- php: ^8.2
- dompdf/dompdf: ^3.1
- illuminate/database: ^12.0|^13.0
- illuminate/events: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- illuminate/view: ^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^3.13
- laravel/pint: ^1.32
- orchestra/testbench: ^10.0|^11.0
- peckphp/peck: ^0.3
- pestphp/pest: ^3.8|^4.7
- pestphp/pest-plugin-laravel: ^3.2|^4.1
- pestphp/pest-plugin-type-coverage: ^3.6|^4.0.4
- phpstan/phpstan: ^2.3.3,!=2.3.4
- rector/rector: ^2.8
- webrteu/payments: ^1.0
Suggests
- webrteu/payments: Emiterea automată a facturii când o plată devine Paid
- webrteu/smartbill: Furnizorul SmartBill
Provides
None
Conflicts
None
Replaces
None
README
Facturare pentru orice proiect Laravel: facturi fiscale și proforme, numerotare proprie pe serii, TVA calculat pe
linii și pe cote, PDF din șablonul propriu (dompdf), anulare, tabela invoices legată de orice model din aplicație.
Furnizorii externi se adaugă cu pachete separate, cu aceeași interfață: webrteu/smartbill, iar mai târziu
pachetul pentru factura.webrt.eu.
Opțional, emite factura automat când webrteu/payments confirmă o plată.
Versiunea 2.x e o rescriere. 1.x era legată de aplicația Auditor (
Order,Client, servicii de audit, Filament). Aplicațiile pe 1.x rămân pe^1.0.
Cerințe
- PHP
^8.2, Laravel 12–13 - opțional:
webrteu/smartbill(furnizor),webrteu/payments(emitere automată la plată)
Instalare
composer require webrteu/invoicing
php artisan vendor:publish --tag=invoicing-config
php artisan vendor:publish --tag=invoicing-migrations
php artisan migrate
php artisan vendor:publish --tag=invoicing-views # doar dacă vrei să modifici șablonul PDF
Configurare
INVOICING_PROVIDER=local # local (numerotare proprie) | smartbill | ... INVOICING_SELLER_NAME="Firma Mea SRL" INVOICING_SELLER_CUI=RO12345678 INVOICING_SELLER_REG_COM=J40/123/2020 INVOICING_SELLER_ADDRESS="Str. Exemplu 1" INVOICING_SELLER_CITY=București INVOICING_SELLER_COUNTY=București INVOICING_SELLER_EMAIL=facturi@firma.ro INVOICING_SELLER_PHONE= INVOICING_SELLER_BANK="Banca Transilvania" INVOICING_SELLER_IBAN=RO49AAAA1B31007593840000 INVOICING_SELLER_VAT_PAYER=true # false = neplătitor de TVA: cota 0 pe toate liniile, automat INVOICING_SERIES_FISCAL=FACT # seriile furnizorului local INVOICING_SERIES_PROFORMA=PROF INVOICING_PDF_SOURCE=local # local = șablonul propriu | provider = PDF-ul furnizorului (ex. SmartBill) INVOICING_PDF_DISK=local # disk privat: facturile conțin date personale INVOICING_PDF_LOGO=/cale/absoluta/logo.png INVOICING_PDF_FOOTER="Factură valabilă fără semnătură și ștampilă." INVOICING_AUTO_ISSUE=false # true = factura la fiecare plată Paid (webrteu/payments)
Datele vânzătorului se salvează pe fiecare factură, deci o schimbare ulterioară în .env nu modifică facturile vechi.
Utilizare
use WeBRTeu\Invoicing\Data\InvoiceData; use WeBRTeu\Invoicing\Data\InvoiceLineData; use WeBRTeu\Invoicing\Data\PartyData; use WeBRTeu\Invoicing\Enums\InvoiceType; use WeBRTeu\Invoicing\Facades\Invoicing; $invoice = Invoicing::issue(new InvoiceData( type: InvoiceType::Fiscal, // sau InvoiceType::Proforma buyer: new PartyData('Client SRL', cui: 'RO987654', regCom: 'J12/3/2021', address: 'Str. Mare 2', city: 'Cluj-Napoca', county: 'Cluj', email: 'client@srl.ro'), lines: [ new InvoiceLineData('Tricou', unitPrice: 5000, quantity: 2, code: 'T-1'), // 50,00 lei cu TVA 21% inclus new InvoiceLineData('Livrare', unitPrice: 1500, isService: true), new InvoiceLineData('Consultanță', unitPrice: 20000, quantity: 1.5, unit: 'ore', priceIncludesVat: false), ], reference: $order->number, ), invoiceable: $order); $invoice->fullNumber(); // "FACT-0001" $invoice->total; // în bani return response(Invoicing::pdf($invoice), 200, ['Content-Type' => 'application/pdf']);
- Sumele sunt în bani (int). TVA-ul se calculează și se rotunjește pe linie. Cota implicită e 21%, iar per linie poți pune
vatRate: 11sau0. - Persoană fizică:
new PartyData('Ion Popescu', email: ..., city: ...), fără CUI. issueDateimplicit e azi, iardueDatee azi +invoicing.due_days(15;null= fără scadență).- La eroarea furnizorului rândul rămâne cu status
Failed, cu mesajul înerror, iar excepția e aruncată mai departe. Fără retry automat, ca să nu se emită de două ori.
| Metodă | Ce face |
|---|---|
Invoicing::issue($data, $invoiceable, provider: null) |
emite factura și întoarce modelul Invoice |
Invoicing::cancel($invoice) |
anulează la furnizor și marchează Cancelled |
Invoicing::pdf($invoice, source: null) |
PDF-ul (bytes), din local sau provider; implicit din config |
Invoicing::html($invoice) |
HTML-ul șablonului, pentru previzualizare |
Invoicing::provider('smartbill') |
furnizorul, pentru metodele lui specifice |
NumberSequence::set('FACT', 120) |
continuă numerotarea locală de la 121 (mutare de pe alt program) |
Șablonul invoicing::pdf.invoice e generic, în română sau engleză (după language din InvoiceData), cu diacritice
(DejaVu Sans), logo și subsol. Îl publici cu --tag=invoicing-views și îl modifici, sau pui alt view în invoicing.pdf.view.
Cu pdf.store = true, PDF-ul se salvează la prima generare (invoices/{sursa}/{tip}-{număr}.pdf pe disk-ul din config)
și se refolosește. Cu SmartBill poți avea ambele variante: Invoicing::pdf($invoice, 'local') (șablonul tău, cu numărul
SmartBill) sau Invoicing::pdf($invoice, 'provider') (PDF-ul SmartBill).
Emitere automată la plată
class Order extends Model implements \WeBRTeu\Invoicing\Contracts\Invoiceable { public function toInvoiceData(InvoiceType $type): InvoiceData { return new InvoiceData($type, buyer: ..., lines: ..., reference: $this->number); } }
Cu INVOICING_AUTO_ISSUE=true, când o plată webrteu/payments legată de comandă (payable) devine Paid, se emite
factura (tipul și furnizorul din invoicing.auto_issue), o singură dată per comandă. O eroare se loghează
(invoicing.auto_issue_failed) și nu oprește procesarea plății.
Evenimente
InvoiceIssued($invoice): ex. trimiți factura pe emailInvoiceCancelled($invoice)
Furnizor propriu
class MyProvider implements \WeBRTeu\Invoicing\Contracts\InvoiceProvider // + ProvidesPdf, opțional { public function name(): string { return 'my'; } public function issue(InvoiceData $invoice): IssuedInvoiceData { /* ... */ } public function cancel(Invoice $invoice): void { /* ... */ } } Invoicing::extend('my', fn ($app) => $app->make(MyProvider::class)); // în service provider
Ce nu face pachetul
- e-Factura / SPV (pachet separat)
- interfață de administrare: o faci în proiect (Filament, Livewire)
- storno automat: o factură fiscală trimisă clientului se stornează cu o factură nouă, cu cantități negative
- emailuri: ascultă
InvoiceIssued
Testare
composer test
Changelog
Vezi CHANGELOG.md.
Autor
Licență
MIT. Vezi LICENSE.md.