mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-09 12:42:36 +01:00
Peppol calculations
This commit is contained in:
parent
0e10ec9d49
commit
0d4a1b9043
@ -11,7 +11,6 @@
|
||||
|
||||
namespace App\Jobs\EDocument;
|
||||
|
||||
use App\Services\EDocument\Standards\RoEInvoice;
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Credit;
|
||||
@ -23,10 +22,12 @@ use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use App\Services\EDocument\Standards\Peppol;
|
||||
use horstoeko\zugferd\ZugferdDocumentBuilder;
|
||||
use App\Services\EDocument\Standards\FatturaPA;
|
||||
use App\Services\EDocument\Standards\RoEInvoice;
|
||||
use App\Services\EDocument\Standards\OrderXDocument;
|
||||
use App\Services\EDocument\Standards\FacturaEInvoice;
|
||||
use App\Services\EDocument\Standards\FatturaPA;
|
||||
use App\Services\EDocument\Standards\ZugferdEDokument;
|
||||
|
||||
class CreateEDocument implements ShouldQueue
|
||||
@ -68,6 +69,8 @@ class CreateEDocument implements ShouldQueue
|
||||
|
||||
if ($this->document instanceof Invoice) {
|
||||
switch ($e_document_type) {
|
||||
case "PEPPOL":
|
||||
return (new Peppol($this->document))->toXml();
|
||||
case "FACT1":
|
||||
return (new RoEInvoice($this->document))->generateXml();
|
||||
case "FatturaPA":
|
||||
|
@ -95,6 +95,48 @@ class Storecove {
|
||||
// parseStrategy: ubl
|
||||
// }
|
||||
*/
|
||||
public function sendJsonDocument($document)
|
||||
{
|
||||
|
||||
|
||||
$payload = [
|
||||
"legalEntityId" => 290868,
|
||||
"idempotencyGuid" => \Illuminate\Support\Str::uuid(),
|
||||
"routing" => [
|
||||
"eIdentifiers" => [],
|
||||
"emails" => ["david@invoiceninja.com"]
|
||||
],
|
||||
// "document" => [
|
||||
// 'documentType' => 'invoice',
|
||||
// "rawDocumentData" => [
|
||||
// "document" => base64_encode($document),
|
||||
// "parse" => true,
|
||||
// "parseStrategy" => "ubl",
|
||||
// ],
|
||||
// ],
|
||||
"document"=> [
|
||||
"documentType" => "invoice",
|
||||
"invoice" => $document,
|
||||
],
|
||||
];
|
||||
|
||||
$uri = "document_submissions";
|
||||
|
||||
nlog($payload);
|
||||
|
||||
$r = $this->httpClient($uri, (HttpVerb::POST)->value, $payload, $this->getHeaders());
|
||||
|
||||
nlog($r->body());
|
||||
nlog($r->json());
|
||||
|
||||
if($r->successful()) {
|
||||
return $r->json()['guid'];
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public function sendDocument($document)
|
||||
{
|
||||
|
||||
@ -256,8 +298,6 @@ class Storecove {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function addIdentifier(int $legal_entity_id, string $identifier, string $scheme)
|
||||
{
|
||||
$uri = "legal_entities/{$legal_entity_id}/peppol_identifiers";
|
||||
@ -278,7 +318,6 @@ class Storecove {
|
||||
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private function getHeaders(array $headers = [])
|
||||
{
|
||||
@ -300,5 +339,4 @@ class Storecove {
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -151,14 +151,36 @@ class Peppol extends AbstractService
|
||||
|
||||
private InvoiceSum | InvoiceSumInclusive $calc;
|
||||
|
||||
private \InvoiceNinja\EInvoice\Models\Peppol\Invoice $p_invoice;
|
||||
/**
|
||||
* @param Invoice $invoice
|
||||
*/
|
||||
public function __construct(public Invoice $invoice, public ?\InvoiceNinja\EInvoice\Models\Peppol\Invoice $p_invoice = null)
|
||||
public function __construct(public Invoice $invoice)
|
||||
{
|
||||
$this->p_invoice = $p_invoice ?? new \InvoiceNinja\EInvoice\Models\Peppol\Invoice();
|
||||
$this->company = $invoice->company;
|
||||
$this->calc = $this->invoice->calc();
|
||||
$this->setInvoice();
|
||||
}
|
||||
|
||||
private function setInvoice(): self
|
||||
{
|
||||
|
||||
|
||||
if($this->invoice->e_invoice){
|
||||
|
||||
|
||||
$e = new EInvoice();
|
||||
$this->p_invoice = $e->decode('Peppol', json_encode($this->invoice->e_invoice->Invoice), 'json');
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
$this->p_invoice = new \InvoiceNinja\EInvoice\Models\Peppol\Invoice();
|
||||
|
||||
$this->setInvoiceDefaults();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInvoice(): \InvoiceNinja\EInvoice\Models\Peppol\Invoice
|
||||
@ -171,7 +193,31 @@ class Peppol extends AbstractService
|
||||
public function toXml(): string
|
||||
{
|
||||
$e = new EInvoice();
|
||||
return $e->encode($this->p_invoice, 'xml');
|
||||
$xml = $e->encode($this->p_invoice, 'xml');
|
||||
|
||||
$prefix = '<?xml version="1.0" encoding="utf-8"?>
|
||||
<Invoice
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">';
|
||||
|
||||
return str_ireplace(['\n','<?xml version="1.0"?>'], ['', $prefix], $xml);
|
||||
|
||||
}
|
||||
|
||||
public function toJson(): string
|
||||
{
|
||||
$e = new EInvoice();
|
||||
$json = $e->encode($this->p_invoice, 'json');
|
||||
|
||||
return $json;
|
||||
// $prefixes = str_ireplace(["cac:","cbc:"], "", $json);
|
||||
// return str_ireplace(["InvoiceLine", "PostalAddress", "PartyName"], ["invoiceLines","address", "companyName"], $prefixes);
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return json_decode($this->toJson(), true);
|
||||
}
|
||||
|
||||
public function run()
|
||||
@ -192,17 +238,6 @@ class Peppol extends AbstractService
|
||||
|
||||
}
|
||||
|
||||
// private function getPaymentMeans(): PaymentMeans
|
||||
// {
|
||||
// $payeeFinancialAccount = new PayeeFinancialAccount()
|
||||
// $payeeFinancialAccount->
|
||||
|
||||
// $ppm = new PaymentMeans();
|
||||
// $ppm->PayeeFinancialAccount = $payeeFinancialAccount;
|
||||
|
||||
// return $ppm;
|
||||
// }
|
||||
|
||||
private function getLegalMonetaryTotal(): LegalMonetaryTotal
|
||||
{
|
||||
$taxable = $this->getTaxable();
|
||||
@ -509,7 +544,7 @@ class Peppol extends AbstractService
|
||||
$tax_amount = new TaxAmount();
|
||||
$tax_amount->currencyID = $this->invoice->client->currency()->code;
|
||||
|
||||
$tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($item->tax_rate3, $item->line_total) : $this->calcAmountLineTax($item->tax_rate3, $item->line_total);
|
||||
$tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($item->tax_rate3, $item->line_total) : $this->calcAmountLineTax($item->tax_rate3, $item->line_total);
|
||||
|
||||
$tax_subtotal = new TaxSubtotal();
|
||||
$tax_subtotal->TaxAmount = $tax_amount;
|
||||
@ -724,22 +759,18 @@ $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiv
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function setPaymentMeans(): self
|
||||
private function setPaymentMeans(bool $required = false): self
|
||||
{
|
||||
$paymentMeans = new PaymentMeans();
|
||||
|
||||
if($this->p_invoice->PaymentMeans)
|
||||
return $this;
|
||||
elseif(!isset($this->p_invoice->PaymentMeans) && $paymentMeans = $this->getSetting('Invoice.PaymentMeans')){
|
||||
$this->p_invoice->PaymentMeans = is_array($paymentMeans) ? $paymentMeans : [$paymentMeans];
|
||||
return $this;
|
||||
}
|
||||
|
||||
// = $this->getPaymentMeans();
|
||||
|
||||
// $payeeFinancialAccount = (new PayeeFinancialAccount())
|
||||
// ->setBankId($company->settings->custom_value1)
|
||||
// ->setBankName($company->settings->custom_value2);
|
||||
|
||||
// $paymentMeans = (new PaymentMeans())
|
||||
// ->setPaymentMeansCode($invoice->custom_value1)
|
||||
// ->setPayeeFinancialAccount($payeeFinancialAccount);
|
||||
// $ubl_invoice->setPaymentMeans($paymentMeans);
|
||||
|
||||
$this->p_invoice->PaymentMeans = $paymentMeans;
|
||||
if($required)
|
||||
throw new \Exception('e-invoice generation halted:: Payment Means required');
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -748,10 +779,9 @@ $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiv
|
||||
{
|
||||
// accountingsupplierparty.party.contact MUST be set - Name / Telephone / Electronic Mail
|
||||
// this is forced by default.
|
||||
|
||||
$this->setPaymentMeans(true);
|
||||
|
||||
// ONE payment means MUST be set
|
||||
|
||||
//
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -778,7 +808,7 @@ $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiv
|
||||
private function ES(): self
|
||||
{
|
||||
|
||||
// For B2B, provide an ES:DIRE routing identifier and an ES:VAT tax identifier.
|
||||
// For B2B, provide an ES:DIRE routing identifier and an ES:VAT tax identifier.
|
||||
// both sender and receiver must be an ES company;
|
||||
// you must have a "credit_transfer" PaymentMean;
|
||||
// the "dueDate" property is mandatory.
|
||||
|
@ -11,18 +11,25 @@
|
||||
|
||||
namespace Tests\Integration\Einvoice\Storecove;
|
||||
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\Models\Client;
|
||||
use Tests\TestCase;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use Tests\MockAccountData;
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Services\EDocument\Standards\Peppol;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\PaymentMeans;
|
||||
|
||||
class StorecoveTest extends TestCase
|
||||
{
|
||||
use MockAccountData;
|
||||
use DatabaseTransactions;
|
||||
|
||||
private string $routing_id = '';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
@ -88,7 +95,7 @@ class StorecoveTest extends TestCase
|
||||
// nlog($r);
|
||||
|
||||
// }
|
||||
|
||||
/*
|
||||
public function testGetLegalEntity()
|
||||
{
|
||||
|
||||
@ -348,7 +355,7 @@ $x = '<?xml version="1.0" encoding="utf-8"?>
|
||||
$sc->sendDocument($x);
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
public function testCreateCHClient()
|
||||
{
|
||||
|
||||
@ -390,4 +397,140 @@ $x = '<?xml version="1.0" encoding="utf-8"?>
|
||||
$this->assertInstanceOf(\App\Models\Client::class, $c);
|
||||
|
||||
}
|
||||
|
||||
private function createDEData()
|
||||
{
|
||||
|
||||
$this->routing_id = '290868';
|
||||
|
||||
$settings = CompanySettings::defaults();
|
||||
$settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png';
|
||||
$settings->website = 'www.invoiceninja.de';
|
||||
$settings->address1 = 'Musterstraße 1';
|
||||
$settings->address2 = 'Etage 2, Büro 3';
|
||||
$settings->city = 'Berlin';
|
||||
$settings->state = 'Berlin';
|
||||
$settings->postal_code = '10115';
|
||||
$settings->phone = '030 1234567';
|
||||
$settings->email = $this->faker->unique()->safeEmail();
|
||||
$settings->country_id = '276'; // Germany's ISO country code
|
||||
$settings->vat_number = 'DE123456789';
|
||||
$settings->id_number = 'HRB 12345';
|
||||
$settings->use_credits_payment = 'always';
|
||||
$settings->timezone_id = '1'; // CET (Central European Time)
|
||||
$settings->entity_send_time = 0;
|
||||
$settings->e_invoice_type = 'PEPPOL';
|
||||
$settings->currency_id = '3';
|
||||
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $this->account->id,
|
||||
'settings' => $settings,
|
||||
]);
|
||||
|
||||
$this->user->companies()->attach($company->id, [
|
||||
'account_id' => $this->account->id,
|
||||
'is_owner' => true,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'permissions' => '',
|
||||
'notifications' => CompanySettings::notificationAdminDefaults(),
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
Client::unguard();
|
||||
|
||||
$c =
|
||||
Client::create([
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'name' => 'Beispiel Firma GmbH',
|
||||
'website' => 'https://www.beispiel-firma.de',
|
||||
'private_notes' => 'Dies sind private Notizen zum Testkunden.',
|
||||
'balance' => 0,
|
||||
'paid_to_date' => 0,
|
||||
'vat_number' => 'DE654321987',
|
||||
'id_number' => 'HRB 12345', // Typical format for German company registration numbers
|
||||
'custom_value1' => '2024-07-22 10:00:00',
|
||||
'custom_value2' => 'blau',
|
||||
'custom_value3' => 'musterwort',
|
||||
'custom_value4' => 'test@example.com',
|
||||
'address1' => 'Musterstraße 123',
|
||||
'address2' => '2. Etage, Büro 45',
|
||||
'city' => 'München',
|
||||
'state' => 'Bayern',
|
||||
'postal_code' => '80331',
|
||||
'country_id' => '276', // Germany
|
||||
'shipping_address1' => 'Musterstraße 123',
|
||||
'shipping_address2' => '2. Etage, Büro 45',
|
||||
'shipping_city' => 'München',
|
||||
'shipping_state' => 'Bayern',
|
||||
'shipping_postal_code' => '80331',
|
||||
'shipping_country_id' => '276', // Germany
|
||||
'settings' => ClientSettings::Defaults(),
|
||||
'client_hash' => \Illuminate\Support\Str::random(32),
|
||||
'routing_id' => '',
|
||||
]);
|
||||
|
||||
|
||||
$item = new InvoiceItem();
|
||||
$item->product_key = "Product Key";
|
||||
$item->notes = "Product Description";
|
||||
$item->cost = 10;
|
||||
$item->quantity = 10;
|
||||
$item->tax_rate1 = 19;
|
||||
$item->tax_name1 = 'mwst';
|
||||
|
||||
$invoice = Invoice::factory()->create([
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'client_id' => $c->id,
|
||||
'discount' => 0,
|
||||
'uses_inclusive_taxes' => false,
|
||||
'status_id' => 1,
|
||||
'tax_rate1' => 0,
|
||||
'tax_name1' => '',
|
||||
'tax_rate2' => 0,
|
||||
'tax_rate3' => 0,
|
||||
'tax_name2' => '',
|
||||
'tax_name3' => '',
|
||||
'line_items' => [$item],
|
||||
'number' => 'DE-'.rand(1000, 100000),
|
||||
'date' => now()->format('Y-m-d')
|
||||
]);
|
||||
|
||||
$invoice = $invoice->calc()->getInvoice();
|
||||
$invoice->service()->markSent()->save();
|
||||
|
||||
|
||||
return $invoice;
|
||||
|
||||
}
|
||||
|
||||
public function testDeRules()
|
||||
{
|
||||
$invoice = $this->createDEData();
|
||||
|
||||
$e_invoice = new \InvoiceNinja\EInvoice\Models\Peppol\Invoice();
|
||||
|
||||
$stub = json_decode('{"Invoice":{"Note":"Nooo","PaymentMeans":[{"ID":{"value":"afdasfasdfasdfas"},"PayeeFinancialAccount":{"Name":"PFA-NAME","ID":{"value":"DE89370400440532013000"},"AliasName":"PFA-Alias","AccountTypeCode":{"value":"CHECKING"},"AccountFormatCode":{"value":"IBAN"},"CurrencyCode":{"value":"EUR"},"FinancialInstitutionBranch":{"ID":{"value":"DEUTDEMMXXX"},"Name":"Deutsche Bank"}}}]}}');
|
||||
foreach($stub as $key => $value)
|
||||
$e_invoice->{$key} = $value;
|
||||
|
||||
$invoice->e_invoice = $e_invoice;
|
||||
$invoice->save();
|
||||
|
||||
$this->assertInstanceOf(Invoice::class, $invoice);
|
||||
$this->assertInstanceof(\InvoiceNinja\EInvoice\Models\Peppol\Invoice::class, $e_invoice);
|
||||
|
||||
$p = new Peppol($invoice);
|
||||
|
||||
$p->run();
|
||||
$xml = $p->toXml();
|
||||
nlog($xml);
|
||||
$sc = new \App\Services\EDocument\Gateway\Storecove\Storecove();
|
||||
$sc->sendDocument($xml);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user