Tests for einvoice validation at invoice level
This commit is contained in:
parent
540e40348c
commit
6215fca188
|
|
@ -12,12 +12,13 @@
|
|||
namespace App\Http\Requests\Invoice;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\Invoice\LockedInvoiceRule;
|
||||
use App\Http\ValidationRules\Project\ValidProjectForClient;
|
||||
use App\Utils\Traits\ChecksEntityStatus;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use App\Utils\Traits\ChecksEntityStatus;
|
||||
use App\Http\ValidationRules\Invoice\LockedInvoiceRule;
|
||||
use App\Http\ValidationRules\EInvoice\ValidInvoiceScheme;
|
||||
use App\Http\ValidationRules\Project\ValidProjectForClient;
|
||||
|
||||
class UpdateInvoiceRequest extends Request
|
||||
{
|
||||
|
|
@ -94,6 +95,7 @@ class UpdateInvoiceRequest extends Request
|
|||
$rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
|
||||
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
|
||||
|
||||
$rules['e_invoice'] = ['sometimes', 'nullable', new ValidInvoiceScheme()];
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*1`
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\ValidationRules\EInvoice;
|
||||
|
||||
use App\Services\EDocument\Standards\Validation\Peppol\InvoiceLevel;
|
||||
use Closure;
|
||||
use InvoiceNinja\EInvoice\EInvoice;
|
||||
use Illuminate\Validation\Validator;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\Invoice;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Contracts\Validation\ValidatorAwareRule;
|
||||
|
||||
/**
|
||||
* Class ValidScheme.
|
||||
*/
|
||||
class ValidInvoiceScheme implements ValidationRule, ValidatorAwareRule
|
||||
{
|
||||
/**
|
||||
* The validator instance.
|
||||
*
|
||||
* @var Validator
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
|
||||
if (isset($value['Invoice'])) {
|
||||
$r = new EInvoice();
|
||||
$errors = $r->validateRequest($value['Invoice'], InvoiceLevel::class);
|
||||
|
||||
foreach ($errors as $key => $msg) {
|
||||
|
||||
$this->validator->errors()->add(
|
||||
"e_invoice.{$key}",
|
||||
"{$key} - {$msg}"
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current validator.
|
||||
*/
|
||||
public function setValidator(Validator $validator): static
|
||||
{
|
||||
$this->validator = $validator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -197,6 +197,7 @@ class Invoice extends BaseModel
|
|||
'auto_bill_enabled',
|
||||
'uses_inclusive_taxes',
|
||||
'vendor_id',
|
||||
'e_invoice',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
|
|
|||
|
|
@ -199,12 +199,12 @@ class Peppol extends AbstractService
|
|||
|
||||
$this->p_invoice->DocumentCurrencyCode = $this->invoice->client->currency()->code;
|
||||
|
||||
if ($this->invoice->date && $this->invoice->due_date) {
|
||||
$ip = new InvoicePeriod();
|
||||
$ip->StartDate = new \DateTime($this->invoice->date);
|
||||
$ip->EndDate = new \DateTime($this->invoice->due_date);
|
||||
$this->p_invoice->InvoicePeriod = [$ip];
|
||||
}
|
||||
// if ($this->invoice->date && $this->invoice->due_date) {
|
||||
// $ip = new InvoicePeriod();
|
||||
// $ip->StartDate = new \DateTime($this->invoice->date);
|
||||
// $ip->EndDate = new \DateTime($this->invoice->due_date);
|
||||
// $this->p_invoice->InvoicePeriod = [$ip];
|
||||
// }
|
||||
|
||||
if ($this->invoice->project_id) {
|
||||
$pr = new \InvoiceNinja\EInvoice\Models\Peppol\ProjectReferenceType\ProjectReference();
|
||||
|
|
@ -254,6 +254,7 @@ class Peppol extends AbstractService
|
|||
*/
|
||||
public function decode(mixed $invoice): self
|
||||
{
|
||||
|
||||
$this->p_invoice = $this->e->decode('Peppol', json_encode($invoice), 'json');
|
||||
|
||||
return $this;
|
||||
|
|
@ -267,7 +268,7 @@ class Peppol extends AbstractService
|
|||
private function setInvoice(): self
|
||||
{
|
||||
/** Handle Existing Document */
|
||||
if ($this->invoice->e_invoice && isset($this->invoice->e_invoice->Invoice)) {
|
||||
if ($this->invoice->e_invoice && isset($this->invoice->e_invoice->Invoice) && isset($this->invoice->e_invoice->Invoice->ID)) {
|
||||
|
||||
$this->decode($this->invoice->e_invoice->Invoice);
|
||||
|
||||
|
|
@ -1256,6 +1257,12 @@ class Peppol extends AbstractService
|
|||
}
|
||||
}
|
||||
|
||||
if(isset($this->invoice->e_invoice->Invoice)) {
|
||||
foreach(get_object_vars($this->invoice->e_invoice->Invoice) as $prop => $value) {
|
||||
$this->p_invoice->{$prop} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Plucks special overriding properties scanning the correct settings level
|
||||
$settings = [
|
||||
'AccountingCostCode' => 7,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<?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\Standards\Validation\Peppol;
|
||||
|
||||
use Symfony\Component\Serializer\Attribute\SerializedName;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\PeriodType\InvoicePeriod;
|
||||
|
||||
class InvoiceLevel
|
||||
{
|
||||
|
||||
/** @var InvoicePeriod[] */
|
||||
#[SerializedName('cac:InvoicePeriod')]
|
||||
public array $InvoicePeriod;
|
||||
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ use InvoiceNinja\EInvoice\Models\Peppol\PaymentMeans;
|
|||
use App\Services\EDocument\Gateway\Storecove\Storecove;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use InvoiceNinja\EInvoice\Models\FatturaPA\FatturaElettronica;
|
||||
use App\Services\EDocument\Standards\Validation\Peppol\InvoiceLevel;
|
||||
use App\Services\EDocument\Standards\Validation\XsltDocumentValidator;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\BranchType\FinancialInstitutionBranch;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\FinancialAccountType\PayeeFinancialAccount;
|
||||
|
|
@ -162,6 +163,51 @@ class PeppolTest extends TestCase
|
|||
return compact('company', 'client', 'invoice');
|
||||
}
|
||||
|
||||
public function testInvoicePeriodValidation()
|
||||
{
|
||||
|
||||
$scenario = [
|
||||
'company_vat' => 'DE923356489',
|
||||
'company_country' => 'DE',
|
||||
'client_country' => 'DE',
|
||||
'client_vat' => 'DE923256489',
|
||||
'client_id_number' => '123456789',
|
||||
'classification' => 'business',
|
||||
'has_valid_vat' => true,
|
||||
'over_threshold' => true,
|
||||
'legal_entity_id' => 290868,
|
||||
'is_tax_exempt' => false,
|
||||
];
|
||||
|
||||
|
||||
$entity_data = $this->setupTestData($scenario);
|
||||
|
||||
$invoice = $entity_data['invoice'];
|
||||
|
||||
$data = $invoice->toArray();
|
||||
|
||||
$data['e_invoice'] = [
|
||||
'Invoice' => [
|
||||
'InvoicePeriod' => [
|
||||
[
|
||||
'StartDate' => '-01',
|
||||
'EndDate' => 'boop',
|
||||
'Description' => 'Mustafa',
|
||||
'HelterSkelter' => 'sif'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson('/api/v1/invoices/'.$invoice->hashed_id, $data);
|
||||
|
||||
$response->assertStatus(422);
|
||||
|
||||
}
|
||||
|
||||
public function testInvoiceValidationWithSmallDiscount()
|
||||
{
|
||||
$scenario = [
|
||||
|
|
@ -452,6 +498,19 @@ class PeppolTest extends TestCase
|
|||
$entity_data = $this->setupTestData($scenario);
|
||||
|
||||
$invoice = $entity_data['invoice'];
|
||||
|
||||
$invoice->e_invoice = [
|
||||
'Invoice' => [
|
||||
'InvoicePeriod' => [
|
||||
[
|
||||
'cbc:StartDate' => $invoice->date,
|
||||
'cbc:EndDate' => $invoice->due_date ?? $invoice->date,
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
$invoice->save();
|
||||
|
||||
$company = $entity_data['company'];
|
||||
$settings = $company->settings;
|
||||
|
||||
|
|
@ -569,6 +628,19 @@ class PeppolTest extends TestCase
|
|||
$invoice = $data['invoice'];
|
||||
$invoice = $invoice->calc()->getInvoice();
|
||||
|
||||
$invoice->e_invoice = [
|
||||
'Invoice' => [
|
||||
'InvoicePeriod' => [
|
||||
[
|
||||
'cbc:StartDate' => $invoice->date,
|
||||
'cbc:EndDate' => $invoice->due_date ?? $invoice->date,
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$storecove = new Storecove();
|
||||
$p = new Peppol($invoice);
|
||||
$p->run();
|
||||
|
|
@ -599,6 +671,19 @@ class PeppolTest extends TestCase
|
|||
$invoice = $data['invoice'];
|
||||
$invoice = $invoice->calc()->getInvoice();
|
||||
|
||||
$invoice->e_invoice = [
|
||||
'Invoice' => [
|
||||
'InvoicePeriod' => [
|
||||
[
|
||||
'cbc:StartDate' => $invoice->date,
|
||||
'cbc:EndDate' => $invoice->due_date ?? $invoice->date,
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$storecove = new Storecove();
|
||||
$p = new Peppol($invoice);
|
||||
$p->run();
|
||||
|
|
@ -830,10 +915,18 @@ class PeppolTest extends TestCase
|
|||
$stub = new \stdClass();
|
||||
$stub->Invoice = $einvoice;
|
||||
|
||||
$tax_data = new TaxModel();
|
||||
$tax_data->regions->EU->has_sales_above_threshold = true;
|
||||
$tax_data->regions->EU->tax_all_subregions = true;
|
||||
$tax_data->seller_subregion = 'DE';
|
||||
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $this->account->id,
|
||||
'settings' => $settings,
|
||||
'e_invoice' => $stub,
|
||||
'calculate_taxes' => true,
|
||||
'tax_data' => $tax_data,
|
||||
'legal_entity_id' => 290868,
|
||||
]);
|
||||
|
||||
$cu = CompanyUserFactory::create($this->user->id, $company->id, $this->account->id);
|
||||
|
|
@ -844,7 +937,8 @@ class PeppolTest extends TestCase
|
|||
|
||||
$client_settings = ClientSettings::defaults();
|
||||
$client_settings->currency_id = '3';
|
||||
|
||||
$client_settings->enable_e_invoice = true;
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $this->user->id,
|
||||
|
|
@ -857,6 +951,7 @@ class PeppolTest extends TestCase
|
|||
'country_id' => 276,
|
||||
'routing_id' => 'ABC1234',
|
||||
'settings' => $client_settings,
|
||||
'is_tax_exempt' => false,
|
||||
]);
|
||||
|
||||
|
||||
|
|
@ -869,6 +964,7 @@ class PeppolTest extends TestCase
|
|||
$item->is_amount_discount = false;
|
||||
$item->tax_rate1 = 19;
|
||||
$item->tax_name1 = 'mwst';
|
||||
$item->tax_id = '1';
|
||||
|
||||
$invoice = Invoice::factory()->create([
|
||||
'company_id' => $company->id,
|
||||
|
|
@ -880,19 +976,23 @@ class PeppolTest extends TestCase
|
|||
'tax_rate1' => 0,
|
||||
'tax_name1' => '',
|
||||
'tax_rate2' => 0,
|
||||
'tax_rate3' => 0,
|
||||
'tax_name2' => '',
|
||||
'tax_rate3' => 0,
|
||||
'tax_name3' => '',
|
||||
'line_items' => [$item],
|
||||
'number' => 'DE-'.rand(1000, 100000),
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'is_amount_discount' => false,
|
||||
'custom_surcharge1' => 10,
|
||||
]);
|
||||
|
||||
$invoice->custom_surcharge1 = 10;
|
||||
$invoice = $invoice->calc()->getInvoice();
|
||||
|
||||
$invoice->service()->markSent()->save();
|
||||
|
||||
|
||||
nlog($invoice->toArray());
|
||||
|
||||
$this->assertEquals(130.90, $invoice->amount);
|
||||
|
||||
$peppol = new Peppol($invoice);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\EInvoice\RequestValidation;
|
||||
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Http\Requests\Invoice\UpdateInvoiceRequest;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Tests\MockAccountData;
|
||||
|
||||
class InvoicePeriodTest extends TestCase
|
||||
{
|
||||
use MockAccountData;
|
||||
|
||||
protected UpdateInvoiceRequest $request;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->withoutMiddleware(
|
||||
ThrottleRequests::class
|
||||
);
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
}
|
||||
|
||||
public function testEInvoicePeriodValidationPasses()
|
||||
{
|
||||
|
||||
$data = $this->invoice->toArray();
|
||||
$data['e_invoice'] = [
|
||||
'Invoice' => [
|
||||
'InvoicePeriod' => [
|
||||
'StartDate' => '2025-01-01',
|
||||
'EndDate' => '2025-01-01',
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson('/api/v1/invoices/'.$this->invoice->hashed_id, $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testEInvoicePeriodValidationFails()
|
||||
{
|
||||
|
||||
$data = $this->invoice->toArray();
|
||||
$data['e_invoice'] = [
|
||||
'Invoice' => [
|
||||
'InvoicePeriod' => [
|
||||
'notarealvar' => '2025-01-01',
|
||||
'worseVar' => '2025-01-01',
|
||||
'Description' => 'Mustafa'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson('/api/v1/invoices/'.$this->invoice->hashed_id, $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
nlog($arr);
|
||||
$response->assertStatus(422);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ class DownloadHistoricalInvoiceTest extends TestCase
|
|||
$obj->invoice_id = $this->invoice->id;
|
||||
$obj->user_id = $this->invoice->user_id;
|
||||
$obj->company_id = $this->company->id;
|
||||
|
||||
$obj->activity_type_id = \App\Models\Activity::EMAIL_INVOICE;
|
||||
$activity_repo->save($obj, $this->invoice, Ninja::eventVars());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue