1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 20:22:42 +01:00

Working on hydrating XML => UBL => PDF

This commit is contained in:
David Bomba 2024-10-15 14:22:47 +11:00
parent 7402f7c110
commit b58be6fdd6
12 changed files with 1779 additions and 1919 deletions

View File

@ -37,9 +37,9 @@ class CreateAccountRequest extends Request
public function rules()
{
if (Ninja::isHosted()) {
$email_rules = ['bail', 'required', 'email:rfc,dns', new NewUniqueUserRule(), new BlackListRule(), new EmailBlackListRule()];
$email_rules = ['bail', 'required', 'max:255', 'email:rfc,dns', new NewUniqueUserRule(), new BlackListRule(), new EmailBlackListRule()];
} else {
$email_rules = ['bail', 'required', 'email:rfc,dns', new NewUniqueUserRule()];
$email_rules = ['bail', 'required', 'max:255', 'email:rfc,dns', new NewUniqueUserRule()];
}
return [

View File

@ -22,7 +22,7 @@ class SendEmailRequest extends Request
{
use MakesHash;
private string $entity_plural = '';
private string $entity_plural = 'invoices';
private string $error_message = '';
public array $templates = [
@ -92,9 +92,8 @@ class SendEmailRequest extends Request
$input['entity_id'] = $this->decodePrimaryKey($input['entity_id']);
}
$this->entity_plural = Str::plural($input['entity']) ?? '';
if (isset($input['entity']) && in_array($input['entity'], ['invoice','quote','credit','recurring_invoice','purchase_order','payment'])) {
$this->entity_plural = Str::plural($input['entity']) ?? '';
$input['entity'] = "App\Models\\".ucfirst(Str::camel($input['entity']));
}
@ -106,13 +105,15 @@ class SendEmailRequest extends Request
})->slice(0, 4)->toArray();
}
$this->replace($input);
}
public function message()
public function messages()
{
return [
'template' => 'Invalid template.',
'template.in' => 'Template :input is anot a valid template.',
'entity.in' => 'Entity :input is not a valid entity.'
];
}
@ -134,8 +135,7 @@ class SendEmailRequest extends Request
}
/*Make sure we have all the require ingredients to send a template*/
if (isset($input['entity']) && array_key_exists('entity_id', $input) && is_string($input['entity']) && $input['entity_id']) {
if (isset($input['entity']) && array_key_exists('entity_id', $input) && in_array($input['entity'], ['App\Models\Invoice','App\Models\Quote','App\Models\Credit','App\Models\RecurringInvoice','App\Models\PurchaseOrder','App\Models\Payment'])) {
$company = $user->company();
@ -148,11 +148,12 @@ class SendEmailRequest extends Request
if ($entity_obj && ($company->id == $entity_obj->company_id) && $user->can('edit', $entity_obj)) {
return true;
}
} else {
$this->error_message = "Invalid entity or entity_id";
}
else {
$this->error_message = "Invalid entity or entity_id";
}
}
return false;
return true;
}
protected function failedAuthorization()

View File

@ -11,16 +11,17 @@
namespace App\Jobs\EDocument;
use App\Models\Expense;
use App\Services\EDocument\Imports\ParseEDocument;
use App\Utils\TempFile;
use Exception;
use App\Models\Company;
use App\Models\Expense;
use App\Utils\TempFile;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use App\Services\EDocument\Imports\ParseEDocument;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use App\Services\EDocument\Imports\ZugferdEDocument;
@ -31,8 +32,6 @@ class ImportEDocument implements ShouldQueue
use Queueable;
use SerializesModels;
public $tries = 1;
public function __construct(private readonly string $file_content, private string $file_name, private string $file_mime_type, private Company $company)
{
@ -46,7 +45,6 @@ class ImportEDocument implements ShouldQueue
*/
public function handle(): Expense
{
$file = TempFile::UploadedFileFromRaw($this->file_content, $this->file_name, $this->file_mime_type);
return (new ParseEDocument($file, $this->company))->run();
@ -63,6 +61,8 @@ class ImportEDocument implements ShouldQueue
if ($exception) {
nlog("EXCEPTION:: ImportEDocument:: " . $exception->getMessage());
}
$this->fail($exception); //manually fail - prevents future jobs with the same name from being discarded
config(['queue.failed.driver' => null]);
}

View File

@ -118,6 +118,7 @@ class Vendor extends BaseModel
'language_id',
'classification',
'is_tax_exempt',
'routing_id',
];
protected $casts = [

View File

@ -41,6 +41,8 @@ class ParseEDocument extends AbstractService
*/
public function run(): Expense
{
nlog("starting");
nlog($this->company->id);
/** @var \App\Models\Account $account */
$account = $this->company->owner()->account;
@ -51,7 +53,6 @@ class ParseEDocument extends AbstractService
// ZUGFERD - try to parse via Zugferd lib
switch (true) {
case ($extension == 'pdf' || $mimetype == 'application/pdf'):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017"):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0"):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_2.1"):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_2.0"):
@ -60,6 +61,7 @@ class ParseEDocument extends AbstractService
} catch (\Throwable $e) {
nlog("Zugferd Exception: " . $e->getMessage());
}
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017"):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:oasis:names:specification:ubl"):
try {
return (new UblEDocument($this->file, $this->company))->run();

View File

@ -0,0 +1,134 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\EDocument\Imports;
use App\Models\Vendor;
use App\Models\Company;
use App\Models\Country;
use App\Models\Expense;
use App\Factory\VendorFactory;
use App\Factory\ExpenseFactory;
use App\Services\AbstractService;
use Illuminate\Http\UploadedFile;
use InvoiceNinja\EInvoice\EInvoice;
use App\Utils\Traits\SavesDocuments;
use App\Factory\VendorContactFactory;
use App\Models\Currency;
use App\Repositories\ExpenseRepository;
class Ubl2Pdf extends AbstractService
{
/**
* @throws \Throwable
*/
public function __construct(public \InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice, public Company $company)
{
}
public function run()
{
$client = $this->clientDetails();
$supplier = $this->supplierDetails();
$invoiceLines = $this->invoiceLines();
$totals = $this->totals();
}
private function clientDetails(): array
{
return [
'name' => data_get($this->invoice, 'AccountingCustomerParty.Party.PartyName.0.Name',''),
'address1' => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.StreetName',''),
'address2' => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.AdditionalStreetName',''),
'city' => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.CityName',''),
'state' => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CountrySubentity',''),
'postal_code' => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.PostalZone',''),
'country_id' => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.Country.IdentificationCode.value',''),
'vat_number' => data_get($this->invoice, 'AccountingCustomerParty.Party.PartyTaxScheme.0.CompanyID.value',''),
'contacts' => [
'first_name' => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Name',''),
'phone' => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Telephone',''),
'email' => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.ElectronicMail',''),
],
'settings' => [
'currency_id' => $this->resolveCurrencyId(data_get($this->invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code))
]
];
}
private function supplierDetails(): array
{
return [
'name' => data_get($this->invoice, 'AccountingSupplierParty.Party.PartyName.0.name', ''),
'address1' => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.StreetName', ''),
'address2' => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.AdditionalStreetName', ''),
'city' => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CityName', ''),
'state' => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CountrySubentity', ''),
'postal_code' => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.PostalZone', ''),
'country_id' => $this->resolveCountry(data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.Country.IdentificationCode.value', '')),
'vat_number' => data_get($this->invoice, 'AccountingSupplierParty.Party.PartyTaxScheme.0.CompanyID.value', ''),
'contacts' => [
'first_name' => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Name', ''),
'phone' => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Telephone', ''),
'email' => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.ElectronicMail', ''),
],
];
}
private function invoiceLines(): array
{
$lines = data_get($this->invoice, 'invoiceLine', []);
return array_map(function ($line) {
return [
'quantity' => data_get($line, 'InvoicedQuantity',0),
'unit_code' => data_get($line, 'InvoicedQuantity.UnitCode',0),
'line_extension_amount' => data_get($line, 'LineExtensionAmount',0),
'item' => [
'name' => data_get($line, 'Item.Name',''),
'description' => data_get($line, 'Item.Description',''),
],
'price' => data_get($line, 'Price.PriceAmount',0),
];
}, $lines);
}
private function totals(): array
{
return [
'line_extension_amount' => data_get($this->invoice, 'LegalMonetaryTotal.LineExtensionAmount',0),
'tax_exclusive_amount' => data_get($this->invoice, 'LegalMonetaryTotal.TaxExclusiveAmount',0),
'tax_inclusive_amount' => data_get($this->invoice, 'LegalMonetaryTotal.TaxInclusiveAmount',0),
'charge_total_amount' => data_get($this->invoice, 'LegalMonetaryTotal.ChargeTotalAmount',0),
'payable_amount' => data_get($this->invoice, 'LegalMonetaryTotal.PayableAmount',0),
];
}
private function resolveCountry(?string $iso_country_code): int
{
return Country::query()
->where('iso_3166_2', $iso_country_code)
->orWhere('iso_3166_3', $iso_country_code)
->first()?->id ?? (int)$this->company->settings->country_id;
}
private function resolveCurrencyId(string $currency_code): int
{
/** @var \Illuminate\Support\Collection<\App\Models\Currency> */
$currencies = app('currencies');
return $currencies->first(function (Currency $c) use ($currency_code) {
return $c->code === $currency_code;
})?->id ?? (int) $this->company->settings->currency_id;
}
}

View File

@ -14,14 +14,17 @@ namespace App\Services\EDocument\Imports;
use App\Models\Vendor;
use App\Models\Company;
use App\Models\Country;
use App\Models\Expense;
use App\Factory\VendorFactory;
use App\Factory\ExpenseFactory;
use App\Factory\VendorContactFactory;
use App\Services\AbstractService;
use Illuminate\Http\UploadedFile;
use InvoiceNinja\EInvoice\EInvoice;
use App\Utils\Traits\SavesDocuments;
use App\Factory\VendorContactFactory;
use App\Models\Currency;
use App\Repositories\ExpenseRepository;
class UblEDocument extends AbstractService
{
@ -41,18 +44,12 @@ class UblEDocument extends AbstractService
public function run(): \App\Models\Expense
{
//parse doc
// try{
$e = new EInvoice();
$invoice = $e->decode('Peppol', $this->file->get(), 'xml');
return $this->buildAndSaveExpense($invoice);
// }
// catch(\Throwable $e){
// return $e->getMessage();
// }
}
private function buildAndSaveExpense(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice): Expense
@ -60,23 +57,116 @@ class UblEDocument extends AbstractService
$vendor = $this->findOrCreateVendor($invoice);
$TaxExclusiveAmount = data_get($invoice, 'LegalMonetaryTotal.TaxExclusiveAmount.amount', 0);
$TaxInclusiveAmount = data_get($invoice, 'LegalMonetaryTotal.TaxExclusiveAmount.amount', 0);
$ChargeTotalAmount = data_get($invoice, 'LegalMonetaryTotal.ChargeTotalAmount.amount', 0);
$PayableAmount = data_get($invoice, 'LegalMonetaryTotal.PayableAmount.amount', 0);
$TaxAmount = data_get($invoice, 'TaxTotal.0.TaxAmount.amount', 0);
$tax_sub_totals = data_get($invoice, 'TaxTotal.0.TaxSubtotal', []);
$currency_code = data_get($invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code);
$date = data_get($invoice, 'IssueDate', now()->format('Y-m-d'));
$payment_means = [];
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PaymentMeansCode.name', false);
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PaymentID.value', false);
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PayeeFinancialAccount.ID.value', false);
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PayeeFinancialAccount.Name', false);
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PayeeFinancialAccount.FinancialInstitutionBranch.ID.value', false);
$payment_means[] = data_get($invoice, 'PaymentTerms.0.Note', false);
$private_notes = collect($payment_means)
->reject(function ($means){
return $means === false;
})->implode("\n");
$invoice_items = data_get($invoice, 'InvoiceLine', []);
$items = [];
foreach($invoice_items as $key => $item)
{
$items[$key]['name'] = data_get($item, 'Item.Name', false);
$items[$key]['description'] = data_get($item, 'Item.Description', false);
}
$public_notes = collect($items)->reject(function ($item){
return $item['name'] === false && $item['description'] === false;
})->map(function ($item){
return $item['name'] ?? ' ' . ' ## '. $item['description'] ?? ' '; //@phpstan-ignore-line
})->implode("\n");
/** @var \App\Models\Expense $expense */
$expense = ExpenseFactory::create($this->company->id, $this->company->owner()->id);
$expense->vendor_id = $vendor->id;
$expense->amount = $this->company->expense_inclusive_taxes ? $TaxInclusiveAmount : $TaxExclusiveAmount;
$expense->currency_id = $this->resolveCurrencyId($currency_code);
$expense->date = $date;
$expense->private_notes = $private_notes;
$expense->public_notes = $public_notes;
$tax_level = 1;
foreach ($tax_sub_totals as $key => $sub_tax) {
$amount = data_get($sub_tax, 'TaxAmount.amount', 0);
$taxable_amount = data_get($sub_tax, 'TaxableAmount.amount', 0);
$tax_rate = data_get($sub_tax, 'TaxCategory.Percent', 0);
$id = data_get($sub_tax, 'TaxCategory.ID.value', '');
$tax_name = data_get($sub_tax, 'TaxCategory.TaxScheme.ID.value', '');
if (!$this->company->calculate_expense_tax_by_amount && $tax_rate > 0) {
$expense->{"tax_rate{$tax_level}"} = $tax_rate;
$expense->{"tax_name{$tax_level}"} = $tax_name;
$expense->calculate_tax_by_amount = false;
}
else {
$expense->calculate_tax_by_amount = true;
$expense->{"tax_amount{$tax_level}"} = $amount; //@phpstan-ignore-line
}
$tax_level++;
if ($tax_level == 4) {
break;
}
}
$expense->save();
$repo = new ExpenseRepository();
$data = [];
$data['documents'][] = $this->file;
$expense = $repo->save($data, $expense);
return $expense;
}
private function resolveCurrencyId(string $currency_code): int
{
/** @var \Illuminate\Support\Collection<\App\Models\Currency> */
$currencies = app('currencies');
return $currencies->first(function (Currency $c) use ($currency_code) {
return $c->code === $currency_code;
})?->id ?? (int) $this->company->settings->currency_id;
}
private function findOrCreateVendor(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice): Vendor
{
$asp = $invoice->AccountingSupplierParty;
$vat_number = $asp->Party->PartyTaxScheme->CompanyID ?? false;
$id_number = $asp->Party->PartyIdentification->ID ?? false;
$routing_id = $asp->Party->EndpointID ?? false;
$vendor_name = $asp->Party->PartyName->Name ?? false;
$vat_number = $this->resolveVendorVat($invoice);
$id_number = $this->resolveVendorIdNumber($invoice);
$routing_id = data_get($invoice, 'AccountingSupplierParty.Party.EndpointID.value', false);
$vendor_name = $this->resolveSupplierName($invoice);
$vendor = Vendor::query()
->where('company_id', $this->company->id)
@ -85,13 +175,13 @@ class UblEDocument extends AbstractService
$q->when($routing_id, function ($q) use ($routing_id){
$q->where('routing_id', $routing_id);
})
->when($vat_number, function ($q) use ($vat_number){
->when(strlen($vat_number) > 1, function ($q) use ($vat_number){
$q->orWhere('vat_number', $vat_number);
})
->when($id_number, function ($q) use ($id_number){
->when(strlen($id_number) > 1, function ($q) use ($id_number){
$q->orWhere('id_number', $id_number);
})
->when($vendor_name, function ($q) use ($vendor_name){
->when(strlen($vendor_name) > 1, function ($q) use ($vendor_name){
$q->orWhere('name', $vendor_name);
});
@ -100,24 +190,62 @@ class UblEDocument extends AbstractService
return $vendor ?? $this->newVendor($invoice);
}
private function resolveSupplierName(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice): string
{
if(data_get($invoice, 'AccountingSupplierParty.Party.PartyName', false)){
$party_name = data_get($invoice, 'AccountingSupplierParty.Party.PartyName', false);
return data_get($party_name[0], 'Name', '');
}
if (data_get($invoice, 'AccountingSupplierParty.Party.PartyLegalEntity', false)) {
$ple = data_get($invoice, 'AccountingSupplierParty.Party.PartyName', false);
return data_get($ple[0], 'RegistrationName', '');
}
return '';
}
private function resolveVendorIdNumber(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice):string
{
$pts = data_get($invoice, 'AccountingSupplierParty.Party.PartyIdentification', false);
return $pts ? data_get($pts[0], 'ID.value', '') : '';
}
private function resolveVendorVat(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice):string
{
$pts = data_get($invoice, 'AccountingSupplierParty.Party.PartyTaxScheme', false);
return $pts ? data_get($pts[0], 'CompanyID.value', '') : '';
}
private function newVendor(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice): Vendor
{
$vendor = VendorFactory::create($this->company->id, $this->company->owner()->id);
$data = [
'name' => data_get($invoice, 'AccountingSupplierParty.Party.PartyName.Name', ''),
'routing_id' => data_get($invoice, 'AccountingSupplierParty.Party.EndpointID', ''),
'vat_number' => data_get($invoice, 'AccountingSupplierParty.Party.PartyTaxScheme.CompanyID', ''),
'name' => $this->resolveSupplierName($invoice),
'routing_id' => data_get($invoice, 'AccountingSupplierParty.Party.EndpointID.value', ''),
'vat_number' => $this->resolveVendorVat($invoice),
'id_number' => $this->resolveVendorIdNumber($invoice),
'address1' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.StreetName',''),
'address2' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.AdditionalStreetName',''),
'city' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.CityName',''),
'state' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.CountrySubentity',''),
'postal_code' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.PostalZone',''),
'country_id' => $this->resovelCountry(data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.Country.IdentificationCode','')),
'country_id' => $this->resolveCountry(data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.Country.IdentificationCode.value','')),
'currency_id' => $this->resolveCurrencyId(data_get($invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code)),
];
$vendor->fill($data);
$vendor->save();
$vendor->service()->applyNumber();
if(data_get($invoice, 'AccountingSupplierParty.Party.Contact',false))
{
@ -138,6 +266,6 @@ class UblEDocument extends AbstractService
return Country::query()
->where('iso_3166_2', $iso_country_code)
->orWhere('iso_3166_3', $iso_country_code)
->first() ?? (int)$this->company->settings->country_id;
->first()?->id ?? (int)$this->company->settings->country_id;
}
}

View File

@ -124,6 +124,8 @@ class Statics
$data['bulk_updates'] = [
'client' => \App\Models\Client::$bulk_update_columns,
'expense' => \App\Models\Expense::$bulk_update_columns,
'recurring_invoice' => \App\Models\RecurringInvoice::$bulk_update_columns,
];
return $data;

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('vendors', function (Blueprint $table) {
if (!Schema::hasColumn('vendors', 'routing_id')) {
$table->string('routing_id')->nullable();
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
}
};

View File

@ -57,6 +57,27 @@ class InvoiceEmailTest extends TestCase
$this->assertTrue(strpos($email, '@example.com') !== false);
}
public function testEntityValidation()
{
$data = [
"body" => "hey what's up",
"entity" => 'blergen',
"entity_id" => $this->invoice->hashed_id,
"subject" => 'Reminder $number',
"template" => "email_template_invoice"
];
$response = false;
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/emails', $data);
$response->assertStatus(422);
}
public function testClientEmailHistory()
{

View File

@ -12,7 +12,10 @@
namespace Tests\Integration\Einvoice;
use Tests\TestCase;
use App\Utils\TempFile;
use Tests\MockAccountData;
use InvoiceNinja\EInvoice\EInvoice;
use App\Services\EDocument\Imports\UblEDocument;
/**
@ -20,9 +23,27 @@ use InvoiceNinja\EInvoice\EInvoice;
*/
class ImportEInvoiceTest extends TestCase
{
use MockAccountData;
protected function setUp(): void
{
parent::setUp();
$this->makeTestData();
}
public function testImportExpenseEinvoice()
{
$file = file_get_contents(base_path('tests/Integration/Einvoice/samples/peppol.xml'));
$file = TempFile::UploadedFileFromRaw($file, 'peppol.xml', 'xml');
$expense = (new UblEDocument($file, $this->company))->run();
$this->assertNotNull($expense);
}
public function testParsingDocument()
@ -45,57 +66,76 @@ class ImportEInvoiceTest extends TestCase
$this->assertInstanceOf(\InvoiceNinja\EInvoice\Models\Peppol\Invoice::class, $invoice);
}
// public function testHtmlConversion()
// {
public function testHtmlConversion()
{
// // Load the XML source
// $xml = new \DOMDocument();
// $xml->load(base_path('tests/Integration/Einvoice/samples/peppol.xml'));
// Load the XML source
$xml = new \DOMDocument();
$xml->load(base_path('tests/Integration/Einvoice/samples/peppol.xml'));
// // Load XSLT stylesheet
// $xsl = new \DOMDocument();
// $xsl->load(base_path('tests/Integration/Einvoice/samples/peppol.xslt'));
// Load XSLT stylesheet
$xsl = new \DOMDocument();
$xsl->load(base_path('tests/Integration/Einvoice/samples/peppol.xslt'));
// // Configure the transformer
// $proc = new \XSLTProcessor();
// $proc->importStyleSheet($xsl); // attach the xsl rules
// Configure the transformer
$proc = new \XSLTProcessor();
$proc->importStyleSheet($xsl); // attach the xsl rules
// $transformed = $proc->transformToXML($xml);
// // determining if output is html document
// $html = $transformed;
// // splitting up html document at doctype and doc
// $html_array = explode("\n", $html, 15);
// $html_doc = array_pop($html_array);
// $html_doctype = implode("\n", $html_array);
// // convert XHTML syntax to HTML5
// // <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
// // <!DOCTYPE html>
// $proc->setParameter('', 'mode', 'document');
// $html_doctype = preg_replace("/<!DOCTYPE [^>]+>/", "<!DOCTYPE html>", $html_doctype);
// // <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
// // <html lang="en">
// libxml_use_internal_errors(true);
// // ... your existing code ...
// $transformed = $proc->transformToXML($xml);
// if ($transformed === false) {
// foreach (libxml_get_errors() as $error) {
// nlog($error->message);
// }
// } else {
// nlog($transformed);
// }
// libxml_clear_errors();
// $html_doctype = preg_replace('/ xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"| xml:lang="[^\"]*\"/', '', $html_doctype);
// nlog($transformed);
// // determining if output is html document
// $html = $transformed;
// // splitting up html document at doctype and doc
// $html_array = explode("\n", $html, 15);
// $html_doc = array_pop($html_array);
// $html_doctype = implode("\n", $html_array);
// // convert XHTML syntax to HTML5
// // <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
// // <!DOCTYPE html>
// // <meta http-equiv="content-type" content="text/html; charset=utf-8" />
// // to this --> <meta charset="utf-8" />
// $html_doctype = preg_replace("/<!DOCTYPE [^>]+>/", "<!DOCTYPE html>", $html_doctype);
// $html_doctype = preg_replace('/<meta http-equiv=\"Content-Type\" content=\"text\/html; charset=(.*[a-z0-9-])\" \/>/i', '<meta charset="\1" />', $html_doctype);
// // <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
// // <html lang="en">
// $html = $html_doctype . "\n" . $html_doc;
// nlog($html);
// }
// $html_doctype = preg_replace('/ xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"| xml:lang="[^\"]*\"/', '', $html_doctype);
// // <meta http-equiv="content-type" content="text/html; charset=utf-8" />
// // to this --> <meta charset="utf-8" />
// $html_doctype = preg_replace('/<meta http-equiv=\"Content-Type\" content=\"text\/html; charset=(.*[a-z0-9-])\" \/>/i', '<meta charset="\1" />', $html_doctype);
// $html = $html_doctype . "\n" . $html_doc;
// nlog($html);
}
}

File diff suppressed because it is too large Load Diff