E-invoicing: Refactor calls (#113)

* Update main PEPPOL controller

* Expose countries in AddTaxIdentifierRequest

* setupLegalEntity method for Storecove

* Add missing methods to the StorecoveProxy

* Endpoint for additional legal identifiers

* Update return types on getLegalEntity
This commit is contained in:
Benjamin Beganović 2024-11-13 01:35:21 +01:00 committed by GitHub
parent 76f2b975a2
commit 20e400d621
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 208 additions and 141 deletions

View File

@ -11,8 +11,6 @@
namespace App\Http\Controllers;
use App\Utils\Ninja;
use Http;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
use App\Http\Requests\EInvoice\Peppol\StoreEntityRequest;
@ -54,65 +52,44 @@ class EInvoicePeppolController extends BaseController
*/
$company = auth()->user()->company();
$headers = [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
];
$response = $storecove
->proxy
->setCompany($company)
->setup($request->validated());
if (Ninja::isSelfHost()) {
$headers['X-EInvoice-Token'] = $company->account->e_invoicing_token;
if (data_get($response, 'status') === 'error') {
return response()->json(data_get($response, 'errors', 'message'), status: $response['code']);
}
if (Ninja::isHosted()) {
$headers['X-EInvoice-Secret'] = config('ninja.hosted_einvoice_secret');
}
$company->legal_entity_id = $response['legal_entity_id'];
$response = Http::baseUrl(config('ninja.hosted_ninja_url'))
->withHeaders($headers)
->post('/api/einvoice/peppol/setup', data: [
...$request->validated(),
'classification' => $request->classification ?? $company->settings->classification,
'vat_number' => $request->vat_number ?? $company->settings->vat_number,
'id_number' => $request->id_number ?? $company->settings->id_number,
]);
$tax_data = $company->tax_data;
if ($response->successful()) {
$company->legal_entity_id = $response->json('legal_entity_id');
$tax_data->acts_as_sender = $request->acts_as_sender;
$tax_data->acts_as_receiver = $request->acts_as_receiver;
$tax_data = $company->tax_data;
$settings = $company->settings;
$tax_data->acts_as_sender = $request->acts_as_sender;
$tax_data->acts_as_receiver = $request->acts_as_receiver;
$settings->name = $request->party_name;
$settings->country_id = (string) $request->country_id;
$settings->address1 = $request->line1;
$settings->address2 = $request->line2;
$settings->city = $request->city;
$settings->state = $request->county;
$settings->postal_code = $request->zip;
$settings = $company->settings;
$settings->e_invoice_type = 'PEPPOL';
$settings->vat_number = $request->vat_number ?? $company->settings->vat_number;
$settings->id_number = $request->id_number ?? $company->settings->id_number;
$settings->classification = $request->classification ?? $company->settings->classification;
$settings->enable_e_invoice = true;
$settings->name = $request->party_name;
$settings->country_id = (string) $request->country_id;
$settings->address1 = $request->line1;
$settings->address2 = $request->line2;
$settings->city = $request->city;
$settings->state = $request->county;
$settings->postal_code = $request->zip;
$company->tax_data = $tax_data;
$company->settings = $settings;
$settings->e_invoice_type = 'PEPPOL';
$settings->vat_number = $request->vat_number ?? $company->settings->vat_number;
$settings->id_number = $request->id_number ?? $company->settings->id_number;
$settings->classification = $request->classification ?? $company->settings->classification;
$settings->enable_e_invoice = true;
$company->save();
$company->tax_data = $tax_data;
$company->settings = $settings;
$company->save();
return response()->noContent();
}
if ($response->status() === 422) {
return response()->json($response->json(), 422);
}
return response()->noContent(status: 500);
return response()->noContent();
}
/**
@ -121,44 +98,29 @@ class EInvoicePeppolController extends BaseController
* @param \App\Http\Requests\EInvoice\Peppol\UpdateEntityRequest $request
* @return JsonResponse|mixed|Response
*/
public function updateLegalEntity(UpdateEntityRequest $request)
public function updateLegalEntity(UpdateEntityRequest $request, Storecove $storecove): JsonResponse
{
$company = auth()->user()->company();
$headers = [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
];
$response = $storecove
->proxy
->setCompany($company)
->updateLegalEntity($request->validated());
if (Ninja::isSelfHost()) {
$headers['X-EInvoice-Token'] = $company->account->e_invoicing_token;
if (data_get($response, 'status') === 'error') {
return response()->json(data_get($response, 'errors', 'message'), status: $response['code']);
}
if (Ninja::isHosted()) {
$headers['X-EInvoice-Secret'] = config('ninja.hosted_einvoice_secret');
}
$tax_data = $company->tax_data;
$response = Http::baseUrl(config('ninja.hosted_ninja_url'))
->withHeaders($headers)
->put('/api/einvoice/peppol/update', data: [
...$request->validated(),
'legal_entity_id' => $company->legal_entity_id,
]);
$tax_data->acts_as_sender = $request->acts_as_sender;
$tax_data->acts_as_receiver = $request->acts_as_receiver;
if ($response->successful()) {
$tax_data = $company->tax_data;
$company->tax_data = $tax_data;
$tax_data->acts_as_sender = $request->acts_as_sender;
$tax_data->acts_as_receiver = $request->acts_as_receiver;
$company->save();
$company->tax_data = $tax_data;
$company->save();
return response()->noContent();
}
return response()->json(['message' => 'Error updating identifier'], 422);
return response()->json();
}
/**
@ -167,50 +129,33 @@ class EInvoicePeppolController extends BaseController
* @param DisconnectRequest $request
* @return \Illuminate\Http\Response
*/
public function disconnect(DisconnectRequest $request): \Illuminate\Http\Response
public function disconnect(DisconnectRequest $request, Storecove $storecove): JsonResponse
{
/**
* @var \App\Models\Company $company
*/
$company = auth()->user()->company();
$headers = [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
];
$response = $storecove
->proxy
->setCompany($company)
->disconnect();
if (Ninja::isSelfHost()) {
$headers['X-EInvoice-Token'] = $company->account->e_invoicing_token;
if (data_get($response, 'status') === 'error') {
return response()->json(data_get($response, 'errors', 'message'), status: $response['code']);
}
if (Ninja::isHosted()) {
$headers['X-EInvoice-Secret'] = config('ninja.hosted_einvoice_secret');
}
$company->legal_entity_id = null;
$company->tax_data = $this->unsetVatNumbers($company->tax_data);
nlog($headers);
$settings = $company->settings;
$settings->e_invoice_type = 'EN16931';
$response = Http::baseUrl(config('ninja.hosted_ninja_url'))
->withHeaders($headers)
->post('/api/einvoice/peppol/disconnect', data: [
'company_key' => $company->company_key,
'legal_entity_id' => $company->legal_entity_id,
]);
$company->settings = $settings;
if ($response->successful()) {
$company->legal_entity_id = null;
$company->tax_data = $this->unsetVatNumbers($company->tax_data);
$company->save();
$settings = $company->settings;
$settings->e_invoice_type = 'EN16931';
$company->settings = $settings;
$company->save();
return response()->noContent();
}
return response()->noContent(status: 500);
return response()->json();
}
/**
@ -223,9 +168,8 @@ class EInvoicePeppolController extends BaseController
* @param Storecove $storecove
* @return \Illuminate\Http\JsonResponse
*/
public function addAdditionalTaxIdentifier(AddTaxIdentifierRequest $request, Storecove $storecove): \Illuminate\Http\JsonResponse
public function addAdditionalTaxIdentifier(AddTaxIdentifierRequest $request, Storecove $storecove): JsonResponse
{
$company = auth()->user()->company();
$tax_data = $company->tax_data;
@ -235,20 +179,22 @@ class EInvoicePeppolController extends BaseController
return response()->json(['message' => 'Identifier already exists for this region.'], 400);
}
$scheme = $storecove->router->resolveRouting($request->country, $company->settings->classification);
$response = $storecove
->proxy
->setCompany($company)
->addAdditionalTaxIdentifier($request->validated());
$storecove->addAdditionalTaxIdentifier($company->legal_entity_id, $request->vat_number, $scheme);
if (data_get($response, 'status') === 'error') {
return response()->json(data_get($response, 'errors', 'message'), status: $response['code']);
}
$tax_data->regions->EU->subregions->{$request->country}->vat_number = $request->vat_number;
$company->tax_data = $tax_data;
$company->save();
return response()->json(['message' => 'ok'], 200);
}
private function unsetVatNumbers(mixed $taxData): mixed
{
if (isset($taxData->regions->EU->subregions)) {

View File

@ -18,7 +18,7 @@ use Illuminate\Foundation\Http\FormRequest;
class AddTaxIdentifierRequest extends FormRequest
{
private array $vat_regex_patterns = [
public static array $vat_regex_patterns = [
'DE' => '/^DE\d{9}$/',
'AT' => '/^ATU\d{8}$/',
'BE' => '/^BE0\d{9}$/',
@ -66,14 +66,14 @@ class AddTaxIdentifierRequest extends FormRequest
public function rules(): array
{
return [
'country' => ['required', 'bail', Rule::in(array_keys($this->vat_regex_patterns))],
'country' => ['required', 'bail', Rule::in(array_keys(self::$vat_regex_patterns))],
'vat_number' => [
'required',
'string',
'bail',
function ($attribute, $value, $fail) {
if ($this->country && isset($this->vat_regex_patterns[$this->country])) {
if (!preg_match($this->vat_regex_patterns[$this->country], $value)) {
if ($this->country && isset(self::$vat_regex_patterns[$this->country])) {
if (!preg_match(self::$vat_regex_patterns[$this->country], $value)) {
$fail(ctrans('texts.invalid_vat_number'));
}
}
@ -89,6 +89,7 @@ class AddTaxIdentifierRequest extends FormRequest
if (isset($input['country'])) {
$country = $this->country();
$input['country'] = $country->iso_3166_2;
$input['country_id'] = $country->id;
}
$this->replace($input);

View File

@ -262,6 +262,35 @@ class Storecove
return $r;
}
public function setupLegalEntity(array $data): array|\Illuminate\Http\Client\Response
{
$legal_entity_response = $this->createLegalEntity($data);
if (! is_array($legal_entity_response)) {
return $legal_entity_response;
}
$scheme = $this->router->resolveRouting($data['country'], $data['classification']);
$add_identifier_response = $this->addIdentifier(
legal_entity_id: $legal_entity_response['id'],
identifier: $data['classification'] === 'individual' ? str_replace('/','', $data['id_number']) : str_replace(" ", "", $data['vat_number']),
scheme: $scheme,
);
if (! is_array($add_identifier_response)) {
return $add_identifier_response;
}
return [
'legal_entity_id' => $legal_entity_response['id'],
'tax_data' => [
'acts_as_sender' => $data['acts_as_sender'],
'acts_as_receiver' => $data['acts_as_receiver'],
],
];
}
/**
* CreateLegalEntity
*
@ -320,7 +349,7 @@ class Storecove
* @param int $id
* @return mixed
*/
public function getLegalEntity($id)
public function getLegalEntity($id): array|\Illuminate\Http\Client\Response
{
$uri = "legal_entities/{$id}";
@ -401,7 +430,7 @@ class Storecove
* @return mixed
*/
public function addAdditionalTaxIdentifier(int $legal_entity_id, string $identifier, string $scheme)
public function addAdditionalTaxIdentifier(int $legal_entity_id, string $identifier, string $scheme): array|\Illuminate\Http\Client\Response
{
$uri = "legal_entities/{$legal_entity_id}/additional_tax_identifiers";
@ -477,13 +506,17 @@ class Storecove
* @param int $legal_entity_id
* @return bool
*/
public function deleteIdentifier(int $legal_entity_id): bool
public function deleteIdentifier(int $legal_entity_id): bool|\Illuminate\Http\Client\Response
{
$uri = "/legal_entities/{$legal_entity_id}";
$r = $this->httpClient($uri, (HttpVerb::DELETE)->value, []);
return $r->successful();
if ($r->successful()) {
return true;
}
return $r;
}
/**

View File

@ -22,13 +22,13 @@ class StorecoveProxy
public function __construct(public Storecove $storecove)
{
}
public function setCompany(Company $company): self
{
$this->company = $company;
return $this;
}
/**
* Example refactor.
* getLegalEntity
@ -38,12 +38,12 @@ class StorecoveProxy
*/
public function getLegalEntity(int $legal_entity_id): array
{
if(Ninja::isHosted()){
if (Ninja::isHosted()) {
$response = $this->storecove->getLegalEntity($legal_entity_id);
if(is_array($response)) //successful response is the array
if (is_array($response)) //successful response is the array
return $response;
return $this->handleResponseError($response); //otherwise need to handle the http response returned
}
@ -52,7 +52,93 @@ class StorecoveProxy
return $this->remoteRequest($uri, $payload); //abstract the http calls
}
public function setup(array $data): array
{
$data = [
...$data,
'classification' => $data['classification'] ?? $this->company->settings->classification,
'vat_number' => $data['vat_number'] ?? $this->company->settings->vat_number,
'id_number' => $data['id_number'] ?? $this->company->settings->id_number,
];
if (Ninja::isHosted()) {
$response = $this->storecove->setupLegalEntity($data);
if (is_array($response)) {
return $response;
}
return $this->handleResponseError($response);
}
return $this->remoteRequest('/api/einvoice/peppol/setup', $data);
}
public function disconnect(): bool|array
{
$data = [
'company_key' => $this->company->company_key,
'legal_entity_id' => $this->company->legal_entity_id,
];
if (Ninja::isHosted()) {
$response = $this->storecove->deleteIdentifier(
legal_entity_id: $data['legal_entity_id'],
);
if (is_bool($response)) {
return $response;
}
return $this->handleResponseError($response);
}
return $this->remoteRequest('/api/einvoice/peppol/disconnect', $data);
}
public function updateLegalEntity(array $data): array
{
$data = [
...$data,
'legal_entity_id' => $this->company->legal_entity_id,
];
if (Ninja::isHosted()) {
$response = $this->storecove->updateLegalEntity($data['legal_entity_id'], $data);
if (is_array($response)) {
return $response;
}
return $this->handleResponseError($response);
}
return $this->remoteRequest('/api/einvoice/peppol/update', $data);
}
public function addAdditionalTaxIdentifier(array $data): array
{
$data = [
...$data,
'classification' => $this->company->settings->classification,
'legal_entity_id' => $this->company->legal_entity_id,
];
if (Ninja::isHosted()) {
$scheme = $this->storecove->router->resolveRouting($data['country'], $data['classification']);
$response = $this->storecove->addAdditionalTaxIdentifier($data['legal_entity_id'], $data['vat_number'], $scheme);
if (is_array($response)) {
return $response;
}
return $this->handleResponseError($response);
}
return $this->remoteRequest('/api/einvoice/peppol/add_additional_legal_identifier', $data);
}
/**
* handleResponseError
*
@ -63,7 +149,6 @@ class StorecoveProxy
*/
private function handleResponseError($response): array
{
$error = [
'status' => 'error',
'message' => 'Unknown error occurred',
@ -92,24 +177,25 @@ class StorecoveProxy
$error['message'] = 'Resource not found';
}
nlog(['Storecove API Error' => [
'status' => $response->status(),
'body' => $response->body(),
'error' => $error
]]);
nlog([
'Storecove API Error' => [
'status' => $response->status(),
'body' => $response->body(),
'error' => $error,
],
]);
return $error;
}
private function remoteRequest(string $uri, array $payload =[]): array
private function remoteRequest(string $uri, array $payload = []): array
{
$response = Http::baseUrl(config('ninja.hosted_ninja_url'))
->withHeaders($this->getHeaders())
->post($uri, $payload);
if($response->successful())
if ($response->successful())
return $response->json();
return $this->handleResponseError($response);
@ -117,7 +203,7 @@ class StorecoveProxy
private function getHeaders(): array
{
return [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
@ -126,4 +212,4 @@ class StorecoveProxy
];
}
}
}

View File

@ -239,6 +239,7 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
Route::post('einvoice/peppol/setup', [EInvoicePeppolController::class, 'setup'])->name('einvoice.peppol.setup');
Route::post('einvoice/peppol/disconnect', [EInvoicePeppolController::class, 'disconnect'])->name('einvoice.peppol.disconnect');
Route::put('einvoice/peppol/update', [EInvoicePeppolController::class, 'updateLegalEntity'])->name('einvoice.peppol.update_legal_entity');
Route::post('einvoice/peppol/add_additional_legal_identifier', [EInvoicePeppolController::class, 'addAdditionalTaxIdentifier'])->name('einvoice.peppol.add_additional_legal_identifier');
Route::post('einvoice/token/update', EInvoiceTokenController::class)->name('einvoice.token.update');
Route::get('einvoice/quota', [EInvoiceController::class, 'quota'])->name('einvoice.quota');