Compliance

Fakturownia Pro for Magento 2 implements the full set of Polish tax compliance requirements: NIP collection at checkout, EU OSS VAT annotation, Split Payment (MPP), GTU codes per product, Reverse Charge for intra-community B2B, and JPK_V7 metadata. All features are implemented via Magento's native architecture — custom attributes, extension attributes, observer hooks, and plugin interceptors — not rewrites.

All compliance settings are at Stores → Configuration → Plugkit → Fakturownia Pro → Compliance.

Compliance configuration

NIP Field at Checkout

What It Does

Adds a NIP (Tax Identification Number) field to the Magento 2 checkout billing address form. This allows B2B customers to enter their Polish or EU company VAT number. The NIP value is:

  • Rendered as a standard checkout field via the UI component system (checkout_index_index.xml layout + JSLayout merge)
  • Validated client-side with a JS mixin using Magento's mage/validation library
  • Validated server-side via a QuotePlugin afterPlugin on \Magento\Quote\Api\CartManagementInterface
  • Stored as a quote extension attribute (fakturownia_nip) and transferred to the order on placement
  • Saved as a custom customer_address attribute for returning customers
  • Automatically included on generated invoices as the buyer's tax_no field

NIP Checksum Validation

The NIP is validated by a weighted checksum algorithm before the quote is saved. Each digit is multiplied by a positional weight, the total is divided by 11, and the remainder must equal the check digit (position 10). If validation fails, the checkout form shows an inline error and the order cannot be placed.

The NIP format accepted: 10 digits, optionally prefixed with PL. Dashes and spaces are stripped before validation.

Example — NIP 1234563218:

Weights: 6, 5, 7, 2, 3, 4, 5, 6, 7
Digits:  1, 2, 3, 4, 5, 6, 3, 2, 1
Sum: 6*1 + 5*2 + 7*3 + 2*4 + 3*5 + 4*6 + 5*3 + 6*2 + 7*1 = 6+10+21+8+15+24+15+12+7 = 118
Remainder: 118 mod 11 = 8
Check digit (position 10): 8 ✓

Configuration

| Option | Description | Default | |---|---|---| | Enable NIP field | Show NIP field at checkout | Off | | Required for companies | Mandatory when Company field is filled | Off | | VIES validation | Cross-check EU VAT numbers via VIES API | Off | | Save to customer address | Persist NIP for returning customers | On | | Field label | Checkout field label | NIP / Tax ID | | Field position | before_company or after_phone | after_company |

VIES Validation

When VIES validation is enabled, the server-side plugin calls the EU VIES SOAP service to validate the VAT number. VIES is an EU Commission service and has occasional downtime. If VIES is unreachable:

  • The module logs a warning to var/log/fakturownia_pro.log
  • The order is allowed to proceed (fail-open by default)
  • The invoice is generated without the vies_validated: true flag

To switch to fail-closed (block checkout when VIES is unreachable), set vies_fallback_allow: false in app/etc/env.php:

'plugkit_fakturownia' => [
    'vies_fallback_allow' => false
]

Custom Attribute Persistence

The NIP is stored as a customer_address entity attribute declared in Setup/Patch/Data/AddNipCustomerAttribute.php as a declarative data patch. This ensures:

  • NIP is available in the REST API: GET /V1/customers/:id includes the address attribute
  • NIP is visible in the Magento admin on the customer address page
  • Third-party OMS integrations that use the REST API can read and write NIP values

EU One Stop Shop (OSS)

Overview

For stores registered under the EU OSS scheme, invoices must reflect the buyer's country VAT rate and include an OSS annotation. Fakturownia Pro reads the destination tax rates calculated by Magento and annotates the invoice accordingly.

How OSS Detection Works

The module hooks into Magento's tax pipeline via an observer on tax_quote_address_collect_totals_after. After the standard tax totals are collected:

  1. The observer checks whether OSS mode is enabled and whether the order is B2C (no valid EU VAT number).
  2. If both conditions are met, it reads the applied AppliedTax objects from the quote address.
  3. Each applied tax is mapped to a country code via the tax rule configuration.
  4. The resulting per-country tax breakdown is passed to the Fakturownia API payload via the taxes array.

The module does not replace Magento's tax calculation. You still need a compatible tax extension (e.g. Amasty EU VAT, Taxify, or the built-in Magento VAT ID validation with manually configured destination tax rules) to calculate the correct destination country rates. Fakturownia Pro only reads the output.

Configuration

| Option | Description | |---|---| | Enable OSS | Activate OSS invoice annotation | | Store country | Physical store location, used to determine domestic vs OSS supply (default: Poland) | | Apply to B2C only | Exclude validated EU VAT customers — intra-community supply rules apply instead | | Fallback VAT rate | Used when no destination country rate is found (default: Polish VAT rate) |

OSS and Reverse Charge Interaction

A customer with a valid EU VAT number is subject to Reverse Charge (intra-community supply), not OSS. The two features are mutually exclusive:

  • Valid EU VAT + VIES validated → Reverse Charge (0% VAT, RC annotation)
  • No EU VAT or VIES not validated → OSS (buyer's country rate, OSS annotation)
  • Domestic Polish customer → Standard Polish VAT

If both OSS and Reverse Charge are enabled, the Reverse Charge check runs first and takes precedence.

Split Payment (Mechanizm Podzielonej Płatności — MPP)

Legal Requirement

Polish law mandates that VAT invoices above PLN 15,000 gross for goods/services listed in Annex 15 of the Polish VAT Act include the annotation mechanizm podzielonej płatności and the split_payment: true flag in the document. This allows — and in many cases requires — the buyer to use split payment when settling the invoice.

Software licences are not in Annex 15. However, some B2B buyers request the MPP annotation anyway for their internal accounting systems. The Apply to all invoices option supports this.

Magento Implementation

The SplitPaymentPlugin is an afterPlugin on DocumentGenerationService::buildPayload():

// Plugin/SplitPaymentPlugin.php
public function afterBuildPayload(
    DocumentGenerationService $subject,
    array $payload,
    OrderInterface $order
): array {
    $totalPln = $this->currencyConverter->convert(
        $order->getGrandTotal(),
        $order->getOrderCurrencyCode(),
        'PLN'
    );
    if ($totalPln >= $this->config->getMppThreshold()) {
        $payload['split_payment'] = true;
        $payload['description'] = ($payload['description'] ?? '') . "\nmechanizm podzielonej płatności";
    }
    return $payload;
}

Currency conversion uses \Magento\Directory\Model\Currency against the stored exchange rates in the directory_currency_rate table. Keep exchange rates current (Stores → Currency → Import) to ensure accurate PLN conversion for foreign-currency orders. Stale rates cause the threshold check to fail silently.

Configuration

| Option | Default | Description | |---|---|---| | Enable MPP | Off | Activate automatic Split Payment detection | | Threshold (PLN) | 15000 | Gross order amount threshold | | Apply to all invoices | Off | Force MPP annotation on every invoice regardless of amount |

GTU Codes

What GTU Codes Are

GTU (Grupy Towarów i Usług) codes are mandatory Polish JPK_V7 transaction classifiers. They identify categories of goods and services that require special VAT reporting. Selling software licences requires GTU_12.

Full GTU Reference

| Code | Category | Software Licence Applies? | |---|---|---| | GTU_01 | Alcoholic beverages | No | | GTU_02 | Tobacco products, cigarettes | No | | GTU_03 | Heating oil, engine fuel | No | | GTU_04 | Electronics in customs tariff 85 (hardware) | Possibly (physical hardware) | | GTU_05 | Greenhouse gas waste | No | | GTU_06 | Electronic devices with reverse charge | No | | GTU_07 | Vehicles and parts | No | | GTU_08 | Precious metals, alloys | No | | GTU_09 | Medicines, medical devices | No | | GTU_10 | Buildings, structures (real estate) | No | | GTU_11 | Natural gas, electricity, heat | No | | GTU_12 | Computer equipment, electronic devices, software licences, IT services | Yes | | GTU_13 | Transport and warehouse services | No |

For software-only stores, assign GTU_12 to all products. For mixed stores (hardware + software), assign GTU_12 to software products and GTU_04 to physical hardware.

Confirm the correct code with your accountant — applying the wrong GTU code is a JPK reporting error.

Assigning GTU Codes to Products

GTU codes are stored as a custom multi-select product attribute fakturownia_gtu_codes, declared in Setup/Patch/Data/AddGtuProductAttribute.php.

To assign:

  1. Navigate to Catalog → Products → Edit.
  2. In the Fakturownia Pro attribute group, select the applicable GTU code(s).
  3. Save the product.

For configurable products: assign GTU codes to the parent configurable product. The module reads GTU codes from the parent when processing order line items that reference simple child products. If the parent has no GTU code set, the module falls back to the simple product's GTU code.

Bulk GTU Assignment via CLI

For stores with many products, assign GTU codes in bulk using the Magento ORM:

php bin/magento eval "
\$collection = \$objectManager->get('\Magento\Catalog\Model\ResourceModel\Product\Collection');
\$collection->addAttributeToFilter('type_id', 'simple');
foreach (\$collection as \$product) {
    \$product->setFakturowniaGtuCodes(['GTU_12']);
    \$product->save();
}
echo count(\$collection) . ' products updated';
"

For production use, implement this as a data patch in your custom module rather than a one-off CLI command.

GTU in API Payload

When an invoice is generated, the module iterates over order line items, reads fakturownia_gtu_codes from each product, and includes them per line:

{
  "positions": [
    {
      "name": "Software Licence — Enterprise Plan",
      "quantity": 1,
      "total_price_gross": 1230.00,
      "tax": 23,
      "gtu_codes": ["GTU_12"]
    }
  ]
}

Shipping lines, gift wrapping, and discount adjustments do not get GTU codes unless you assign them to the virtual products representing those items.

Reverse Charge

When It Applies

Reverse Charge applies to intra-community B2B supplies: the buyer is a VAT-registered business in another EU member state. For Magento stores selling software licences to EU businesses, this is common.

When Reverse Charge applies:

  • VAT rate on the invoice: 0%
  • Invoice annotation: Odwrotne obciążenie / Reverse charge
  • Buyer's VAT number included on the invoice
  • No Polish VAT reported on this transaction (the buyer self-accounts)

Magento Implementation

The module integrates with Magento's native VAT validation system (\Magento\Customer\Model\Vat). The flow:

  1. Customer enters an EU VAT number at checkout.
  2. Magento's VatValidator fires (if enabled) and verifies the number against VIES.
  3. Magento's automatic customer group assignment moves the customer to a tax-exempt group (e.g. B2B_EU_VAT).
  4. The TaxClassObserver in the module detects the tax-exempt group.
  5. During invoice generation, the ReverseChargePlugin sets vat_rate: 0 and adds the RC annotation.

Dependency on Magento VAT Validation

Enable Magento's built-in VAT validation at:

Stores → Configuration → Customers → Customer Configuration → Create New Account Options → Enable Automatic Assignment to Customer Group → Yes

Configure tax rules so that the B2B EU group has 0% VAT applied. Without this, Magento will still charge standard VAT even when the customer provides a valid EU VAT number.

Configuration

| Option | Description | |---|---| | Enable Reverse Charge | Activate RC logic | | Applicable customer groups | Groups that qualify for RC (typically your B2B EU group) | | RC note text | Default: Odwrotne obciążenie / Reverse charge | | Require VIES validation | Only apply RC to orders where VIES returned valid=true |

RC and VIES Downtime

If VIES is unavailable during checkout and Require VIES validation is enabled, the customer is not assigned to the B2B EU group, and the RC annotation is not applied — the invoice is generated with standard Polish VAT. This protects you from incorrectly issuing 0% VAT invoices to unverified buyers.

JPK_V7 Compatibility

All documents generated by the module include the metadata required for JPK_V7:

| JPK_V7 Field | Source | |---|---| | GTU codes per line | fakturownia_gtu_codes product attribute | | MPP flag | split_payment from SplitPaymentPlugin | | RC flag | vat_rate: 0 + RC annotation from ReverseChargePlugin | | Document type marker | FV (Faktura VAT), RO (receipt/paragon) | | OSS country VAT | taxes array from OSS observer |

Fakturownia generates compliant JPK_V7 XML export files from these documents. The export is done directly from the Fakturownia portal — no additional Magento module is required.

Custom Attribute Architecture

All compliance data flows through Magento's extension attribute system:

| Extension Attribute | Entity | Type | Purpose | |---|---|---|---| | fakturownia_nip | Order, OrderAddress, Quote | string | Buyer NIP / VAT number | | fakturownia_invoice_id | Order | int | Fakturownia document ID | | fakturownia_invoice_number | Order | string | Human-readable invoice number | | fakturownia_invoice_status | Order | string | pending, completed, failed | | fakturownia_gtu_codes | Product (EAV attribute) | multiselect | GTU codes for this product |

All extension attributes are declared in extension_attributes.xml and implemented via ExtensionAttributeInterface, ensuring compatibility with the Magento REST API, GraphQL API, and third-party OMS integrations. Any system that reads orders via GET /V1/orders/:id receives the compliance data in the extension_attributes block.

Compliance Checklist

Before going live, verify:

  • [ ] NIP field enabled and visible at checkout (test with a Polish NIP: 5862296909)
  • [ ] NIP is present on generated invoices as tax_no
  • [ ] GTU_12 assigned to all software product SKUs
  • [ ] GTU codes appear on invoice line items (check Fakturownia document view)
  • [ ] MPP enabled if you sell to Polish businesses with order totals above PLN 15,000
  • [ ] OSS enabled and store country set to Poland (if OSS registered)
  • [ ] Reverse Charge enabled, Magento VAT validation enabled, B2B EU customer group configured with 0% tax rule
  • [ ] Exchange rates imported and current (Stores → Currency → Import)
  • [ ] Test a B2B EU order: expect 0% VAT and RC annotation on invoice
  • [ ] Test a domestic B2C order: expect standard 23% VAT, no RC annotation

Next Steps

Edit this page on GitHub
Was this page helpful?

Documentation associée

Ce sujet est également disponible pour d'autres plateformes :