From 0cdbbbb2013d39bff6d1ab7e012589d314730ada Mon Sep 17 00:00:00 2001 From: Andrei Ciungulete Date: Tue, 30 Jan 2024 00:13:24 +0200 Subject: [PATCH] efactura: move requests to classes --- README.md | 47 +++++---- src/Enums/Efactura/ConvertXmlStandard.php | 11 ++ src/Enums/Efactura/MessagesFilter.php | 13 +++ src/Enums/Efactura/UploadStandard.php | 3 - src/Requests/Efactura/MessagesRequest.php | 76 ++++++++++++++ src/Requests/Efactura/UploadRequest.php | 111 ++++++++++++++++++++ src/Requests/Efactura/XmlToPdfRequest.php | 61 +++++++++++ src/Resources/Efactura.php | 43 +++----- tests/Arch.php | 1 + tests/Requests/Efactura/MessagesRequest.php | 39 +++++++ tests/Requests/Efactura/UploadRequest.php | 62 +++++++++++ tests/Requests/Efactura/XmlToPdfRequest.php | 33 ++++++ tests/Resources/Efactura.php | 32 +++--- 13 files changed, 467 insertions(+), 65 deletions(-) create mode 100644 src/Enums/Efactura/ConvertXmlStandard.php create mode 100644 src/Enums/Efactura/MessagesFilter.php create mode 100644 src/Requests/Efactura/MessagesRequest.php create mode 100644 src/Requests/Efactura/UploadRequest.php create mode 100644 src/Requests/Efactura/XmlToPdfRequest.php create mode 100644 tests/Requests/Efactura/MessagesRequest.php create mode 100644 tests/Requests/Efactura/UploadRequest.php create mode 100644 tests/Requests/Efactura/XmlToPdfRequest.php diff --git a/README.md b/README.md index b6d2743..82b2509 100644 --- a/README.md +++ b/README.md @@ -246,13 +246,15 @@ Upload an XML (eFactura) file to the SPV TODO: improve error handling ```php -$upload = $authorizedClient->efactura()->upload( - xml_path: $pathToXmlFile, - taxIdentificationNumber: '12345678', - //standard: UploadStandard::UBL, // default value is UBL - //extern: false, // default value is false - //selfInvoice: false, // default value is false -); +use Anaf\Requests\Efactura\UploadRequest; +use Anaf\Enums\Efactura\UploadStandard; + +$uploadRequest = UploadRequest::withXmlPath($pathToXmlFile) + ->withTaxIdentificationNumber('12345678') + ->withStandard(UploadStandard::UBL); + //->extern() // for external invoices + //->selfInvoice(); // for self invoices +$upload = $authorizedClient->efactura()->upload($uploadRequest); $upload->responseDate, // 202401011640 $upload->executionStatus, $upload->uploadIndex, @@ -265,10 +267,14 @@ TODO: implement `status` from [here](https://mfinante.gov.ro/static/10/eFactura/ TODO: implement `paginated messages` from [here](https://mfinante.gov.ro/static/10/eFactura/listamesaje.html#/EFacturaListaMesaje/getPaginatie) Get the list of available messages ```php -$spvMessages = $authorizedClient->efactura()->messages([ - 'zile' => 30, // between 1 and 60 - 'cif' => '12345678', -]); + +use Anaf\Enums\Efactura\MessagesFilter; +use Anaf\Requests\Efactura\MessagesRequest; + +$messagesRequest = MessagesRequest::withTaxIdetificationNumber('12345678') + ->withDays(30); + //->withFilter(MessagesFilter::RECEIVED); // received messages +$spvMessages = $authorizedClient->efactura()->messages($messagesRequest); $spvMessages->messages; // array $spvMessages->serial; @@ -287,9 +293,7 @@ $message->id, #### [Download - eFactura XML](https://mfinante.gov.ro/static/10/eFactura/descarcare.html) Resource Get a file from the SPV identified by the `id` received from the messages endpoint ```php -$file = $authorizedClient->efactura()->download([ - 'id' => '12345678', -]); +$file = $authorizedClient->efactura()->download(12345678); $file->getContent(); // string - You can save/download the content to a file ``` @@ -299,14 +303,17 @@ TODO: implement `validate` from [here](https://mfinante.gov.ro/static/10/eFactur #### [XmlToPdf](https://mfinante.gov.ro/static/10/eFactura/xmltopdf.html) Resource Convert XML eFactura to PDF. For this endpoint you need to use unauthenticated client ```php -/* - * $xmlStandard can be one of the following: 'FACT1', 'FCN'. - * The default value is 'FACT1' - */ -$file = $client->efactura()->xmlToPdf($pathToXmlFile, $xmlStandard); + +use Anaf\Enums\Efactura\ConvertXmlStandard; +use Anaf\Requests\Efactura\XmlToPdfRequest; + +$xmlToPdfRequest = XmlToPdfRequest::withXmlPath($pathToXmlFile) + ->withStandard(ConvertXmlStandard::UBL); + //->withoutValidation(); + +$file = $client->efactura()->xmlToPdf($xmlToPdfRequest); $file->getContent(); // string - You can save the pdf content to a file ``` - --- ANAF PHP is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**. diff --git a/src/Enums/Efactura/ConvertXmlStandard.php b/src/Enums/Efactura/ConvertXmlStandard.php new file mode 100644 index 0000000..9df76d7 --- /dev/null +++ b/src/Enums/Efactura/ConvertXmlStandard.php @@ -0,0 +1,11 @@ + $parameters + */ + public function __construct( + private readonly array $parameters, + ) { + // .. + } + + /** + * Creates a new Messages value object + */ + public static function withTaxIdetificationNumber(string $taxIdentificationNumber): self + { + return new self( + parameters: [ + 'cif' => $taxIdentificationNumber, + ], + ); + } + + /** + * Creates a new Message value object with the zile parameter + */ + public function withDays(int $days): self + { + if ($days < 1 || $days > 60) { + throw new InvalidArgumentException('Days must be between 1 and 60'); + } + + return new self( + parameters: [ + ...$this->parameters, + 'zile' => (string) $days, + ], + ); + } + + /** + * Creates a new Message value object with the filtru parameter + */ + public function withFilter(MessagesFilter $filter): self + { + return new self( + parameters: [ + ...$this->parameters, + 'filtru' => $filter->value, + ], + ); + } + + /** + * @return array $parameters + */ + public function toArray(): array + { + $requiredKeys = ['zile', 'cif']; + + $missingKeys = array_diff($requiredKeys, array_keys($this->parameters)); + + if ($missingKeys !== []) { + throw new InvalidArgumentException('Missing required parameters: '.implode(', ', $missingKeys)); + } + + return $this->parameters; + } +} diff --git a/src/Requests/Efactura/UploadRequest.php b/src/Requests/Efactura/UploadRequest.php new file mode 100644 index 0000000..42fb050 --- /dev/null +++ b/src/Requests/Efactura/UploadRequest.php @@ -0,0 +1,111 @@ + $parameters + */ + public function __construct( + private readonly string $xml_path, + private readonly array $parameters, + ) { + // .. + } + + /** + * Creates a new UploadParameters value object with the xml_path parameter + */ + public static function withXmlPath(string $xml_path): self + { + return new self(xml_path: $xml_path, parameters: []); + } + + /** + * Creates a new UploadParameters value object with the tax_identification_number parameter + */ + public function withTaxIdentificationNumber(string|int $tax_identification_number): self + { + return new self( + xml_path: $this->xml_path, + parameters: [ + ...$this->parameters, + 'cif' => (string) $tax_identification_number, + ], + ); + } + + /** + * Creates a new UploadParameters value object with the standard parameter + */ + public function withStandard(UploadStandard $standard): self + { + return new self( + xml_path: $this->xml_path, + parameters: [ + ...$this->parameters, + 'standard' => $standard->value, + ], + ); + } + + /** + * Creates a new UploadParameters value object with the extern parameter + */ + public function extern(): self + { + return new self( + xml_path: $this->xml_path, + parameters: [ + ...$this->parameters, + 'extern' => 'DA', + ], + ); + } + + /** + * Creates a new UploadParameters value object with the selfInvoice parameter + */ + public function selfInvoice(): self + { + return new self( + xml_path: $this->xml_path, + parameters: [ + ...$this->parameters, + 'autofactura' => 'DA', + ], + ); + } + + /** + * @return array $parameters + */ + public function toArray(): array + { + $requiredKeys = ['cif', 'standard']; + + $missingKeys = array_diff($requiredKeys, array_keys($this->parameters)); + + if ($missingKeys !== []) { + throw new InvalidArgumentException('Missing required parameters: '.implode(', ', $missingKeys)); + } + + return $this->parameters; + } + + /** + * Xml path getter + */ + public function getXmlPath(): string + { + if ($this->xml_path === '' || $this->xml_path === '0') { + throw new InvalidArgumentException('Xml path is required'); + } + + return $this->xml_path; + } +} diff --git a/src/Requests/Efactura/XmlToPdfRequest.php b/src/Requests/Efactura/XmlToPdfRequest.php new file mode 100644 index 0000000..515e0d6 --- /dev/null +++ b/src/Requests/Efactura/XmlToPdfRequest.php @@ -0,0 +1,61 @@ +xml_path, + standard: $standard, + validate: $this->validate, + ); + } + + /** + * Creates a new UploadParameters value object with the selfInvoice parameter + */ + public function withoutValidation(): self + { + return new self( + xml_path: $this->xml_path, + standard: $this->standard, + validate: false, + ); + } + + /** + * Xml path getter + */ + public function getXmlPath(): string + { + if ($this->xml_path === '' || $this->xml_path === '0') { + throw new InvalidArgumentException('Xml path is required'); + } + + return $this->xml_path; + } +} diff --git a/src/Resources/Efactura.php b/src/Resources/Efactura.php index 3fd3518..88c11ff 100644 --- a/src/Resources/Efactura.php +++ b/src/Resources/Efactura.php @@ -5,7 +5,9 @@ namespace Anaf\Resources; use Anaf\Contracts\FileContract; -use Anaf\Enums\Efactura\UploadStandard; +use Anaf\Requests\Efactura\MessagesRequest; +use Anaf\Requests\Efactura\UploadRequest; +use Anaf\Requests\Efactura\XmlToPdfRequest; use Anaf\Responses\Efactura\CreateMessagesResponse; use Anaf\Responses\Efactura\CreateUploadResponse; use Anaf\ValueObjects\Transporter\Payload; @@ -24,17 +26,12 @@ class Efactura * * @throws Exception */ - public function upload(string $xml_path, string $tax_identification_number, UploadStandard $standard = UploadStandard::UBL, bool $extern = false, bool $selfInvoice = false): CreateUploadResponse + public function upload(UploadRequest $uploadRequest): CreateUploadResponse { $payload = Payload::upload( resource: 'prod/FCTEL/rest/upload', - body: Xml::from($xml_path)->toString(), - parameters: [ - 'cif' => $tax_identification_number, - 'standard' => $standard->value, - ...($extern ? ['extern' => 'DA'] : []), - ...($selfInvoice ? ['autofactura' => 'DA'] : []), - ], + body: Xml::from($uploadRequest->getXmlPath())->toString(), + parameters: $uploadRequest->toArray(), ); /** @var array $response */ @@ -47,12 +44,10 @@ public function upload(string $xml_path, string $tax_identification_number, Uplo * Get the list of messages for a given taxpayer. * * @see https://mfinante.gov.ro/static/10/eFactura/listamesaje.html - * - * @param array $parameters */ - public function messages(array $parameters): CreateMessagesResponse + public function messages(MessagesRequest $messagesRequest): CreateMessagesResponse { - $payload = Payload::get('prod/FCTEL/rest/listaMesajeFactura', $parameters); + $payload = Payload::get('prod/FCTEL/rest/listaMesajeFactura', $messagesRequest->toArray()); /** * @var array{eroare?: string, mesaje: array, serial: string, cui: string, titlu: string} $response @@ -67,15 +62,15 @@ public function messages(array $parameters): CreateMessagesResponse } /** - * Get the list of messages for a given taxpayer. + * Download an eFactura XML file from ANAF. * * @see https://mfinante.gov.ro/static/10/eFactura/descarcare.html - * - * @param array $parameters */ - public function download(array $parameters): FileContract + public function download(int $id): FileContract { - $payload = Payload::get('prod/FCTEL/rest/descarcare', $parameters); + $payload = Payload::get('prod/FCTEL/rest/descarcare', [ + 'id' => (string) $id, + ]); return $this->transporter->requestFile($payload); } @@ -87,17 +82,13 @@ public function download(array $parameters): FileContract * * @throws Exception */ - public function xmlToPdf(string $xml_path, string $standard = 'FACT1', bool $validate = true): FileContract + public function xmlToPdf(XmlToPdfRequest $xmlToPdfRequest): FileContract { - if (! in_array($standard, ['FACT1', 'FCN'])) { - throw new RuntimeException("Invalid standard {$standard}"); - } - - $validateFile = $validate ? '/DA' : ''; + $validateFile = $xmlToPdfRequest->validate ? '/DA' : ''; $payload = Payload::upload( - resource: "prod/FCTEL/rest/transformare/{$standard}{$validateFile}", - body: Xml::from($xml_path)->toString(), + resource: "prod/FCTEL/rest/transformare/{$xmlToPdfRequest->standard->value}{$validateFile}", + body: Xml::from($xmlToPdfRequest->getXmlPath())->toString(), ); return $this->transporter->requestFile($payload); diff --git a/tests/Arch.php b/tests/Arch.php index d83c4f0..499a38b 100644 --- a/tests/Arch.php +++ b/tests/Arch.php @@ -18,6 +18,7 @@ 'Anaf\Exceptions', 'Anaf\Responses', 'Anaf\Enums', + 'Anaf\Requests', ]); test('responses')->expect('Anaf\Responses')->toOnlyUse([ diff --git a/tests/Requests/Efactura/MessagesRequest.php b/tests/Requests/Efactura/MessagesRequest.php new file mode 100644 index 0000000..7e36547 --- /dev/null +++ b/tests/Requests/Efactura/MessagesRequest.php @@ -0,0 +1,39 @@ +withDays(30); + + expect($messagesRequest->toArray())->toBe([ + 'cif' => '8000000000', + 'zile' => '30', + ]); +}); + +it('can be created for with filters', function () { + $messagesRequest = MessagesRequest::withTaxIdetificationNumber('8000000000') + ->withDays(30) + ->withFilter(MessagesFilter::ERROR); + + expect($messagesRequest->toArray())->toBe([ + 'cif' => '8000000000', + 'zile' => '30', + 'filtru' => 'E', + ]); +}); + +it('can throw exception when days is not set', function () { + $messagesRequest = MessagesRequest::withTaxIdetificationNumber('8000000000'); + + $messagesRequest->toArray(); +})->throws(InvalidArgumentException::class); + +it('can throw exception when days is not between 1 and 60', function () { + $messagesRequest = MessagesRequest::withTaxIdetificationNumber('8000000000') + ->withDays(61); + + $messagesRequest->toArray(); +})->throws(InvalidArgumentException::class); diff --git a/tests/Requests/Efactura/UploadRequest.php b/tests/Requests/Efactura/UploadRequest.php new file mode 100644 index 0000000..22edcb4 --- /dev/null +++ b/tests/Requests/Efactura/UploadRequest.php @@ -0,0 +1,62 @@ +withTaxIdentificationNumber('8000000000') + ->withStandard(UploadStandard::UBL); + + expect($uploadParameters->getXmlPath())->toBe('dummyxmlpath.xml') + ->and($uploadParameters->toArray())->toBe([ + 'cif' => '8000000000', + 'standard' => 'UBL', + ]); +}); + +it('can be created for extern invoices ', function () { + $uploadParameters = UploadRequest::withXmlPath('dummyxmlpath.xml') + ->withTaxIdentificationNumber('8000000000') + ->withStandard(UploadStandard::UBL) + ->extern(); + + expect($uploadParameters->getXmlPath())->toBe('dummyxmlpath.xml') + ->and($uploadParameters->toArray())->toBe([ + 'cif' => '8000000000', + 'standard' => 'UBL', + 'extern' => 'DA', + ]); +}); + +it('can be created for self invoices ', function () { + $uploadParameters = UploadRequest::withXmlPath('dummyxmlpath.xml') + ->withTaxIdentificationNumber('8000000000') + ->withStandard(UploadStandard::UBL) + ->selfInvoice(); + + expect($uploadParameters->getXmlPath())->toBe('dummyxmlpath.xml') + ->and($uploadParameters->toArray())->toBe([ + 'cif' => '8000000000', + 'standard' => 'UBL', + 'autofactura' => 'DA', + ]); +}); + +it('can throw exception when xml path is not set', function () { + UploadRequest::withXmlPath('') + ->withStandard(UploadStandard::UBL) + ->toArray(); +})->throws(InvalidArgumentException::class); + +it('can throw exception when tax identification number is not set', function () { + UploadRequest::withXmlPath('dummyxmlpath.xml') + ->withStandard(UploadStandard::UBL) + ->toArray(); +})->throws(InvalidArgumentException::class); + +it('can throw exception when standard is not set', function () { + UploadRequest::withXmlPath('dummyxmlpath.xml') + ->withTaxIdentificationNumber('8000000000') + ->toArray(); +})->throws(InvalidArgumentException::class); diff --git a/tests/Requests/Efactura/XmlToPdfRequest.php b/tests/Requests/Efactura/XmlToPdfRequest.php new file mode 100644 index 0000000..fdc5019 --- /dev/null +++ b/tests/Requests/Efactura/XmlToPdfRequest.php @@ -0,0 +1,33 @@ +withStandard(ConvertXmlStandard::INVOICE); + + expect($xmlToPdfRequest->getXmlPath())->toBe('dummyxmlpath.xml') + ->and($xmlToPdfRequest->standard->value)->toBe('FACT1') + ->and($xmlToPdfRequest->validate)->toBeTrue(); +}); + +it('can be created without validation', function () { + $xmlToPdfRequest = XmlToPdfRequest::withXmlPath('dummyxmlpath.xml') + ->withStandard(ConvertXmlStandard::INVOICE) + ->withoutValidation(); + + expect($xmlToPdfRequest->getXmlPath())->toBe('dummyxmlpath.xml') + ->and($xmlToPdfRequest->standard->value)->toBe('FACT1') + ->and($xmlToPdfRequest->validate)->toBeFalse(); +}); + +it('can be created without validation method order random', function () { + $xmlToPdfRequest = XmlToPdfRequest::withXmlPath('dummyxmlpath.xml') + ->withoutValidation() + ->withStandard(ConvertXmlStandard::CREDIT_NOTE); + + expect($xmlToPdfRequest->getXmlPath())->toBe('dummyxmlpath.xml') + ->and($xmlToPdfRequest->standard->value)->toBe('FCN') + ->and($xmlToPdfRequest->validate)->toBeFalse(); +}); diff --git a/tests/Resources/Efactura.php b/tests/Resources/Efactura.php index 8316f27..3e06b68 100644 --- a/tests/Resources/Efactura.php +++ b/tests/Resources/Efactura.php @@ -1,6 +1,10 @@ efactura()->upload( - xml_path: __DIR__.'/../Fixtures/dummyxml.xml', - tax_identification_number: '8000000000', - ); + $uploadParameters = UploadRequest::withXmlPath(__DIR__.'/../Fixtures/dummyxml.xml') + ->withTaxIdentificationNumber('8000000000') + ->withStandard(UploadStandard::UBL); + + $response = $authorizedClient->efactura()->upload($uploadParameters); expect($response)->toBeInstanceOf(CreateUploadResponse::class); }); @@ -19,12 +24,9 @@ test('get messages', function () { $authorizedClient = mockAuthorizedClient('GET', '/prod/FCTEL/rest/listaMesajeFactura', getEfacturaMessages()); - $response = $authorizedClient->efactura()->messages( - [ - 'zile' => 60, - 'cif' => '8000000000', - ], - ); + $request = MessagesRequest::withTaxIdetificationNumber('8000000000') + ->withDays(60); + $response = $authorizedClient->efactura()->messages($request); expect($response) ->toBeInstanceOf(CreateMessagesResponse::class) @@ -43,11 +45,7 @@ test('download efactura', function () { $authorizedClient = mockAuthorizedClient('GET', '/prod/FCTEL/rest/descarcare', getFakeFile('dummy content'), 'requestFile'); - $response = $authorizedClient->efactura()->download( - [ - 'id' => '1234AA456', - ], - ); + $response = $authorizedClient->efactura()->download(1234456); expect($response)->toBeInstanceOf(FileContract::class) ->and($response->getContent()) @@ -57,7 +55,9 @@ test('xml to pdf', function () { $authorizedClient = mockAuthorizedClient('POST', '/prod/FCTEL/rest/transformare/FACT1', getFakeFile('dummy pdf content'), 'requestFile'); - $response = $authorizedClient->efactura()->xmlToPdf(__DIR__.'/../Fixtures/dummyxml.xml', validate: false); + $request = XmlToPdfRequest::withXmlPath(__DIR__.'/../Fixtures/dummyxml.xml') + ->withoutValidation(); + $response = $authorizedClient->efactura()->xmlToPdf($request); expect($response)->toBeInstanceOf(FileContract::class); });