psys / order-invoice-bundle
Symfony bundle for managing orders & invoices in non-shop environments
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:symfony-bundle
Requires
- php: >=8.2
- doctrine/doctrine-bundle: ^2.14
- doctrine/orm: ^3.3
- psys/utils: ^1.0.10
- twig/twig: ^3.21
Requires (Dev)
- rector/rector: ^2.0
Suggests
- mpdf/mpdf: Needed for the MpdfExporter
README
Use case
- You're not running a typical online store — full-featured e-commerce platforms would be overkill.
Features
- manages orders and associated invoices
- exports invoices
Installation
composer req psys/order-invoice-bundle
The recipe has not been merged yet. See: symfony/recipes-contrib#1820
1. Set your target entities
# config/packages/doctrine.yaml orm: resolve_target_entities: Psys\OrderInvoiceBundle\Model\CustomerInterface: App\Entity\YourCustomerEntity Psys\OrderInvoiceBundle\Model\FileInterface: App\Entity\YourFileEntity
- And let them implement the interfaces mentioned
2. Init database
symfony console make:migration
Then rename the migrations/VersionOrderInvoiceInit.php
(also the class inside), so it runs just after the migration you've just created.
symfony console doctrine:migrations:migrate
3. Define categories for orders and/or its items (OPTIONAL)
namespace App\Lib; enum MyOrderCategory :int { case FOO = 1; case BAR = 2; }
How to use
Creating a new order and its proforma invoice:
use Psys\OrderInvoiceBundle\Entity\Invoice; use Psys\OrderInvoiceBundle\Entity\InvoiceBuyer; use Psys\OrderInvoiceBundle\Entity\InvoiceProforma; use Psys\OrderInvoiceBundle\Entity\InvoiceSeller; use Psys\OrderInvoiceBundle\Entity\Order; use Psys\OrderInvoiceBundle\Entity\OrderItem; use Psys\OrderInvoiceBundle\Model\OrderItem\AmountType; use Psys\OrderInvoiceBundle\Model\Order\PaymentMode; use Psys\OrderInvoiceBundle\Model\Order\State; use Psys\OrderInvoiceBundle\Service\InvoiceManager\InvoiceManager; use Psys\OrderInvoiceBundle\Service\OrderManager\OrderManager; use Symfony\Bundle\SecurityBundle\Security; use App\Lib\MyOrderCategory; public function newOrder (OrderManager $orderManager, InvoiceManager $invoiceManager, Security $security) : void { $ent_Order = new Order(); $ent_Order->setCategory(MyOrderCategory::FOO); $ent_Order->setPaymentMode(PaymentMode::BANK_ACCOUNT_REGULAR); $ent_Order->setPaymentModeBankAccount('5465878565/6556'); $ent_Order->setCustomer($security->getUser()); // Customer can be null $ent_Order->setCreatedAt(new \DateTimeImmutable()); $ent_Order->setState(State::NEW); $ent_Order->addOrderItem( (new OrderItem()) ->setName('Foo') ->setPriceVatIncluded(1599) // If not set, it will be automatically calculated from price exclusive of VAT ->setPriceVatExcluded(1300) // If not set, it will be automatically calculated from price inclusive of VAT ->setVatRate(21) ->setAmount(1) ->setAmountType(AmountType::ITEM) ); $ent_InvoiceProforma = (new InvoiceProforma()) ->setCreatedAt(new \DateTimeImmutable()) ->setDueDate(new \DateTimeImmutable('+14 days')); $invoiceManager->setSequentialNumber($ent_InvoiceProforma); // Use custom formatting for the reference number $ent_InvoiceProforma->setReferenceNumber(date('Y').$ent_InvoiceProforma->getSequentialNumber()); $ent_Invoice = (new Invoice()) ->setInvoiceProforma($ent_InvoiceProforma) ->setInvoiceBuyer ( (new InvoiceBuyer()) ->setFullName('John Doe') ->setStreetAddress1('Street') ->setStreetAddress2('123') ->setCity('Some City') ->setPostcode('25689') ->setRegion('Some Region') ->setCountry('Italy') ) ->setInvoiceSeller ( (new InvoiceSeller()) ->setOrganization('Seller Organization') ->setStreetAddress1('Street 123') ->setStreetAddress2('123') ->setCity('Some City') ->setPostcode('25689') ->setRegion('Some Region') ->setCountry('United Kingdom') ->setVatIdentificationNumber('5468484') ->setCompanyIdentificationNumber('5655') ->setLegalEntityRegistrationDetails('Registered in England & Wales No. 01234567 · Registered office : 1 King’s Road, London SW1') ); $invoiceManager->setUniqueVariableSymbol($ent_Invoice); $ent_Order->setInvoice($ent_Invoice); $orderManager->processAndSaveNewOrder($ent_Order); }
Exporting an invoice:
Set the template path and choose the engine:
# config/packages/psys_order_invoice.yaml psys_order_invoice: pdf_exporter: engine: mpdf template_path: invoice/oi_default.html.twig
Available exporter engines: mpdf
Or you can create your custom exporter by implementing the Psys\OrderInvoiceBundle\Service\InvoiceExporter\ExporterInterface
and adjusting the config to:
# config/packages/psys_order_invoice.yaml psys_order_invoice: pdf_exporter: class: App\Lib\MyExporter
Example using the mpdf exporter:
use Psys\OrderInvoiceBundle\Service\InvoiceExporter\MpdfExporter; use Psys\OrderInvoiceBundle\Model\Invoice\ExportMode; use Psys\OrderInvoiceBundle\Model\Invoice\InvoiceType; use Symfony\Component\Filesystem\Filesystem; use Doctrine\ORM\EntityManagerInterface; public function saveInvoice (MpdfExporter $mpdfExporter, Filesystem $filesystem, Order $ent_Order, EntityManagerInterface $entityManager) : void { $absPath = $filesystem->tempnam('/some/dir', '', '.pdf'); $mpdfExporter->export($ent_Order, InvoiceType::PROFORMA, ExportMode::FILE, '', $absPath); $ent_File = new File(); $ent_File->setMimeType('application/pdf'); $ent_File->setNameFileSystem(basename($absPath)); $ent_File->setNameDisplay('my_invoice.pdf'); $ent_File->setCreatedAt(); $ent_InvoiceProforma = $ent_Order->getInvoice()->getInvoiceProforma(); $ent_InvoiceProforma->setFile($ent_File); $entityManager->persist($ent_File); $entityManager->persist($ent_InvoiceProforma); $entityManager->flush(); }
Reseting sequential numbers:
use App\Lib\InvoiceManager; public function resetSequentialNumbers (InvoiceManager $invoiceManager) : void { // This method is meant to be used inside a cron. // This cron needs to be run 1 to 10 minutes before a new year. $invoiceManager->resetSequentialNumbersEveryYear(); // Use this method for resetting sequential numbers whenever you want. $invoiceManager->resetSequentialNumbers(); }