Merge branch 'v5-develop' into v5-stable

This commit is contained in:
David Bomba 2022-10-15 09:22:40 +11:00
commit 25967124c1
34 changed files with 226112 additions and 225270 deletions

View File

@ -1 +1 @@
5.5.31
5.5.32

View File

@ -712,14 +712,23 @@ class CheckData extends Command
->pluck('p')
->first();
$over_payment = $over_payment*-1;
if(floatval($over_payment) == floatval($client->balance)){
}
else {
$this->logMessage("# {$client->id} # {$client->name} {$client->balance} is invalid should be {$over_payment}");
}
if($this->option('client_balance') && (floatval($over_payment) != floatval($client->balance) )){
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to 0");
$client->balance = $over_payment * -1;
$client->balance = $over_payment;
$client->save();
}

View File

@ -437,7 +437,7 @@ class CreateTestData extends Command
'company_id' => $client->company->id,
]);
Document::factory()->count(50)->create([
Document::factory()->count(5)->create([
'user_id' => $client->user->id,
'company_id' => $client->company_id,
'documentable_type' => Vendor::class,

View File

@ -25,9 +25,9 @@ class RecurringInvoiceToInvoiceFactory
$invoice->discount = $recurring_invoice->discount;
$invoice->is_amount_discount = $recurring_invoice->is_amount_discount;
$invoice->po_number = $recurring_invoice->po_number;
$invoice->footer = self::tranformObject($recurring_invoice->footer, $client);
$invoice->terms = self::tranformObject($recurring_invoice->terms, $client);
$invoice->public_notes = self::tranformObject($recurring_invoice->public_notes, $client);
$invoice->footer = $recurring_invoice->footer ? self::tranformObject($recurring_invoice->footer, $client) : null;
$invoice->terms = $recurring_invoice->terms ? self::tranformObject($recurring_invoice->terms, $client) : null;
$invoice->public_notes = $recurring_invoice->public_notes ? self::tranformObject($recurring_invoice->public_notes, $client) : null;
$invoice->private_notes = $recurring_invoice->private_notes;
$invoice->is_deleted = $recurring_invoice->is_deleted;
$invoice->line_items = self::transformItems($recurring_invoice, $client);

View File

@ -28,7 +28,7 @@ class ClientFilters extends QueryFilters
* @param string $name
* @return Builder
*/
public function name(string $name): Builder
public function name(string $name = ''): Builder
{
if(strlen($name) >=1)
return $this->builder->where('name', 'like', '%'.$name.'%');

View File

@ -640,7 +640,14 @@ class ClientController extends BaseController
{
//delete all documents
$client->documents->each(function ($document) {
try{
Storage::disk(config('filesystems.default'))->delete($document->url);
}
catch(\Exception $e){
nlog($e->getMessage());
}
});
//force delete the client

View File

@ -36,6 +36,7 @@ class SubdomainController extends BaseController
'lb',
'shopify',
'beta',
'prometh'
];
public function __construct()

View File

@ -51,7 +51,7 @@ class ClientLedgerBalanceUpdate implements ShouldQueue
MultiDB::setDb($this->company->db);
CompanyLedger::where('balance', 0)->where('client_id', $this->client->id)->cursor()->each(function ($company_ledger) {
CompanyLedger::where('balance', 0)->where('client_id', $this->client->id)->orderBy('updated_at', 'ASC')->cursor()->each(function ($company_ledger) {
if ($company_ledger->balance > 0) {
return;
}

View File

@ -42,6 +42,7 @@ class CompanyGateway extends BaseModel
'require_postal_code',
'require_client_phone',
'require_contact_name',
'require_contact_email',
'update_details',
'config',
'fees_and_limits',

View File

@ -288,11 +288,21 @@ class Credit extends BaseModel
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
try {
$file_exists = Storage::disk(config('filesystems.default'))->exists($file_path);
} catch (\Exception $e) {
nlog($e->getMessage());
}
if ($file_exists) {
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
if (Storage::disk('public')->exists($file_path)) {
return Storage::disk('public')->{$type}($file_path);
}
$file_path = (new CreateEntityPdf($invitation))->handle();
return Storage::disk('public')->{$type}($file_path);

View File

@ -451,6 +451,17 @@ class Invoice extends BaseModel
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
try {
$file_exists = Storage::disk(config('filesystems.default'))->exists($file_path);
} catch (\Exception $e) {
nlog($e->getMessage());
}
if ($file_exists) {
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
try {
$file_exists = Storage::disk('public')->exists($file_path);
} catch (\Exception $e) {

View File

@ -247,6 +247,16 @@ class Quote extends BaseModel
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
try {
$file_exists = Storage::disk(config('filesystems.default'))->exists($file_path);
} catch (\Exception $e) {
nlog($e->getMessage());
}
if ($file_exists) {
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
if (Storage::disk('public')->exists($file_path)) {
return Storage::disk('public')->{$type}($file_path);
}

View File

@ -41,6 +41,8 @@ class SystemLog extends Model
const CATEGORY_SECURITY = 5;
const CATEGORY_LOG = 6;
/* Event IDs*/
const EVENT_PAYMENT_RECONCILIATION_FAILURE = 10;
@ -127,6 +129,8 @@ class SystemLog extends Model
const TYPE_LOGIN_FAILURE = 801;
const TYPE_GENERIC = 900;
protected $fillable = [
'client_id',
'company_id',

View File

@ -78,6 +78,7 @@ class Charge
'payment_method' => $cgt->token,
'customer' => $cgt->gateway_customer_reference,
'confirm' => true,
// 'off_session' => true,
'description' => $description,
'metadata' => [
'payment_hash' => $payment_hash->hash,

View File

@ -398,7 +398,7 @@ class StripePaymentDriver extends BaseDriver
{
$this->init();
$params = [];
$params = ['usage' => 'off_session'];
$meta = $this->stripe_connect_auth;
return SetupIntent::create($params, $meta);

View File

@ -268,6 +268,10 @@ class InvoiceService
return $this;
}
//12-10-2022
if($this->invoice->partial > 0 && !$this->invoice->partial_due_date)
$this->invoice->partial_due_date = Carbon::parse($this->invoice->date)->addDays($this->invoice->client->getSetting('payment_terms'));
else
$this->invoice->due_date = Carbon::parse($this->invoice->date)->addDays($this->invoice->client->getSetting('payment_terms'));
return $this;
@ -309,7 +313,7 @@ class InvoiceService
} elseif ($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount) {
$this->invoice->status_id = Invoice::STATUS_PARTIAL;
}
elseif ($this->invoice->balance < 0) {
elseif ($this->invoice->balance > 0) {
$this->invoice->status_id = Invoice::STATUS_SENT;
}

View File

@ -28,6 +28,7 @@ class UpdateReminder extends AbstractService
$this->settings = $settings;
}
/* We only support setting reminders based on the due date, not the partial due date */
public function run()
{
if (! $this->settings) {

View File

@ -16,10 +16,12 @@ use App\Factory\CreditFactory;
use App\Factory\InvoiceItemFactory;
use App\Jobs\Ninja\TransactionLog;
use App\Jobs\Payment\EmailRefundPayment;
use App\Jobs\Util\SystemLogger;
use App\Models\Activity;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\SystemLog;
use App\Models\TransactionEvent;
use App\Repositories\ActivityRepository;
use App\Utils\Ninja;
@ -77,6 +79,11 @@ class RefundPayment
TransactionLog::dispatch(TransactionEvent::PAYMENT_REFUND, $transaction, $this->payment->company->db);
$notes = ctrans('texts.refunded') . " : {$this->total_refund} - " . ctrans('texts.gateway_refund') . " : ";
$notes .= $this->refund_data['gateway_refund'] !== false ? ctrans('texts.yes') : ctrans('texts.no');
$this->createActivity($notes);
return $this->payment;
}
@ -94,8 +101,6 @@ class RefundPayment
$this->payment->refunded += $this->total_refund;
$this->createActivity($this->payment);
if ($response['success'] == false) {
$this->payment->save();
@ -129,7 +134,7 @@ class RefundPayment
$fields->company_id = $this->payment->company_id;
$fields->activity_type_id = Activity::REFUNDED_PAYMENT;
// $fields->credit_id = $this->credit_note->id; // TODO
$fields->notes = json_encode($notes);
$fields->notes = $notes;
if (isset($this->refund_data['invoices'])) {
foreach ($this->refund_data['invoices'] as $invoice) {

View File

@ -150,7 +150,7 @@ class HtmlEngine
$data['$number'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.invoice_number')];
$data['$invoice'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.invoice_number')];
$data['$number_short'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.invoice_number_short')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->client) ?: '', 'label' => ctrans('texts.invoice_terms')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.invoice_terms')];
$data['$terms'] = &$data['$entity.terms'];
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_invoice').'</a>', 'label' => ctrans('texts.view_invoice')];
$data['$viewLink'] = &$data['$view_link'];
@ -185,7 +185,7 @@ class HtmlEngine
$data['$entity'] = ['value' => '', 'label' => ctrans('texts.quote')];
$data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number')];
$data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number_short')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->client) ?: '', 'label' => ctrans('texts.quote_terms')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.quote_terms')];
$data['$terms'] = &$data['$entity.terms'];
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_quote').'</a>', 'label' => ctrans('texts.view_quote')];
$data['$viewLink'] = &$data['$view_link'];
@ -210,7 +210,7 @@ class HtmlEngine
$data['$entity'] = ['value' => '', 'label' => ctrans('texts.credit')];
$data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number')];
$data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number_short')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->client) ?: '', 'label' => ctrans('texts.credit_terms')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.credit_terms')];
$data['$terms'] = &$data['$entity.terms'];
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_credit').'</a>', 'label' => ctrans('texts.view_credit')];
$data['$viewButton'] = &$data['$view_link'];
@ -303,7 +303,7 @@ class HtmlEngine
$data['$invoice.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
$data['$invoice.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
$data['$invoice.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice4', $this->entity->custom_value4, $this->client) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
$data['$invoice.public_notes'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->public_notes), $this->client) ?: '', 'label' => ctrans('texts.public_notes')];
$data['$invoice.public_notes'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->public_notes ?: ''), $this->client) ?: '', 'label' => ctrans('texts.public_notes')];
$data['$entity.public_notes'] = &$data['$invoice.public_notes'];
$data['$public_notes'] = &$data['$invoice.public_notes'];
$data['$notes'] = &$data['$public_notes'];
@ -535,7 +535,7 @@ class HtmlEngine
$data['$description'] = ['value' => '', 'label' => ctrans('texts.description')];
//$data['$entity_footer'] = ['value' => $this->client->getSetting("{$this->entity_string}_footer"), 'label' => ''];
$data['$entity_footer'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->footer), $this->client), 'label' => ''];
$data['$entity_footer'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->footer ?: ''), $this->client), 'label' => ''];
$data['$footer'] = &$data['$entity_footer'];
$data['$page_size'] = ['value' => $this->settings->page_size, 'label' => ''];

View File

@ -300,10 +300,10 @@ trait MakesInvoiceValues
$data[$key][$table_type.'.notes'] = Helpers::processReservedKeywords($item->notes, $entity);
$data[$key][$table_type.'.description'] = Helpers::processReservedKeywords($item->notes, $entity);
$data[$key][$table_type.".{$_table_type}1"] = strlen($item->custom_value1) > 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}1", $item->custom_value1, $entity) : '';
$data[$key][$table_type.".{$_table_type}2"] = strlen($item->custom_value2) > 2 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}2", $item->custom_value2, $entity) : '';
$data[$key][$table_type.".{$_table_type}3"] = strlen($item->custom_value3) > 3 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}3", $item->custom_value3, $entity) : '';
$data[$key][$table_type.".{$_table_type}4"] = strlen($item->custom_value4) > 4 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}4", $item->custom_value4, $entity) : '';
$data[$key][$table_type.".{$_table_type}1"] = strlen($item->custom_value1) >= 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}1", $item->custom_value1, $entity) : '';
$data[$key][$table_type.".{$_table_type}2"] = strlen($item->custom_value2) >= 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}2", $item->custom_value2, $entity) : '';
$data[$key][$table_type.".{$_table_type}3"] = strlen($item->custom_value3) >= 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}3", $item->custom_value3, $entity) : '';
$data[$key][$table_type.".{$_table_type}4"] = strlen($item->custom_value4) >= 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}4", $item->custom_value4, $entity) : '';
if ($item->quantity > 0 || $item->cost > 0) {
$data[$key][$table_type.'.quantity'] = Number::formatValueNoTrailingZeroes($item->quantity, $entity_currency);

View File

@ -40,7 +40,7 @@ return [
/*
* Route for accessing parsed swagger annotations.
*/
'docs' => 'docs',
'docs' => 'swagger-docs-that-should-be-inaccessible',
/*
* Route for Oauth2 authentication callback.

View File

@ -43,6 +43,7 @@ return [
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'local_domain' => env('MAIL_EHLO_DOMAIN'),
'verify_peer' => env('MAIL_VERIFY_PEER', true),
],
'ses' => [

View File

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.5.31',
'app_tag' => '5.5.31',
'app_version' => '5.5.32',
'app_tag' => '5.5.32',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -0,0 +1,45 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('documents', function (Blueprint $table) {
$table->index(['documentable_id', 'documentable_type', 'deleted_at']);
});
Schema::table('expenses', function (Blueprint $table) {
$table->index(['invoice_id', 'deleted_at']);
});
Schema::table('company_tokens', function (Blueprint $table) {
$table->dropIndex('company_tokens_token_index');
$table->index(['token','deleted_at']);
});
Schema::table('invoice_invitations', function (Blueprint $table) {
$table->dropIndex('invoice_invitations_key_index');
$table->index(['key','deleted_at']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
};

136
preload.php Normal file
View File

@ -0,0 +1,136 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
class Preloader
{
private array $ignores = [];
private static int $count = 0;
private array $paths;
private array $fileMap;
public function __construct(string ...$paths)
{
$this->paths = $paths;
// We'll use composer's classmap
// to easily find which classes to autoload,
// based on their filename
$classMap = require __DIR__ . '/vendor/composer/autoload_classmap.php';
$this->fileMap = array_flip($classMap);
}
public function paths(string ...$paths): Preloader
{
$this->paths = array_merge(
$this->paths,
$paths
);
return $this;
}
public function ignore(string ...$names): Preloader
{
$this->ignores = array_merge(
$this->ignores,
$names
);
return $this;
}
public function load(): void
{
// We'll loop over all registered paths
// and load them one by one
foreach ($this->paths as $path) {
$this->loadPath(rtrim($path, '/'));
}
$count = self::$count;
echo "[Preloader] Preloaded {$count} classes" . PHP_EOL;
}
private function loadPath(string $path): void
{
// If the current path is a directory,
// we'll load all files in it
if (is_dir($path)) {
$this->loadDir($path);
return;
}
// Otherwise we'll just load this one file
$this->loadFile($path);
}
private function loadDir(string $path): void
{
$handle = opendir($path);
// We'll loop over all files and directories
// in the current path,
// and load them one by one
while ($file = readdir($handle)) {
if (in_array($file, ['.', '..'])) {
continue;
}
$this->loadPath("{$path}/{$file}");
}
closedir($handle);
}
private function loadFile(string $path): void
{
// We resolve the classname from composer's autoload mapping
$class = $this->fileMap[$path] ?? null;
// And use it to make sure the class shouldn't be ignored
if ($this->shouldIgnore($class)) {
return;
}
// Finally we require the path,
// causing all its dependencies to be loaded as well
require_once($path);
self::$count++;
echo "[Preloader] Preloaded `{$class}`" . PHP_EOL;
}
private function shouldIgnore(?string $name): bool
{
if ($name === null) {
return true;
}
foreach ($this->ignores as $ignore) {
if (strpos($name, $ignore) === 0) {
return true;
}
}
return false;
}
}
(new Preloader())
->paths(__DIR__ . '/vendor/laravel')
->ignore(
\Illuminate\Filesystem\Cache::class,
\Illuminate\Log\LogManager::class,
\Illuminate\Http\Testing\File::class,
\Illuminate\Http\UploadedFile::class,
\Illuminate\Support\Carbon::class,
)
->load();

View File

@ -4,9 +4,9 @@ const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"main.dart.js": "284cb064303ead96ba3479bfebd29d06",
"main.dart.js": "221c5c0123d1312d9dd4625143c4e890",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"/": "94e51cb94d703239a16eb20a46a53cfa",
"/": "3bb8121d93d3f214ecd76dd2a5db3bad",
"flutter.js": "f85e6fb278b0fd20c349186fb46ae36d",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"canvaskit/canvaskit.js": "2bc454a691c631b07a9307ac4ca47797",
@ -299,7 +299,7 @@ const RESOURCES = {
"assets/assets/google_fonts/Roboto-Regular.ttf": "8a36205bd9b83e03af0591a004bc97f4",
"assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b",
"assets/NOTICES": "1a34e70168d56fad075adfb4bdbb20eb",
"version.json": "fe3b943c516fb905ec25a31e0878c315",
"version.json": "5e61b05ca3d51910bc4464df86bcda1a",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed"
};

223880
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

217110
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"app_name":"invoiceninja_flutter","version":"5.0.97","build_number":"97","package_name":"invoiceninja_flutter"}
{"app_name":"invoiceninja_flutter","version":"5.0.98","build_number":"98","package_name":"invoiceninja_flutter"}

View File

@ -77,11 +77,6 @@
{{ ctrans('texts.name') }}
</span>
</th>
<th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider">
<span role="button" wire:click="sortBy('type')" class="cursor-pointer">
{{ ctrans('texts.type') }}
</span>
</th>
<th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider">
<span role="button" wire:click="sortBy('size')" class="cursor-pointer">
{{ ctrans('texts.size') }}
@ -102,9 +97,7 @@
<td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500">
{{ Illuminate\Support\Str::limit($document->name, 20) }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500">
{{ App\Models\Document::$types[$document->type]['mime'] }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500">
{{ $document->size / 1000 }} kB
</td>

View File

@ -24,14 +24,6 @@
{{ Illuminate\Support\Str::limit($document->name, 40) }}
</dd>
</div>
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.type') }}
</dt>
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{{ App\Models\Document::$types[$document->type]['mime'] }}
</dd>
</div>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.hash') }}