Skip to content

Commit

Permalink
Merge pull request #25 from josemmo/develop
Browse files Browse the repository at this point in the history
v0.2.1
  • Loading branch information
josemmo authored Jun 8, 2022
2 parents 967d57e + 9ddfb64 commit c4a1d1b
Show file tree
Hide file tree
Showing 19 changed files with 583 additions and 76 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
</p>

## About
eInvoicing is a PHP library for creating and reading electronic invoices according to the [eInvoicing Directive and European standard](https://ec.europa.eu/cefdigital/wiki/display/CEFDIGITAL/eInvoicing).
eInvoicing is a PHP library for creating and reading electronic invoices according to the [eInvoicing Directive and European standard](https://ec.europa.eu/digital-building-blocks/wikis/display/DIGITAL/eInvoicing).

It aims to be 100% compliant with [EN 16931](https://ec.europa.eu/cefdigital/wiki/x/kwFVBg) as well as with the most popular CIUS and extensions, such as [PEPPOL BIS](https://docs.peppol.eu/poacc/billing/3.0/bis/).
It aims to be 100% compliant with [EN 16931](https://ec.europa.eu/digital-building-blocks/wikis/x/boTXGw) as well as with the most popular CIUS and extensions, such as [PEPPOL BIS](https://docs.peppol.eu/poacc/billing/3.0/bis/).

## Installation
First of all, make sure your environment meets the following requirements:
Expand Down Expand Up @@ -98,7 +98,7 @@ echo $writer->export($inv);
These are the expected features for the library and how's it going so far:

- [x] Representation of invoices, parties and invoice lines as objects
- [x] Compatibility with the most used [CIUS and extensions](https://ec.europa.eu/cefdigital/wiki/x/5xLoAg)
- [x] Compatibility with the most used [CIUS and extensions](https://ec.europa.eu/digital-building-blocks/wikis/display/EINVCOMMUNITY/Registry+of+CIUS+%28Core+Invoice+Usage+Specifications%29+and+Extensions)
- [x] Export invoices to UBL documents
- [x] Import invoices from UBL documents
- [ ] Export invoices to CII documents
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
},
"require": {
"php": ">=7.1",
"josemmo/uxml": "^0.1.3"
"josemmo/uxml": "^0.1.4"
},
"require-dev": {
"ext-openssl": "*",
Expand Down
6 changes: 3 additions & 3 deletions docs/getting-started/eu-einvoicing-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ more particular cases, such us the ones mentioned before.
The most popular CIUS is [PEPPOL BIS Billing 3.0][4], a cross-border specification used by multiple countries and
international companies from both public and private sectors.

[1]: https://ec.europa.eu/cefdigital/wiki/display/CEFDIGITAL/Navigating+the+eInvoicing+standard+documentation
[2]: https://ec.europa.eu/cefdigital/wiki/display/CEFDIGITAL/Required+syntaxes
[3]: https://ec.europa.eu/cefdigital/wiki/x/5xLoAg
[1]: https://ec.europa.eu/digital-building-blocks/wikis/display/DIGITAL/Navigating+the+eInvoicing+standard+documentation
[2]: https://ec.europa.eu/digital-building-blocks/wikis/display/DIGITAL/Required+syntaxes
[3]: https://ec.europa.eu/digital-building-blocks/wikis/display/EINVCOMMUNITY/Registry+of+CIUS+%28Core+Invoice+Usage+Specifications%29+and+Extensions
[4]: http://docs.peppol.eu/poacc/billing/3.0/
2 changes: 1 addition & 1 deletion docs/getting-started/installation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Getting Started
eInvoicing (short for "European Invoicing") is a free and open-source library written in PHP for creating and reading
electronic invoices compliant with [EN 16931](https://ec.europa.eu/cefdigital/wiki/x/kwFVBg).
electronic invoices compliant with [EN 16931](https://ec.europa.eu/digital-building-blocks/wikis/x/boTXGw).

## Requirements
In order to install this library, your environment has to meet the following requirements:
Expand Down
6 changes: 3 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# European Invoicing (eInvoicing)

eInvoicing is a PHP library for creating and reading electronic invoices according to the [eInvoicing Directive and European standard](https://ec.europa.eu/cefdigital/wiki/display/CEFDIGITAL/eInvoicing).
eInvoicing is a PHP library for creating and reading electronic invoices according to the [eInvoicing Directive and European standard](https://ec.europa.eu/digital-building-blocks/wikis/display/DIGITAL/eInvoicing).

It aims to be 100% compliant with [EN 16931](https://ec.europa.eu/cefdigital/wiki/x/kwFVBg) as well as with the most popular CIUS and extensions, such as [PEPPOL BIS](https://docs.peppol.eu/poacc/billing/3.0/bis/).
It aims to be 100% compliant with [EN 16931](https://ec.europa.eu/digital-building-blocks/wikis/x/boTXGw) as well as with the most popular CIUS and extensions, such as [PEPPOL BIS](https://docs.peppol.eu/poacc/billing/3.0/bis/).

[Get Started](getting-started/installation.md){: .md-button .md-button--primary }
&nbsp;
[Go to GitHub](https://github.com/josemmo/einvoicing){: .md-button }
[Go to GitHub](https://github.com/josemmo/einvoicing){: .md-button }
27 changes: 21 additions & 6 deletions scripts/build-docs.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<?php
use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
use phpDocumentor\Reflection\DocBlock\Tags\Param;
use phpDocumentor\Reflection\DocBlock\Tags\Return_;
use phpDocumentor\Reflection\DocBlock\Tags\See;
use phpDocumentor\Reflection\DocBlock\Tags\Throws;
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
use phpDocumentor\Reflection\File\LocalFile;
Expand All @@ -27,7 +29,7 @@

/**
* Get project files
* @return Element[string] Array of documentable classes
* @return array<string,Element> Array of documentable classes
*/
function getProjectFiles(): array {
$files = [];
Expand Down Expand Up @@ -55,7 +57,7 @@ function getProjectFiles(): array {
* Get class public elements
* @param Class_ $class Class instance
* @param string $type Element type ("constants", "properties" or "methods")
* @param Element[string] &$project Project files
* @param array<string,Element> &$project Project files
* @return Constant[]|Property[]|Method[] Public elements
*/
function getPublicElements(Class_ $class, string $type, array &$project): array {
Expand Down Expand Up @@ -102,9 +104,9 @@ function getClassUrl(string $fqsen): string {

/**
* Render class
* @param Class_ $class Class instance
* @param Element[string] &$project Project files
* @return string Markdown documentation
* @param Class_ $class Class instance
* @param array<string,Element> &$project Project files
* @return string Markdown documentation
*/
function renderClass(Class_ $class, array &$project): string {
$doc = "# {$class->getFqsen()}\n\n";
Expand Down Expand Up @@ -180,9 +182,22 @@ function renderMethod(Method $method, array $addDocblocks, Class_ $class): strin
$return = $docblock->getTagsByName('return')[0] ?? null;

// Method summary
$doc = "## `{$method->getName()}()`\n";
$doc = "## `{$method->getName()}()`\n";
$doc .= $docblock->getSummary() . "\n";

// Deprecation warning
/** @var Deprecated|null */
$deprecated = $docblock->getTagsByName('deprecated')[0] ?? null;
if ($deprecated !== null) {
/** @var See */
$see = $docblock->getTagsByName('see')[0];
$seeRef = $see->getReference();
$doc .= "\n";
$doc .= "!!! warning \"Deprecated since v{$deprecated->getVersion()}\"\n";
$doc .= "\n";
$doc .= " Use [`$seeRef`](#" . strtolower(substr($seeRef, strpos($seeRef, '::')+2, -2)) . ") instead.\n";
}

// Signature
$doc .= "\n```php\n";
$doc .= "public " . ($method->isStatic() ? "static " : "") . $method->getName() . "(";
Expand Down
114 changes: 111 additions & 3 deletions src/Invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use function array_splice;
use function count;
use function is_subclass_of;
use function round;

class Invoice {
const DEFAULT_DECIMALS = 8;
Expand All @@ -27,17 +28,19 @@ class Invoice {
protected $number = null;
protected $type = 380; // TODO: add constants
protected $currency = "EUR"; // TODO: add constants
protected $vatCurrency = null;
protected $issueDate = null;
protected $dueDate = null;
protected $taxPointDate = null;
protected $note = null;
protected $notes = [];
protected $buyerReference = null;
protected $purchaseOrderReference = null;
protected $salesOrderReference = null;
protected $tenderOrLotReference = null;
protected $contractReference = null;
protected $paidAmount = 0;
protected $roundingAmount = 0;
protected $customVatAmount = null;
protected $seller = null;
protected $buyer = null;
protected $payee = null;
Expand Down Expand Up @@ -83,6 +86,21 @@ public function getDecimals(string $field): int {
}


/**
* Round value
* @param float $value Value to round
* @param string $field Field name
* @return float Rounded value
*/
public function round(float $value, string $field): float {
$rounded = round($value, $this->getDecimals($field));
if ($rounded == 0) {
$rounded += 0; // To fix negative zero
}
return $rounded;
}


/**
* Set rounding matrix
* @param array $matrix Rounding matrix
Expand Down Expand Up @@ -194,6 +212,26 @@ public function setCurrency(string $currencyCode): self {
}


/**
* Get VAT accounting currency code
* @return string|null VAT accounting currency code or NULL if same as document's
*/
public function getVatCurrency(): ?string {
return $this->vatCurrency;
}


/**
* Set VAT accounting currency code
* @param string|null $currencyCode VAT accounting currency code or NULL if same as document's
* @return self Invoice instance
*/
public function setVatCurrency(?string $currencyCode): self {
$this->vatCurrency = $currencyCode;
return $this;
}


/**
* Get invoice issue date
* @return DateTime|null Invoice issue date
Expand Down Expand Up @@ -254,22 +292,72 @@ public function setTaxPointDate(?DateTime $taxPointDate): self {
}


/**
* Get invoice notes
* @return string[] Invoice notes
*/
public function getNotes(): array {
return $this->notes;
}


/**
* Add invoice note
* @param string $note Invoice note
* @return self Invoice instance
*/
public function addNote(string $note): self {
$this->notes[] = $note;
return $this;
}


/**
* Remove invoice note
* @param int $index Invoice note index
* @return self Invoice instance
* @throws OutOfBoundsException if invoice note index is out of bounds
*/
public function removeNote(int $index): self {
if ($index < 0 || $index >= count($this->notes)) {
throw new OutOfBoundsException('Could not find invoice note by index');
}
array_splice($this->notes, $index, 1);
return $this;
}


/**
* Clear all invoice notes
* @return self Invoice instance
*/
public function clearNotes(): self {
$this->notes = [];
return $this;
}


/**
* Get invoice note
* @return string|null Invoice note
* @deprecated 0.2.1
* @see Invoice::getNotes()
*/
public function getNote(): ?string {
return $this->note;
return $this->notes[0] ?? null;
}


/**
* Set invoice note
* @param string|null $note Invoice note
* @return self Invoice instance
* @deprecated 0.2.1
* @see Invoice::addNote()
*/
public function setNote(?string $note): self {
$this->note = $note;
// @phan-suppress-next-line PhanPartialTypeMismatchProperty
$this->notes = ($note === null) ? [] : [$note];
return $this;
}

Expand Down Expand Up @@ -414,6 +502,26 @@ public function setRoundingAmount(float $roundingAmount): self {
}


/**
* Get total VAT amount in VAT accounting currency
* @return float|null Total amount in accounting currency
*/
public function getCustomVatAmount(): ?float {
return $this->customVatAmount;
}


/**
* Set total VAT amount in VAT accounting currency
* @param float|null $customVatAmount Total amount in accounting currency
* @return self Invoice instance
*/
public function setCustomVatAmount(?float $customVatAmount): self {
$this->customVatAmount = $customVatAmount;
return $this;
}


/**
* Get seller
* @return Party|null Seller instance
Expand Down
41 changes: 27 additions & 14 deletions src/Models/InvoiceTotals.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@
use Einvoicing\Invoice;
use Einvoicing\Traits\VatTrait;
use function array_values;
use function round;

class InvoiceTotals {
/**
* Totals currency code
* Invoice currency code
* @var string
*/
public $currency;

/**
* VAT accounting currency code
* @var string|null
*/
public $vatCurrency = null;

/**
* Sum of all invoice line net amounts
* @var float
Expand Down Expand Up @@ -61,6 +66,12 @@ class InvoiceTotals {
*/
public $roundingAmount = 0;

/**
* Total VAT amount in accounting currency
* @var float|null
*/
public $customVatAmount = null;

/**
* Amount due for payment
* @var float
Expand All @@ -83,8 +94,9 @@ static public function fromInvoice(Invoice $inv, bool $round=true): InvoiceTotal
$totals = new self();
$vatMap = [];

// Set currency code
// Set currency codes
$totals->currency = $inv->getCurrency();
$totals->vatCurrency = $inv->getVatCurrency();

// Process all invoice lines
foreach ($inv->getLines() as $line) {
Expand Down Expand Up @@ -116,25 +128,26 @@ static public function fromInvoice(Invoice $inv, bool $round=true): InvoiceTotal
$totals->taxInclusiveAmount = $totals->taxExclusiveAmount + $totals->vatAmount;
$totals->paidAmount = $inv->getPaidAmount();
$totals->roundingAmount = $inv->getRoundingAmount();
$totals->customVatAmount = $inv->getCustomVatAmount();
$totals->payableAmount = $totals->taxInclusiveAmount - $totals->paidAmount + $totals->roundingAmount;

// Attach VAT breakdown
$totals->vatBreakdown = array_values($vatMap);

// Round values
if ($round) {
$totals->netAmount = round($totals->netAmount, $inv->getDecimals('invoice/netAmount'));
$totals->allowancesAmount = round($totals->allowancesAmount, $inv->getDecimals('invoice/allowancesChargesAmount'));
$totals->chargesAmount = round($totals->chargesAmount, $inv->getDecimals('invoice/allowancesChargesAmount'));
$totals->vatAmount = round($totals->vatAmount, $inv->getDecimals('invoice/vatAmount'));
$totals->taxExclusiveAmount = round($totals->taxExclusiveAmount, $inv->getDecimals('invoice/taxExclusiveAmount'));
$totals->taxInclusiveAmount = round($totals->taxInclusiveAmount, $inv->getDecimals('invoice/taxInclusiveAmount'));
$totals->paidAmount = round($totals->paidAmount, $inv->getDecimals('invoice/paidAmount'));
$totals->roundingAmount = round($totals->roundingAmount, $inv->getDecimals('invoice/roundingAmount'));
$totals->payableAmount = round($totals->payableAmount, $inv->getDecimals('invoice/payableAmount'));
$totals->netAmount = $inv->round($totals->netAmount, 'invoice/netAmount');
$totals->allowancesAmount = $inv->round($totals->allowancesAmount, 'invoice/allowancesChargesAmount');
$totals->chargesAmount = $inv->round($totals->chargesAmount, 'invoice/allowancesChargesAmount');
$totals->vatAmount = $inv->round($totals->vatAmount, 'invoice/vatAmount');
$totals->taxExclusiveAmount = $inv->round($totals->taxExclusiveAmount, 'invoice/taxExclusiveAmount');
$totals->taxInclusiveAmount = $inv->round($totals->taxInclusiveAmount, 'invoice/taxInclusiveAmount');
$totals->paidAmount = $inv->round($totals->paidAmount, 'invoice/paidAmount');
$totals->roundingAmount = $inv->round($totals->roundingAmount, 'invoice/roundingAmount');
$totals->payableAmount = $inv->round($totals->payableAmount, 'invoice/payableAmount');
foreach ($totals->vatBreakdown as $item) {
$item->taxableAmount = round($item->taxableAmount, $inv->getDecimals('invoice/allowancesChargesAmount'));
$item->taxAmount = round($item->taxAmount, $inv->getDecimals('invoice/taxAmount'));
$item->taxableAmount = $inv->round($item->taxableAmount, 'invoice/allowancesChargesAmount');
$item->taxAmount = $inv->round($item->taxAmount, 'invoice/taxAmount');
}
}

Expand Down
Loading

0 comments on commit c4a1d1b

Please sign in to comment.