diff --git a/app/Http/Controllers/EInvoicePeppolController.php b/app/Http/Controllers/EInvoicePeppolController.php index 6a905af964..296268fe5f 100644 --- a/app/Http/Controllers/EInvoicePeppolController.php +++ b/app/Http/Controllers/EInvoicePeppolController.php @@ -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)) { diff --git a/app/Http/Requests/EInvoice/Peppol/AddTaxIdentifierRequest.php b/app/Http/Requests/EInvoice/Peppol/AddTaxIdentifierRequest.php index 8831eb259c..727ac69d76 100644 --- a/app/Http/Requests/EInvoice/Peppol/AddTaxIdentifierRequest.php +++ b/app/Http/Requests/EInvoice/Peppol/AddTaxIdentifierRequest.php @@ -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); diff --git a/app/Services/EDocument/Gateway/Storecove/Storecove.php b/app/Services/EDocument/Gateway/Storecove/Storecove.php index 2c22418f6f..5f67399d5a 100644 --- a/app/Services/EDocument/Gateway/Storecove/Storecove.php +++ b/app/Services/EDocument/Gateway/Storecove/Storecove.php @@ -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; } /** diff --git a/app/Services/EDocument/Gateway/Storecove/StorecoveProxy.php b/app/Services/EDocument/Gateway/Storecove/StorecoveProxy.php index a4390f8268..34a5f48758 100644 --- a/app/Services/EDocument/Gateway/Storecove/StorecoveProxy.php +++ b/app/Services/EDocument/Gateway/Storecove/StorecoveProxy.php @@ -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 ]; } -} \ No newline at end of file +} diff --git a/routes/api.php b/routes/api.php index 04d4ae12ce..6990e463ba 100644 --- a/routes/api.php +++ b/routes/api.php @@ -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');