1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-15 15:42:51 +01:00
invoiceninja/app/Services/EDocument/Imports/MindeeEDocument.php

189 lines
8.1 KiB
PHP
Raw Normal View History

2024-06-22 18:52:25 +02:00
<?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\Factory\ExpenseFactory;
2024-06-24 16:30:00 +02:00
use App\Factory\VendorContactFactory;
2024-06-22 18:52:25 +02:00
use App\Factory\VendorFactory;
2024-08-28 09:29:11 +02:00
use App\Models\Company;
2024-06-22 18:52:25 +02:00
use App\Models\Country;
use App\Models\Currency;
use App\Models\Expense;
use App\Models\Vendor;
2024-06-24 16:30:00 +02:00
use App\Models\VendorContact;
2024-06-22 18:52:25 +02:00
use App\Services\AbstractService;
use App\Utils\TempFile;
use App\Utils\Traits\SavesDocuments;
2024-06-23 09:32:51 +02:00
use Cache;
2024-06-22 18:52:25 +02:00
use Exception;
use Mindee\Client;
use Mindee\Product\Invoice\InvoiceV4;
use Illuminate\Http\UploadedFile;
class MindeeEDocument extends AbstractService
{
use SavesDocuments;
/**
* @throws Exception
*/
2024-08-28 09:29:11 +02:00
public function __construct(public UploadedFile $file, public Company $company)
2024-06-22 18:52:25 +02:00
{
# curl -X POST http://localhost:8000/api/v1/edocument/upload -H "Content-Type: multipart/form-data" -H "X-API-TOKEN: 7tdDdkz987H3AYIWhNGXy8jTjJIoDhkAclCDLE26cTCj1KYX7EBHC66VEitJwWhn" -H "X-Requested-With: XMLHttpRequest" -F _method=PUT -F documents[]=@einvoice.xml
}
/**
* @throws Exception
*/
public function run(): Expense
{
$api_key = config('services.mindee.api_key');
2024-06-22 18:52:25 +02:00
if (!$api_key)
throw new Exception('Mindee API key not configured');
2024-06-23 09:32:51 +02:00
$this->checkLimits();
2024-06-22 18:52:25 +02:00
2024-06-23 09:32:51 +02:00
// perform parsing
2024-06-22 18:52:25 +02:00
$mindeeClient = new Client($api_key);
2024-06-24 16:30:00 +02:00
$inputSource = $mindeeClient->sourceFromBytes($this->file->get(), $this->file->getClientOriginalName());
2024-06-22 19:58:55 +02:00
$result = $mindeeClient->parse(InvoiceV4::class, $inputSource);
2024-06-23 09:32:51 +02:00
$this->incrementRequestCounts();
2024-06-22 19:58:55 +02:00
/** @var \Mindee\Product\Invoice\InvoiceV4Document $prediction */
$prediction = $result->document->inference->prediction;
2024-06-24 16:30:00 +02:00
if ($prediction->documentType->value !== 'INVOICE')
2024-06-22 20:57:11 +02:00
throw new Exception('Unsupported document type');
2024-06-22 19:58:55 +02:00
$grandTotalAmount = $prediction->totalAmount->value;
$documentno = $prediction->invoiceNumber->value;
$documentdate = $prediction->date->value;
$invoiceCurrency = $prediction->locale->currency;
$country = $prediction->locale->country;
2024-06-22 18:52:25 +02:00
$expense = Expense::query()
->where('company_id', $this->company->id)
->where('amount', $grandTotalAmount)
->where("transaction_reference", $documentno)
->whereDate("date", $documentdate)
->first();
if (!$expense) {
2024-06-22 18:52:25 +02:00
// The document does not exist as an expense
// Handle accordingly
/** @var \App\Models\Currency $currency */
$currency = app('currencies')->first(function ($c) use ($invoiceCurrency){
/** @var \App\Models\Currency $c */
return $c->code == $invoiceCurrency;
});
2024-08-28 09:29:11 +02:00
$expense = ExpenseFactory::create($this->company->id, $this->company->owner()->id);
2024-06-22 18:52:25 +02:00
$expense->date = $documentdate;
$expense->public_notes = $documentno;
$expense->currency_id = $currency ? $currency->id : $this->company->settings->currency_id;
2024-06-22 18:52:25 +02:00
$expense->save();
2024-06-23 09:32:51 +02:00
$this->saveDocuments([
$this->file,
TempFile::UploadedFileFromRaw(strval($result->document), $documentno . "_mindee_orc_result.txt", "text/plain")
], $expense);
// $expense->saveQuietly();
2024-06-22 18:52:25 +02:00
$expense->uses_inclusive_taxes = True;
$expense->amount = $grandTotalAmount;
$counter = 1;
2024-06-22 20:57:11 +02:00
foreach ($prediction->taxes as $taxesElem) {
$expense->{"tax_amount{$counter}"} = $taxesElem->amount;
$expense->{"tax_rate{$counter}"} = $taxesElem->rate;
2024-06-22 20:57:11 +02:00
$counter++;
2024-06-22 18:52:25 +02:00
}
/** @var \App\Models\VendorContact $vendor_contact */
$vendor_contact = VendorContact::query()->where("company_id", $this->company->id)->where("email", $prediction->supplierEmail)->first();
/** @var \App\Models\Vendor|null $vendor */
$vendor = $vendor_contact ? $vendor_contact->vendor : Vendor::query()->where("company_id", $this->company->id)->where("name", $prediction->supplierName)->first();
2024-06-24 16:30:00 +02:00
if ($vendor) {
2024-06-22 18:52:25 +02:00
$expense->vendor_id = $vendor->id;
} else {
2024-08-28 09:29:11 +02:00
$vendor = VendorFactory::create($this->company->id, $this->company->owner()->id);
2024-06-22 20:57:11 +02:00
$vendor->name = $prediction->supplierName;
2024-06-23 09:32:51 +02:00
$vendor->currency_id = Currency::whereCode($invoiceCurrency)->first()?->id;
2024-06-22 19:58:55 +02:00
$vendor->phone = $prediction->supplierPhoneNumber;
2024-06-23 09:32:51 +02:00
// $vendor->address1 = $address_1; // TODO: we only have the full address string from mindee returned
2024-06-22 20:57:11 +02:00
// $vendor->address2 = $address_2;
// $vendor->city = $city;
// $vendor->postal_code = $postcode;
/** @var ?\App\Models\Country $country */
$country = app('countries')->first(function ($c) use ($country) {
/** @var \App\Models\Country $c */
return $c->iso_3166_2 == $country || $c->iso_3166_3 == $country;
});
2024-06-24 16:30:00 +02:00
if ($country)
$vendor->country_id = $country->id;
2024-06-22 18:52:25 +02:00
$vendor->save();
2024-06-24 16:30:00 +02:00
if (strlen($prediction->supplierEmail ?? '') > 2) {
2024-08-28 09:29:11 +02:00
$vendor_contact = VendorContactFactory::create($this->company->id, $this->company->owner()->id);
2024-06-24 16:30:00 +02:00
$vendor_contact->vendor_id = $vendor->id;
$vendor_contact->email = $prediction->supplierEmail;
$vendor_contact->save();
}
2024-06-22 18:52:25 +02:00
$expense->vendor_id = $vendor->id;
}
$expense->transaction_reference = $documentno;
} else {
// The document exists as an expense
// Handle accordingly
2024-08-28 09:53:38 +02:00
nlog("Mindee: Document already exists");
$expense->private_notes = $expense->private_notes . ctrans("texts.edocument_import_already_exists", ["date" => now()->format('Y-m-d')]);
2024-06-22 18:52:25 +02:00
}
2024-06-22 18:52:25 +02:00
$expense->save();
return $expense;
}
2024-06-23 09:32:51 +02:00
private function checkLimits()
{
Cache::add('mindeeTotalDailyRequests', 0, now()->endOfDay());
Cache::add('mindeeTotalMonthlyRequests', 0, now()->endOfMonth());
2024-08-28 09:29:11 +02:00
Cache::add('mindeeAccountDailyRequests' . $this->company->account->id, 0, now()->endOfDay());
Cache::add('mindeeAccountMonthlyRequests' . $this->company->account->id, 0, now()->endOfMonth());
2024-06-23 09:32:51 +02:00
if (config('services.mindee.daily_limit') != 0 && Cache::get('mindeeTotalDailyRequests') > config('services.mindee.daily_limit'))
throw new Exception('Mindee daily limit reached');
if (config('services.mindee.monthly_limit') != 0 && Cache::get('mindeeTotalMonthlyRequests') > config('services.mindee.monthly_limit'))
throw new Exception('Mindee monthly limit reached');
2024-08-28 09:29:11 +02:00
if (config('services.mindee.account_daily_limit') != 0 && Cache::get('mindeeAccountDailyRequests' . $this->company->account->id) > config('services.mindee.account_daily_limit'))
throw new Exception('Mindee daily limit reached for account: ' . $this->company->account->id);
if (config('services.mindee.account_monthly_limit') != 0 && Cache::get('mindeeAccountMonthlyRequests' . $this->company->account->id) > config('services.mindee.account_monthly_limit'))
throw new Exception('Mindee monthly limit reached for account: ' . $this->company->account->id);
2024-06-23 09:32:51 +02:00
}
private function incrementRequestCounts()
{
Cache::increment('mindeeTotalDailyRequests');
Cache::increment('mindeeTotalMonthlyRequests');
2024-08-28 09:29:11 +02:00
Cache::increment('mindeeAccountDailyRequests' . $this->company->account->id);
Cache::increment('mindeeAccountMonthlyRequests' . $this->company->account->id);
2024-06-23 09:32:51 +02:00
}
2024-06-22 18:52:25 +02:00
}