Merge pull request #8797 from turbo124/v5-develop

Updates for search + fixes for gross line totals
This commit is contained in:
David Bomba 2023-09-12 19:52:28 +10:00 committed by GitHub
commit 7ee507e425
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1237 additions and 1000 deletions

View File

@ -12,6 +12,7 @@
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class ReactBuilder extends Command
{
@ -48,6 +49,8 @@ class ReactBuilder extends Command
{
$includes = '';
Storage::makeDirectory(public_path('react'));
$directoryIterator = new \RecursiveDirectoryIterator(public_path('react'), \RecursiveDirectoryIterator::SKIP_DOTS);
foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {

View File

@ -843,6 +843,23 @@ class CompanySettings extends BaseSettings
return $notification;
}
/**
* Stubs the notification defaults
*
* @return stdClass
*/
public static function notificationAdminDefaults() :stdClass
{
$notification = new stdClass;
$notification->email = [];
$notification->email = ['invoice_sent_all'];
return $notification;
}
/**
* Defines entity variables for PDF generation
*

View File

@ -11,20 +11,29 @@
namespace App\Export\CSV;
use App\Models\Quote;
use App\Utils\Number;
use App\Models\Client;
use App\Models\Credit;
use App\Utils\Helpers;
use App\Models\Company;
use App\Models\Expense;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Document;
use League\Fractal\Manager;
use App\Models\ClientContact;
use App\Models\PurchaseOrder;
use App\Models\RecurringInvoice;
use Illuminate\Support\Carbon;
use App\Utils\Traits\MakesHash;
use App\Transformers\TaskTransformer;
use App\Transformers\PaymentTransformer;
use Illuminate\Database\Eloquent\Builder;
use League\Fractal\Serializer\ArraySerializer;
use App\Models\Product;
use App\Models\Task;
use App\Models\Vendor;
class BaseExport
{
@ -56,7 +65,7 @@ class BaseExport
'id_number' => 'vendor.id_number',
'name' => 'vendor.name',
'number' => 'vendor.number',
'client_phone' => 'vendor.phone',
'phone' => 'vendor.phone',
'postal_code' => 'vendor.postal_code',
'private_notes' => 'vendor.private_notes',
'public_notes' => 'vendor.public_notes',
@ -229,8 +238,8 @@ class BaseExport
];
protected array $product_report_keys = [
'project' => 'project_id',
'vendor' => 'vendor_id',
// 'project' => 'project_id',
// 'vendor' => 'vendor_id',
'custom_value1' => 'custom_value1',
'custom_value2' => 'custom_value2',
'custom_value3' => 'custom_value3',
@ -246,6 +255,10 @@ class BaseExport
'tax_name1' => 'tax_name1',
'tax_name2' => 'tax_name2',
'tax_name3' => 'tax_name3',
'image' => 'product_image',
'tax_category' => 'tax_id',
'max_quantity' => 'max_quantity',
'in_stock_quantity' => 'in_stock_quantity',
];
protected array $item_report_keys = [
@ -644,7 +657,7 @@ class BaseExport
// nlog("searching for {$column}");
$transformed_invoice = false;
if($transformer instanceof PaymentTransformer) {
if($transformer instanceof PaymentTransformer && ($entity->invoices ?? false)) {
$transformed_invoices = $transformer->includeInvoices($entity);
$manager = new Manager();
@ -666,7 +679,7 @@ class BaseExport
}
if($transformer instanceof TaskTransformer) {
if($transformer instanceof TaskTransformer && ($entity->invoice ?? false)) {
$transformed_invoice = $transformer->includeInvoice($entity);
if(!$transformed_invoice)
@ -986,6 +999,7 @@ class BaseExport
$key = str_replace('payment.', '', $key);
$key = str_replace('expense.', '', $key);
$key = str_replace('product.', '', $key);
$key = str_replace('task.', '', $key);
if(stripos($value, 'custom_value') !== false)
{
@ -1009,7 +1023,7 @@ class BaseExport
$custom_field_string = strlen($helper->makeCustomField($this->company->custom_fields, $entity)) > 1 ? $helper->makeCustomField($this->company->custom_fields, $entity) : ctrans("texts.{$parts[1]}");
$header[] = ctrans("texts.{$parts[0]}") . " " . $custom_field_string;
}
elseif(count($parts) == 2 && in_array(substr($original_key, 0, -1), ['credit','quote','invoice','purchase_order','recurring_invoice'])){
elseif(count($parts) == 2 && in_array(substr($original_key, 0, -1), ['credit','quote','invoice','purchase_order','recurring_invoice','task'])){
$custom_field_string = strlen($helper->makeCustomField($this->company->custom_fields, "product".substr($original_key,-1))) > 1 ? $helper->makeCustomField($this->company->custom_fields, "product".substr($original_key,-1)) : ctrans("texts.{$parts[1]}");
$header[] = ctrans("texts.{$parts[0]}") . " " . $custom_field_string;
}
@ -1028,4 +1042,102 @@ class BaseExport
return $header;
}
public function processMetaData(array $row, $resource): array
{
$class = get_class($resource);
$entity = '';
match ($class) {
Invoice::class => $entity = 'invoice',
RecurringInvoice::class => $entity = 'recurring_invoice',
Quote::class => $entity = 'quote',
Credit::class => $entity = 'credit',
Expense::class => $entity = 'expense',
Document::class => $entity = 'document',
ClientContact::class => $entity = 'contact',
PurchaseOrder::class => $entity = 'purchase_order',
Payment::class => $entity = 'payment',
Product::class => $entity = 'product',
Task::class => $entity = 'task',
Vendor::class => $entity = 'vendor',
default => $entity = 'invoice',
};
$clean_row = [];
foreach (array_values($this->input['report_keys']) as $key => $value) {
$report_keys = explode(".", $value);
$column_key = $value;
if($value == 'product_image') {
$column_key = 'image';
$value = 'image';
}
if($value == 'tax_id') {
$column_key = 'tax_category';
$value = 'tax_category';
}
$clean_row[$key]['entity'] = $report_keys[0];
$clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0];
$clean_row[$key]['hashed_id'] = $report_keys[0] == $entity ? null : $resource->{$report_keys[0]}->hashed_id ?? null;
$clean_row[$key]['value'] = $row[$column_key];
$clean_row[$key]['identifier'] = $value;
$clean_row[$key]['display_value'] = $row[$column_key];
}
nlog($clean_row);
return $clean_row;
}
public function processItemMetaData(array $row, $resource): array
{
$class = get_class($resource);
$entity = '';
match ($class) {
Invoice::class => $entity = 'invoice',
Quote::class => $entity = 'quote',
Credit::class => $entity = 'credit',
Expense::class => $entity = 'expense',
Document::class => $entity = 'document',
ClientContact::class => $entity = 'contact',
PurchaseOrder::class => $entity = 'purchase_order',
default => $entity = 'invoice',
};
$clean_row = [];
foreach (array_values($this->input['report_keys']) as $key => $value) {
$report_keys = explode(".", $value);
$column_key = $value;
if($value == 'type_id' || $value == 'item.type_id')
$column_key = 'type';
if($value == 'tax_id' || $value == 'item.tax_id')
$column_key = 'tax_category';
$clean_row[$key]['entity'] = $report_keys[0];
$clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0];
$clean_row[$key]['hashed_id'] = $report_keys[0] == $entity ? null : $resource->{$report_keys[0]}->hashed_id ?? null;
$clean_row[$key]['value'] = isset($row[$column_key]) ? $row[$column_key] : $row[$report_keys[1]];
$clean_row[$key]['identifier'] = $value;
$clean_row[$key]['display_value'] = isset($row[$column_key]) ? $row[$column_key] : $row[$report_keys[1]];
}
return $clean_row;
}
}

View File

@ -11,15 +11,16 @@
namespace App\Export\CSV;
use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\Company;
use App\Transformers\ClientContactTransformer;
use App\Transformers\ClientTransformer;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use App\Utils\Number;
use App\Models\Client;
use League\Csv\Writer;
use App\Models\Company;
use App\Libraries\MultiDB;
use Illuminate\Support\Facades\App;
use App\Transformers\ClientTransformer;
use Illuminate\Database\Eloquent\Builder;
use App\Transformers\ClientContactTransformer;
class ClientExport extends BaseExport
{
@ -93,7 +94,8 @@ class ClientExport extends BaseExport
$report = $query->cursor()
->map(function ($client) {
return $this->buildRow($client);
$row = $this->buildRow($client);
return $this->processMetaData($row, $client);
})->toArray();
return array_merge(['columns' => $header], $report);
@ -171,6 +173,30 @@ class ClientExport extends BaseExport
return $this->decorateAdvancedFields($client, $entity);
}
public function processMetaData(array $row, $resource): array
{
$clean_row = [];
foreach (array_values($this->input['report_keys']) as $key => $value) {
$report_keys = explode(".", $value);
$column_key = $value;
$clean_row[$key]['entity'] = $report_keys[0];
$clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0];
$clean_row[$key]['hashed_id'] = $report_keys[0] == 'client' ? null : $resource->{$report_keys[0]}->hashed_id ?? null;
$clean_row[$key]['value'] = $row[$column_key];
$clean_row[$key]['identifier'] = $value;
if(in_array($clean_row[$key]['id'], ['paid_to_date', 'balance', 'credit_balance','payment_balance']))
$clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $resource);
else
$clean_row[$key]['display_value'] = $row[$column_key];
}
return $clean_row;
}
private function decorateAdvancedFields(Client $client, array $entity) :array
{
if (in_array('client.user', $this->input['report_keys'])) {

View File

@ -94,7 +94,8 @@ class ContactExport extends BaseExport
$report = $query->cursor()
->map(function ($contact) {
return $this->buildRow($contact);
$row = $this->buildRow($contact);
return $this->processMetaData($row, $contact);
})->toArray();
return array_merge(['columns' => $header], $report);

View File

@ -19,7 +19,7 @@ use App\Models\Company;
use App\Libraries\MultiDB;
use Illuminate\Support\Facades\App;
use App\Transformers\CreditTransformer;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
class CreditExport extends BaseExport
{
@ -56,7 +56,7 @@ class CreditExport extends BaseExport
return array_merge(['columns' => $header], $report);
}
private function processMetaData(array $row, Credit $credit): array
public function processMetaData(array $row, $resource): array
{
$clean_row = [];
foreach (array_values($this->input['report_keys']) as $key => $value) {
@ -66,12 +66,12 @@ class CreditExport extends BaseExport
$column_key = $value;
$clean_row[$key]['entity'] = $report_keys[0];
$clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0];
$clean_row[$key]['hashed_id'] = $report_keys[0] == 'credit' ? null : $credit->{$report_keys[0]}->hashed_id ?? null;
$clean_row[$key]['hashed_id'] = $report_keys[0] == 'credit' ? null : $resource->{$report_keys[0]}->hashed_id ?? null;
$clean_row[$key]['value'] = $row[$column_key];
$clean_row[$key]['identifier'] = $value;
if(in_array($clean_row[$key]['id'], ['paid_to_date','total_taxes','amount', 'balance', 'partial', 'refunded', 'applied','unit_cost','cost','price']))
$clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $credit->client);
$clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $resource->client);
else
$clean_row[$key]['display_value'] = $row[$column_key];

View File

@ -16,7 +16,7 @@ use App\Models\Company;
use App\Models\Document;
use App\Transformers\DocumentTransformer;
use App\Utils\Ninja;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -56,6 +56,7 @@ class DocumentExport extends BaseExport
$report = $query->cursor()
->map(function ($document) {
$row = $this->buildRow($document);
return $this->processMetaData($row, $document);
})->toArray();
return array_merge(['columns' => $header], $report);

View File

@ -49,7 +49,8 @@ class ExpenseExport extends BaseExport
$report = $query->cursor()
->map(function ($resource) {
return $this->buildRow($resource);
$row = $this->buildRow($resource);
return $this->processMetaData($row, $resource);
})->toArray();
return array_merge(['columns' => $header], $report);

View File

@ -20,7 +20,7 @@ use App\Libraries\MultiDB;
use App\Export\CSV\BaseExport;
use Illuminate\Support\Facades\App;
use App\Transformers\InvoiceTransformer;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
class InvoiceExport extends BaseExport
{
@ -78,7 +78,8 @@ class InvoiceExport extends BaseExport
$report = $query->cursor()
->map(function ($resource) {
return $this->buildRow($resource);
$row = $this->buildRow($resource);
return $this->processMetaData($row, $resource);
})->toArray();
return array_merge(['columns' => $header], $report);
@ -94,7 +95,6 @@ class InvoiceExport extends BaseExport
//insert the header
$this->csv->insertOne($this->buildHeader());
$query->cursor()
->each(function ($invoice) {
$this->csv->insertOne($this->buildRow($invoice));

View File

@ -16,7 +16,7 @@ use App\Models\Company;
use App\Models\Invoice;
use App\Transformers\InvoiceTransformer;
use App\Utils\Ninja;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -33,6 +33,8 @@ class InvoiceItemExport extends BaseExport
private array $storage_array = [];
private array $storage_item_array = [];
private array $decorate_keys = [
'client',
'currency_id',
@ -62,7 +64,8 @@ class InvoiceItemExport extends BaseExport
$query = Invoice::query()
->withTrashed()
->with('client')->where('company_id', $this->company->id)
->with('client')
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);
@ -81,12 +84,21 @@ class InvoiceItemExport extends BaseExport
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]];
})->toArray();
$query->cursor()
->each(function ($resource) {
$this->iterateItems($resource);
foreach($this->storage_array as $row) {
$this->storage_item_array[] = $this->processItemMetaData($row, $resource);
}
$this->storage_array = [];
});
return array_merge(['columns' => $header], $this->storage_array);
return array_merge(['columns' => $header], $this->storage_item_array);
}
@ -188,13 +200,7 @@ class InvoiceItemExport extends BaseExport
$entity['tax_category'] = $invoice->taxTypeString($entity['tax_category']);
}
// if($this->force_keys) {
// $entity['client'] = $invoice->client->present()->name();
// $entity['client_id_number'] = $invoice->client->id_number;
// $entity['client_number'] = $invoice->client->number;
// $entity['status'] = $invoice->stringStatus($invoice->status_id);
// }
return $entity;
}
}

View File

@ -16,7 +16,7 @@ use App\Models\Company;
use App\Models\Payment;
use App\Transformers\PaymentTransformer;
use App\Utils\Ninja;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -28,40 +28,6 @@ class PaymentExport extends BaseExport
public Writer $csv;
// public array $entity_keys = [
// 'amount' => 'amount',
// 'applied' => 'applied',
// 'client' => 'client_id',
// 'currency' => 'currency_id',
// 'custom_value1' => 'custom_value1',
// 'custom_value2' => 'custom_value2',
// 'custom_value3' => 'custom_value3',
// 'custom_value4' => 'custom_value4',
// 'date' => 'date',
// 'exchange_currency' => 'exchange_currency_id',
// 'gateway' => 'gateway_type_id',
// 'number' => 'number',
// 'private_notes' => 'private_notes',
// 'project' => 'project_id',
// 'refunded' => 'refunded',
// 'status' => 'status_id',
// 'transaction_reference' => 'transaction_reference',
// 'type' => 'type_id',
// 'vendor' => 'vendor_id',
// 'invoices' => 'invoices',
// ];
// private array $decorate_keys = [
// 'vendor',
// 'status',
// 'project',
// 'client',
// 'currency',
// 'exchange_currency',
// 'type',
// 'invoices',
// ];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -105,12 +71,12 @@ class PaymentExport extends BaseExport
$report = $query->cursor()
->map(function ($resource) {
return $this->buildRow($resource);
$row = $this->buildRow($resource);
return $this->processMetaData($row, $resource);
})->toArray();
return array_merge(['columns' => $header], $report);
}
public function run()

View File

@ -13,11 +13,10 @@ namespace App\Export\CSV;
use App\Libraries\MultiDB;
use App\Models\Company;
use App\Models\Document;
use App\Models\Product;
use App\Transformers\ProductTransformer;
use App\Utils\Ninja;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -48,7 +47,8 @@ class ProductExport extends BaseExport
$report = $query->cursor()
->map(function ($resource) {
return $this->buildRow($resource);
$row = $this->buildRow($resource);
return $this->processMetaData($row, $resource);
})->toArray();
return array_merge(['columns' => $header], $report);

View File

@ -19,7 +19,7 @@ use App\Libraries\MultiDB;
use App\Models\PurchaseOrder;
use Illuminate\Support\Facades\App;
use App\Transformers\PurchaseOrderTransformer;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
class PurchaseOrderExport extends BaseExport
{
@ -119,7 +119,8 @@ class PurchaseOrderExport extends BaseExport
$report = $query->cursor()
->map(function ($resource) {
return $this->buildRow($resource);
$row = $this->buildRow($resource);
return $this->processMetaData($row, $resource);
})->toArray();
return array_merge(['columns' => $header], $report);

View File

@ -11,14 +11,14 @@
namespace App\Export\CSV;
use App\Libraries\MultiDB;
use App\Models\Company;
use App\Models\PurchaseOrder;
use App\Transformers\PurchaseOrderTransformer;
use App\Utils\Ninja;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
use App\Models\Company;
use App\Libraries\MultiDB;
use App\Models\PurchaseOrder;
use Illuminate\Support\Facades\App;
use Illuminate\Database\Eloquent\Builder;
use App\Transformers\PurchaseOrderTransformer;
class PurchaseOrderItemExport extends BaseExport
{
@ -33,6 +33,8 @@ class PurchaseOrderItemExport extends BaseExport
private array $storage_array = [];
private array $storage_item_array = [];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -50,7 +52,6 @@ class PurchaseOrderItemExport extends BaseExport
$t->replace(Ninja::transformTranslations($this->company->settings));
if (count($this->input['report_keys']) == 0) {
// $this->force_keys = true;
$this->input['report_keys'] = array_values($this->mergeItemsKeys('purchase_order_report_keys'));
}
@ -78,9 +79,16 @@ class PurchaseOrderItemExport extends BaseExport
$query->cursor()
->each(function ($resource) {
$this->iterateItems($resource);
foreach($this->storage_array as $row) {
$this->storage_item_array[] = $this->processItemMetaData($row, $resource);
}
$this->storage_array = [];
});
return array_merge(['columns' => $header], $this->storage_array);
return array_merge(['columns' => $header], $this->storage_item_array);
}
public function run()
@ -113,7 +121,7 @@ class PurchaseOrderItemExport extends BaseExport
foreach ($purchase_order->line_items as $item) {
$item_array = [];
foreach (array_values($this->input['report_keys']) as $key) { //items iterator produces item array
foreach (array_values(array_intersect($this->input['report_keys'], $this->item_report_keys)) as $key) { //items iterator produces item array
if (str_contains($key, "item.")) {
@ -190,4 +198,5 @@ class PurchaseOrderItemExport extends BaseExport
return $entity;
}
}

View File

@ -16,7 +16,7 @@ use App\Models\Company;
use App\Models\Quote;
use App\Transformers\QuoteTransformer;
use App\Utils\Ninja;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -80,7 +80,8 @@ class QuoteExport extends BaseExport
$report = $query->cursor()
->map(function ($resource) {
return $this->buildRow($resource);
$row = $this->buildRow($resource);
return $this->processMetaData($row, $resource);
})->toArray();
return array_merge(['columns' => $header], $report);

View File

@ -16,7 +16,7 @@ use App\Models\Company;
use App\Models\Quote;
use App\Transformers\QuoteTransformer;
use App\Utils\Ninja;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -29,7 +29,8 @@ class QuoteItemExport extends BaseExport
public Writer $csv;
private array $storage_array;
private array $storage_array = [];
private array $storage_item_array = [];
private array $decorate_keys = [
'client',
@ -80,9 +81,17 @@ class QuoteItemExport extends BaseExport
$query->cursor()
->each(function ($resource) {
$this->iterateItems($resource);
foreach($this->storage_array as $row) {
$this->storage_item_array[] = $this->processItemMetaData($row, $resource);
}
$this->storage_array = [];
});
return array_merge(['columns' => $header], $this->storage_array);
return array_merge(['columns' => $header], $this->storage_item_array);
}

View File

@ -16,6 +16,7 @@ use App\Models\Company;
use App\Models\RecurringInvoice;
use App\Transformers\RecurringInvoiceTransformer;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -28,55 +29,6 @@ class RecurringInvoiceExport extends BaseExport
public Writer $csv;
public array $entity_keys = [
'amount' => 'amount',
'balance' => 'balance',
'client' => 'client_id',
// 'custom_surcharge1' => 'custom_surcharge1',
// 'custom_surcharge2' => 'custom_surcharge2',
// 'custom_surcharge3' => 'custom_surcharge3',
// 'custom_surcharge4' => 'custom_surcharge4',
'custom_value1' => 'custom_value1',
'custom_value2' => 'custom_value2',
'custom_value3' => 'custom_value3',
'custom_value4' => 'custom_value4',
'date' => 'date',
'discount' => 'discount',
'due_date' => 'due_date',
'exchange_rate' => 'exchange_rate',
'footer' => 'footer',
'number' => 'number',
'paid_to_date' => 'paid_to_date',
'partial' => 'partial',
'partial_due_date' => 'partial_due_date',
'po_number' => 'po_number',
'private_notes' => 'private_notes',
'public_notes' => 'public_notes',
'next_send_date' => 'next_send_date',
'status' => 'status_id',
'tax_name1' => 'tax_name1',
'tax_name2' => 'tax_name2',
'tax_name3' => 'tax_name3',
'tax_rate1' => 'tax_rate1',
'tax_rate2' => 'tax_rate2',
'tax_rate3' => 'tax_rate3',
'terms' => 'terms',
'total_taxes' => 'total_taxes',
'currency' => 'currency_id',
'vendor' => 'vendor_id',
'project' => 'project_id',
'frequency_id' => 'frequency_id',
];
private array $decorate_keys = [
'country',
'client',
'currency',
'status',
'vendor',
'project',
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -84,7 +36,7 @@ class RecurringInvoiceExport extends BaseExport
$this->invoice_transformer = new RecurringInvoiceTransformer();
}
public function run()
public function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
@ -92,23 +44,33 @@ class RecurringInvoiceExport extends BaseExport
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->recurring_invoice_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = RecurringInvoice::query()
->withTrashed()
->with('client')->where('company_id', $this->company->id)
->with('client')
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);
return $query;
}
public function run()
{
$query = $this->init();
//load the CSV document from a string
$this->csv = Writer::createFromString();
//insert the header
$this->csv->insertOne($this->buildHeader());
$query->cursor()
->each(function ($invoice) {
$this->csv->insertOne($this->buildRow($invoice));
@ -117,6 +79,27 @@ class RecurringInvoiceExport extends BaseExport
return $this->csv->toString();
}
public function returnJson()
{
$query = $this->init();
$headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]];
})->toArray();
$report = $query->cursor()
->map(function ($resource) {
$row = $this->buildRow($resource);
return $this->processMetaData($row, $resource);
})->toArray();
return array_merge(['columns' => $header], $report);
}
private function buildRow(RecurringInvoice $invoice) :array
{
$transformed_invoice = $this->invoice_transformer->transform($invoice);
@ -124,22 +107,13 @@ class RecurringInvoiceExport extends BaseExport
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) {
$keyval = array_search($key, $this->entity_keys);
if(!$keyval) {
$keyval = array_search(str_replace("recurring_invoice.", "", $key), $this->entity_keys) ?? $key;
}
$parts = explode('.', $key);
if(!$keyval) {
$keyval = $key;
}
if (array_key_exists($key, $transformed_invoice)) {
$entity[$keyval] = $transformed_invoice[$key];
} elseif (array_key_exists($keyval, $transformed_invoice)) {
$entity[$keyval] = $transformed_invoice[$keyval];
if (is_array($parts) && $parts[0] == 'recurring_invoice' && array_key_exists($parts[1], $transformed_invoice)) {
$entity[$key] = $transformed_invoice[$parts[1]];
} else {
$entity[$keyval] = $this->resolveKey($keyval, $invoice, $this->invoice_transformer);
$entity[$key] = $this->resolveKey($key, $invoice, $this->invoice_transformer);
}
}
@ -174,7 +148,7 @@ class RecurringInvoiceExport extends BaseExport
}
if (in_array('recurring_invoice.frequency_id', $this->input['report_keys']) || in_array('frequency_id', $this->input['report_keys'])) {
$entity['frequency_id'] = $invoice->frequencyForKey($invoice->frequency_id);
$entity['recurring_invoice.frequency_id'] = $invoice->frequencyForKey($invoice->frequency_id);
}
return $entity;

View File

@ -18,6 +18,7 @@ use App\Models\Task;
use App\Models\Timezone;
use App\Transformers\TaskTransformer;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -33,30 +34,9 @@ class TaskExport extends BaseExport
public Writer $csv;
public array $entity_keys = [
'start_date' => 'start_date',
'end_date' => 'end_date',
'duration' => 'duration',
'rate' => 'rate',
'number' => 'number',
'description' => 'description',
'custom_value1' => 'custom_value1',
'custom_value2' => 'custom_value2',
'custom_value3' => 'custom_value3',
'custom_value4' => 'custom_value4',
'status' => 'status_id',
'project' => 'project_id',
];
private array $storage_array = [];
private array $decorate_keys = [
'status',
'project',
'client',
'invoice',
'start_date',
'end_date',
'duration',
];
private array $storage_item_array = [];
public function __construct(Company $company, array $input)
{
@ -65,7 +45,7 @@ class TaskExport extends BaseExport
$this->entity_transformer = new TaskTransformer();
}
public function run()
public function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
@ -74,19 +54,12 @@ class TaskExport extends BaseExport
$t->replace(Ninja::transformTranslations($this->company->settings));
$this->date_format = DateFormat::find($this->company->settings->date_format_id)->format;
//load the CSV document from a string
$this->csv = Writer::createFromString();
ksort($this->entity_keys);
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->task_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Task::query()
->withTrashed()
->where('company_id', $this->company->id)
@ -94,52 +67,87 @@ class TaskExport extends BaseExport
$query = $this->addDateRange($query);
return $query;
}
public function run()
{
$query = $this->init();
//load the CSV document from a string
$this->csv = Writer::createFromString();
//insert the header
$this->csv->insertOne($this->buildHeader());
$query->cursor()
->each(function ($entity) {
$this->buildRow($entity);
});
$this->csv->insertAll($this->storage_array);
return $this->csv->toString();
}
public function returnJson()
{
$query = $this->init();
$headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]];
})->toArray();
$query->cursor()
->each(function ($resource) {
$this->buildRow($resource);
foreach($this->storage_array as $row)
{
$this->storage_item_array[] = $this->processMetaData($row, $resource);
}
$this->storage_array = [];
});
nlog($this->storage_item_array);
return array_merge(['columns' => $header], $this->storage_item_array);
}
private function buildRow(Task $task)
{
$entity = [];
$transformed_entity = $this->entity_transformer->transform($task);
foreach (array_values($this->input['report_keys']) as $key) {
$keyval = array_search($key, $this->entity_keys);
if(!$keyval) {
$keyval = array_search(str_replace("task.", "", $key), $this->entity_keys) ?? $key;
$parts = explode('.', $key);
if (is_array($parts) && $parts[0] == 'task' && array_key_exists($parts[1], $transformed_entity)) {
$entity[$key] = $transformed_entity[$parts[1]];
} elseif (array_key_exists($key, $transformed_entity)) {
$entity[$key] = $transformed_entity[$key];
} else {
$entity[$key] = $this->resolveKey($key, $task, $this->entity_transformer);
}
if(!$keyval) {
$keyval = $key;
}
if (array_key_exists($key, $transformed_entity)) {
$entity[$keyval] = $transformed_entity[$key];
} elseif (array_key_exists($keyval, $transformed_entity)) {
$entity[$keyval] = $transformed_entity[$keyval];
}
else {
$entity[$keyval] = $this->resolveKey($keyval, $task, $this->entity_transformer);
}
}
$entity['start_date'] = '';
$entity['end_date'] = '';
$entity['duration'] = '';
$entity['task.start_date'] = '';
$entity['task.end_date'] = '';
$entity['task.duration'] = '';
if (is_null($task->time_log) || (is_array(json_decode($task->time_log, 1)) && count(json_decode($task->time_log, 1)) == 0)) {
$this->csv->insertOne($entity);
$this->storage_array[] = $entity;
} else {
$this->iterateLogs($task, $entity);
}
}
private function iterateLogs(Task $task, array $entity)
@ -163,39 +171,40 @@ class TaskExport extends BaseExport
foreach ($logs as $key => $item) {
if (in_array('task.start_date', $this->input['report_keys']) || in_array('start_date', $this->input['report_keys'])) {
$entity['start_date'] = Carbon::createFromTimeStamp($item[0])->setTimezone($timezone_name)->format($date_format_default);
$entity['task.start_date'] = Carbon::createFromTimeStamp($item[0])->setTimezone($timezone_name)->format($date_format_default);
}
if ((in_array('task.end_date', $this->input['report_keys']) || in_array('end_date', $this->input['report_keys'])) && $item[1] > 0) {
$entity['end_date'] = Carbon::createFromTimeStamp($item[1])->setTimezone($timezone_name)->format($date_format_default);
$entity['task.end_date'] = Carbon::createFromTimeStamp($item[1])->setTimezone($timezone_name)->format($date_format_default);
}
if ((in_array('task.end_date', $this->input['report_keys']) || in_array('end_date', $this->input['report_keys'])) && $item[1] == 0) {
$entity['end_date'] = ctrans('texts.is_running');
$entity['task.end_date'] = ctrans('texts.is_running');
}
if (in_array('task.duration', $this->input['report_keys']) || in_array('duration', $this->input['report_keys'])) {
$entity['duration'] = $task->calcDuration();
$entity['task.duration'] = $task->calcDuration();
}
$entity = $this->decorateAdvancedFields($task, $entity);
$this->csv->insertOne($entity);
$this->storage_array[] = $entity;
unset($entity['start_date']);
unset($entity['end_date']);
unset($entity['duration']);
unset($entity['task.start_date']);
unset($entity['task.end_date']);
unset($entity['task.duration']);
}
}
private function decorateAdvancedFields(Task $task, array $entity) :array
{
if (in_array('status_id', $this->input['report_keys'])) {
$entity['status'] = $task->status()->exists() ? $task->status->name : '';
if (in_array('task.status_id', $this->input['report_keys'])) {
$entity['task.status_id'] = $task->status()->exists() ? $task->status->name : '';
}
if (in_array('project_id', $this->input['report_keys'])) {
$entity['project'] = $task->project()->exists() ? $task->project->name : '';
if (in_array('task.project_id', $this->input['report_keys'])) {
$entity['task.project_id'] = $task->project()->exists() ? $task->project->name : '';
}
return $entity;

View File

@ -17,6 +17,7 @@ use App\Models\Company;
use App\Transformers\VendorContactTransformer;
use App\Transformers\VendorTransformer;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -31,42 +32,6 @@ class VendorExport extends BaseExport
public string $date_key = 'created_at';
public array $entity_keys = [
'address1' => 'vendor.address1',
'address2' => 'vendor.address2',
'city' => 'vendor.city',
'country' => 'vendor.country_id',
'custom_value1' => 'vendor.custom_value1',
'custom_value2' => 'vendor.custom_value2',
'custom_value3' => 'vendor.custom_value3',
'custom_value4' => 'vendor.custom_value4',
'id_number' => 'vendor.id_number',
'name' => 'vendor.name',
'number' => 'vendor.number',
'phone' => 'vendor.phone',
'postal_code' => 'vendor.postal_code',
'private_notes' => 'vendor.private_notes',
'public_notes' => 'vendor.public_notes',
'state' => 'vendor.state',
'vat_number' => 'vendor.vat_number',
'website' => 'vendor.website',
'currency' => 'vendor.currency',
'first_name' => 'vendor_contact.first_name',
'last_name' => 'vendor_contact.last_name',
'contact_phone' => 'vendor_contact.phone',
'contact_custom_value1' => 'vendor_contact.custom_value1',
'contact_custom_value2' => 'vendor_contact.custom_value2',
'contact_custom_value3' => 'vendor_contact.custom_value3',
'contact_custom_value4' => 'vendor_contact.custom_value4',
'email' => 'vendor_contact.email',
'status' => 'vendor.status'
];
private array $decorate_keys = [
'vendor.country_id',
'vendor.currency',
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -75,8 +40,9 @@ class VendorExport extends BaseExport
$this->contact_transformer = new VendorContactTransformer();
}
public function run()
public function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
@ -87,12 +53,9 @@ class VendorExport extends BaseExport
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->vendor_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Vendor::query()->with('contacts')
->withTrashed()
->where('company_id', $this->company->id)
@ -100,6 +63,37 @@ class VendorExport extends BaseExport
$query = $this->addDateRange($query);
return $query;
}
public function returnJson()
{
$query = $this->init();
$headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]];
})->toArray();
$report = $query->cursor()
->map(function ($resource) {
$row = $this->buildRow($resource);
return $this->processMetaData($row, $resource);
})->toArray();
return array_merge(['columns' => $header], $report);
}
public function run()
{
$query = $this->init();
//insert the header
$this->csv->insertOne($this->buildHeader());
$query->cursor()
->each(function ($vendor) {
$this->csv->insertOne($this->buildRow($vendor));
@ -110,7 +104,7 @@ class VendorExport extends BaseExport
private function buildRow(Vendor $vendor) :array
{
$transformed_contact = [];
$transformed_contact = false;
$transformed_vendor = $this->vendor_transformer->transform($vendor);
@ -123,14 +117,12 @@ class VendorExport extends BaseExport
foreach (array_values($this->input['report_keys']) as $key) {
$parts = explode('.', $key);
$keyval = array_search($key, $this->entity_keys);
if (is_array($parts) && $parts[0] == 'vendor' && array_key_exists($parts[1], $transformed_vendor)) {
$entity[$keyval] = $transformed_vendor[$parts[1]];
} elseif (is_array($parts) && $parts[0] == 'vendor_contact' && array_key_exists($parts[1], $transformed_contact)) {
$entity[$keyval] = $transformed_contact[$parts[1]];
$entity[$key] = $transformed_vendor[$parts[1]];
} elseif (is_array($parts) && $parts[0] == 'vendor_contact' && isset($transformed_contact[$parts[1]])) {
$entity[$key] = $transformed_contact[$parts[1]];
} else {
$entity[$keyval] = '';
$entity[$key] = $this->resolveKey($key, $vendor, $this->vendor_transformer);
}
}

View File

@ -391,15 +391,17 @@ class InvoiceItemSum
{
$this->setGroupedTaxes(collect([]));
$item_tax = 0;
foreach ($this->line_items as $this->item) {
foreach ($this->line_items as $key => $this->item) {
if ($this->item->line_total == 0) {
continue;
}
$item_tax = 0;
//$amount = $this->item->line_total - ($this->item->line_total * ($this->invoice->discount / $this->sub_total));
$amount = ($this->sub_total > 0) ? $this->item->line_total - ($this->item->line_total * ($this->invoice->discount / $this->sub_total)) : 0;
$amount = ($this->sub_total > 0) ? $this->item->line_total - ($this->invoice->discount * ( $this->item->line_total / $this->sub_total)) : 0;
$item_tax_rate1_total = $this->calcAmountLineTax($this->item->tax_rate1, $amount);
@ -424,9 +426,19 @@ class InvoiceItemSum
if ($item_tax_rate3_total != 0) {
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
}
$this->item->gross_line_total = $this->getLineTotal() + $item_tax;
$this->item->tax_amount = $item_tax;
$this->line_items[$key] = $this->item;
$this->setTotalTaxes($this->getTotalTaxes() + $item_tax);
}
$this->setTotalTaxes($item_tax);
return $this;
}
/**

View File

@ -308,8 +308,9 @@ class InvoiceSum
public function setTaxMap(): self
{
if ($this->invoice->is_amount_discount == true) {
if ($this->invoice->is_amount_discount) {
$this->invoice_items->calcTaxesWithAmountDiscount();
$this->invoice->line_items = $this->invoice_items->getLineItems();
}
$this->tax_map = collect();
@ -327,8 +328,6 @@ class InvoiceSum
return $value['key'] == $key;
})->sum('total');
//$total_line_tax -= $this->discount($total_line_tax);
$this->tax_map[] = ['name' => $tax_name, 'total' => $total_line_tax];
$this->total_taxes += $total_line_tax;
@ -377,16 +376,6 @@ class InvoiceSum
public function purgeTaxes(): self
{
// $this->tax_rate1 = 0;
// $this->tax_name1 = '';
// $this->tax_rate2 = 0;
// $this->tax_name2 = '';
// $this->tax_rate3 = 0;
// $this->tax_name3 = '';
// $this->discount = 0;
$line_items = collect($this->invoice->line_items);

View File

@ -576,14 +576,9 @@ class QuoteController extends BaseController
if ($action == 'convert_to_project') {
$quotes->each(function ($quote, $key) use ($user) {
if ($user->can('edit', $quote)) {
$project = CloneQuoteToProjectFactory::create($quote, $user->id);
if (empty($project->number)) {
$project->number = $this->getNextProjectNumber($project);
}
$project->save();
$quote->project_id = $project->id;
$quote->save();
$quote->service()->convertToProject();
}
});

View File

@ -11,12 +11,13 @@
namespace App\Http\Controllers\Reports;
use Illuminate\Http\Response;
use App\Utils\Traits\MakesHash;
use App\Jobs\Report\SendToAdmin;
use App\Export\CSV\DocumentExport;
use App\Jobs\Report\PreviewReport;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Report\GenericReportRequest;
use App\Jobs\Report\SendToAdmin;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Response;
class DocumentReportController extends BaseController
{
@ -62,14 +63,27 @@ class DocumentReportController extends BaseController
*/
public function __invoke(GenericReportRequest $request)
{
/** @var \App\Models\User $user */
$user = auth()->user();
if ($request->has('send_email') && $request->get('send_email')) {
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), DocumentExport::class, $this->filename);
SendToAdmin::dispatch($user->company(), $request->all(), DocumentExport::class, $this->filename);
return response()->json(['message' => 'working...'], 200);
}
// expect a list of visible fields, or use the default
$export = new DocumentExport(auth()->user()->company(), $request->all());
if($request->has('output') && $request->input('output') == 'json') {
$hash = \Illuminate\Support\Str::uuid();
PreviewReport::dispatch($user->company(), $request->all(), DocumentExport::class, $hash);
return response()->json(['message' => $hash], 200);
}
$export = new DocumentExport($user->company(), $request->all());
$csv = $export->run();

View File

@ -81,7 +81,6 @@ class InvoiceItemReportController extends BaseController
return response()->json(['message' => $hash], 200);
}
$export = new InvoiceItemExport($user->company(), $request->all());
$csv = $export->run();

View File

@ -11,12 +11,13 @@
namespace App\Http\Controllers\Reports;
use Illuminate\Http\Response;
use App\Utils\Traits\MakesHash;
use App\Jobs\Report\SendToAdmin;
use App\Jobs\Report\PreviewReport;
use App\Export\CSV\QuoteItemExport;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Report\GenericReportRequest;
use App\Jobs\Report\SendToAdmin;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Response;
class QuoteItemReportController extends BaseController
{
@ -62,14 +63,26 @@ class QuoteItemReportController extends BaseController
*/
public function __invoke(GenericReportRequest $request)
{
/** @var \App\Models\User $user */
$user = auth()->user();
if ($request->has('send_email') && $request->get('send_email')) {
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), QuoteItemExport::class, $this->filename);
SendToAdmin::dispatch($user->company(), $request->all(), QuoteItemExport::class, $this->filename);
return response()->json(['message' => 'working...'], 200);
}
// expect a list of visible fields, or use the default
$export = new QuoteItemExport(auth()->user()->company(), $request->all());
if($request->has('output') && $request->input('output') == 'json') {
$hash = \Illuminate\Support\Str::uuid();
PreviewReport::dispatch($user->company(), $request->all(), QuoteItemExport::class, $hash);
return response()->json(['message' => $hash], 200);
}
$export = new QuoteItemExport($user->company(), $request->all());
$csv = $export->run();

View File

@ -11,12 +11,13 @@
namespace App\Http\Controllers\Reports;
use App\Export\CSV\RecurringInvoiceExport;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Report\GenericReportRequest;
use App\Jobs\Report\SendToAdmin;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Response;
use App\Utils\Traits\MakesHash;
use App\Jobs\Report\SendToAdmin;
use App\Jobs\Report\PreviewReport;
use App\Http\Controllers\BaseController;
use App\Export\CSV\RecurringInvoiceExport;
use App\Http\Requests\Report\GenericReportRequest;
class RecurringInvoiceReportController extends BaseController
{
@ -31,14 +32,26 @@ class RecurringInvoiceReportController extends BaseController
public function __invoke(GenericReportRequest $request)
{
/** @var \App\Models\User $user */
$user = auth()->user();
if ($request->has('send_email') && $request->get('send_email')) {
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), RecurringInvoiceExport::class, $this->filename);
SendToAdmin::dispatch($user->company(), $request->all(), RecurringInvoiceExport::class, $this->filename);
return response()->json(['message' => 'working...'], 200);
}
// expect a list of visible fields, or use the default
$export = new RecurringInvoiceExport(auth()->user()->company(), $request->all());
if($request->has('output') && $request->input('output') == 'json') {
$hash = \Illuminate\Support\Str::uuid();
PreviewReport::dispatch($user->company(), $request->all(), RecurringInvoiceExport::class, $hash);
return response()->json(['message' => $hash], 200);
}
$export = new RecurringInvoiceExport($user->company(), $request->all());
$csv = $export->run();

View File

@ -11,12 +11,13 @@
namespace App\Http\Controllers\Reports;
use Illuminate\Http\Response;
use App\Export\CSV\TaskExport;
use App\Utils\Traits\MakesHash;
use App\Jobs\Report\SendToAdmin;
use App\Jobs\Report\PreviewReport;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Report\GenericReportRequest;
use App\Jobs\Report\SendToAdmin;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Response;
class TaskReportController extends BaseController
{
@ -62,14 +63,26 @@ class TaskReportController extends BaseController
*/
public function __invoke(GenericReportRequest $request)
{
/** @var \App\Models\User $user */
$user = auth()->user();
if ($request->has('send_email') && $request->get('send_email')) {
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), TaskExport::class, $this->filename);
SendToAdmin::dispatch($user->company(), $request->all(), TaskExport::class, $this->filename);
return response()->json(['message' => 'working...'], 200);
}
// expect a list of visible fields, or use the default
$export = new TaskExport(auth()->user()->company(), $request->all());
if($request->has('output') && $request->input('output') == 'json') {
$hash = \Illuminate\Support\Str::uuid();
PreviewReport::dispatch($user->company(), $request->all(), TaskExport::class, $hash);
return response()->json(['message' => $hash], 200);
}
$export = new TaskExport($user->company(), $request->all());
$csv = $export->run();

View File

@ -11,11 +11,12 @@
namespace App\Http\Controllers\Reports;
use App\Utils\Traits\MakesHash;
use App\Export\CSV\VendorExport;
use App\Jobs\Report\SendToAdmin;
use App\Jobs\Report\PreviewReport;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Report\GenericReportRequest;
use App\Jobs\Report\SendToAdmin;
use App\Utils\Traits\MakesHash;
class VendorReportController extends BaseController
{
@ -30,14 +31,26 @@ class VendorReportController extends BaseController
public function __invoke(GenericReportRequest $request)
{
/** @var \App\Models\User $user */
$user = auth()->user();
if ($request->has('send_email') && $request->get('send_email')) {
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), VendorExport::class, $this->filename);
SendToAdmin::dispatch($user->company(), $request->all(), VendorExport::class, $this->filename);
return response()->json(['message' => 'working...'], 200);
}
// expect a list of visible fields, or use the default
$export = new VendorExport(auth()->user()->company(), $request->all());
if($request->has('output') && $request->input('output') == 'json') {
$hash = \Illuminate\Support\Str::uuid();
PreviewReport::dispatch($user->company(), $request->all(), VendorExport::class, $hash);
return response()->json(['message' => $hash], 200);
}
$export = new VendorExport($user->company(), $request->all());
$csv = $export->run();

View File

@ -46,7 +46,8 @@ class SearchController extends Controller
'name' => $client->present()->name(),
'type' => '/client',
'id' => $client->hashed_id,
'path' => "/clients/{$client->hashed_id}/edit"
'path' => "/clients/{$client->hashed_id}/edit",
'heading' => ctrans('texts.clients')
];
});
}
@ -65,7 +66,8 @@ class SearchController extends Controller
'name' => $contact->present()->search_display(),
'type' => '/client_contact',
'id' => $contact->client->hashed_id,
'path' => "/clients/{$contact->client->hashed_id}"
'path' => "/clients/{$contact->client->hashed_id}",
'heading' => ctrans('texts.contacts')
];
});
}
@ -84,7 +86,8 @@ class SearchController extends Controller
'name' => $invoice->client->present()->name() . ' - ' . $invoice->number,
'type' => '/invoice',
'id' => $invoice->hashed_id,
'path' => "/clients/{$invoice->hashed_id}/edit"
'path' => "/clients/{$invoice->hashed_id}/edit",
'heading' => ctrans('texts.invoices')
];
});
}
@ -179,6 +182,7 @@ class SearchController extends Controller
'path' => $value,
'type' => $transkey,
'name' => $translation,
'heading' => ctrans('texts.settings')
];
}

View File

@ -76,7 +76,7 @@ class CreateUser
'is_admin' => 1,
'is_locked' => 0,
'permissions' => '',
'notifications' => CompanySettings::notificationDefaults(),
'notifications' => CompanySettings::notificationAdminDefaults(),
'settings' => null,
]);

View File

@ -123,21 +123,21 @@ class CreditEmailEngine extends BaseEmailEngine
//attach third party documents
if ($this->client->getSetting('document_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) {
// Storage::url
foreach ($this->credit->documents as $document) {
$this->credit->documents()->where('is_public',true)->cursor()->each(function($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, 'file' => base64_encode($document->getFile())]]);
}
}
});
foreach ($this->credit->company->documents as $document) {
$this->credit->company->documents()->where('is_public',true)->cursor()->each(function($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, 'file' => base64_encode($document->getFile())]]);
}
}
});
}
return $this;

View File

@ -135,31 +135,31 @@ class InvoiceEmailEngine extends BaseEmailEngine
//attach third party documents
if ($this->client->getSetting('document_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) {
if ($this->invoice->recurring_invoice()->exists()) {
foreach ($this->invoice->recurring_invoice->documents as $document) {
$this->invoice->recurring_invoice->documents()->where('is_public',true)->cursor()->each(function ($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]);
}
}
});
}
// Storage::url
foreach ($this->invoice->documents as $document) {
$this->invoice->documents()->where('is_public',true)->cursor()->each(function ($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]);
}
}
});
foreach ($this->invoice->company->documents as $document) {
$this->invoice->company->documents()->where('is_public',true)->cursor()->each(function ($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]);
}
}
});
$line_items = $this->invoice->line_items;
@ -175,13 +175,13 @@ class InvoiceEmailEngine extends BaseEmailEngine
->where('invoice_documents', 1)
->cursor()
->each(function ($expense) {
foreach ($expense->documents as $document) {
$expense->documents()->where('is_public',true)->cursor()->each(function ($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]);
}
}
});
});
}
@ -195,13 +195,13 @@ class InvoiceEmailEngine extends BaseEmailEngine
$tasks = Task::query()->whereIn('id', $this->transformKeys($task_ids))
->cursor()
->each(function ($task) {
foreach ($task->documents as $document) {
$task->documents()->where('is_public', true)->cursor()->each(function ($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]);
}
}
});
});
}
}

View File

@ -98,13 +98,13 @@ class PaymentEmailEngine extends BaseEmailEngine
//attach invoice documents also to payments
if ($this->client->getSetting('document_email_attachment') !== false) {
foreach ($invoice->documents as $document) {
$invoice->documents()->where('is_public', true)->cursor()->each(function ($document){
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]);
}
}
});
}
if($this->client->getSetting('enable_e_invoice'))

View File

@ -127,21 +127,21 @@ class PurchaseOrderEmailEngine extends BaseEmailEngine
//attach third party documents
if ($this->vendor->getSetting('document_email_attachment') !== false && $this->purchase_order->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) {
// Storage::url
foreach ($this->purchase_order->documents as $document) {
$this->purchase_order->documents()->where('is_public', true)->cursor()->each(function ($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
}
}
});
foreach ($this->purchase_order->company->documents as $document) {
$this->purchase_order->company->documents()->where('is_public', true)->cursor()->each(function ($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
}
}
});
}
return $this;

View File

@ -121,21 +121,21 @@ class QuoteEmailEngine extends BaseEmailEngine
//attach third party documents
if ($this->client->getSetting('document_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) {
// Storage::url
foreach ($this->quote->documents as $document) {
$this->quote->documents()->where('is_public', true)->cursor()->each(function ($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]);
}
}
});
foreach ($this->quote->company->documents as $document) {
$this->quote->company->documents()->where('is_public', true)->cursor()->each(function ($document) {
if ($document->size > $this->max_attachment_size) {
$this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]);
} else {
$this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]);
}
}
});
}
return $this;

View File

@ -12,25 +12,25 @@
namespace App\PaymentDrivers\CheckoutCom;
use App\Exceptions\PaymentFailed;
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
use App\Jobs\Util\SystemLogger;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\SystemLog;
use App\PaymentDrivers\CheckoutComPaymentDriver;
use App\PaymentDrivers\Common\MethodInterface;
use Illuminate\View\View;
use App\Models\GatewayType;
use Illuminate\Http\Request;
use App\Jobs\Util\SystemLogger;
use App\Utils\Traits\MakesHash;
use App\Exceptions\PaymentFailed;
use App\Models\ClientGatewayToken;
use Checkout\CheckoutApiException;
use Illuminate\Contracts\View\Factory;
use Checkout\CheckoutArgumentException;
use Checkout\CheckoutAuthorizationException;
use Checkout\Payments\Four\Request\PaymentRequest;
use Checkout\Payments\Four\Request\Source\RequestTokenSource;
use Checkout\Payments\PaymentRequest as PaymentsPaymentRequest;
use Checkout\Payments\Source\RequestTokenSource as SourceRequestTokenSource;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\Request;
use Illuminate\View\View;
use Checkout\Payments\Request\PaymentRequest;
use App\PaymentDrivers\Common\MethodInterface;
use App\PaymentDrivers\CheckoutComPaymentDriver;
use Checkout\Payments\Previous\Source\RequestTokenSource;
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
use Checkout\Payments\Previous\PaymentRequest as PreviousPaymentRequest;
use Checkout\Payments\Request\Source\RequestTokenSource as SourceRequestTokenSource;
class CreditCard implements MethodInterface
{
@ -67,12 +67,12 @@ class CreditCard implements MethodInterface
if ($this->checkout->is_four_api) {
$token_source = new RequestTokenSource();
$token_source->token = $token;
$request = new PaymentRequest();
$request = new PreviousPaymentRequest();
$request->source = $token_source;
} else {
$token_source = new SourceRequestTokenSource();
$token_source->token = $token;
$request = new PaymentsPaymentRequest();
$request = new PaymentRequest();
$request->source = $token_source;
}
@ -120,44 +120,24 @@ class CreditCard implements MethodInterface
return redirect()->route('client.payment_methods.show', $payment_method->hashed_id);
}
} catch (CheckoutApiException $e) {
// API error
$request_id = $e->request_id ?: '';
$http_status_code = $e->http_status_code ?: '';
$error_details = $e->error_details;
if (is_array($error_details)) {
if (isset($e->error_details['error_codes']) ?? false) {
$error_details = end($e->error_details['error_codes']);
}
else {
$error_details = $e->getMessage();
}
$human_exception = $error_details ? $error_details : $e->getMessage();
$human_exception = "{$human_exception} - Request ID: {$request_id}";
throw new PaymentFailed($human_exception, $http_status_code);
throw new PaymentFailed($error_details, $e->getCode());
} catch (CheckoutArgumentException $e) {
// Bad arguments
$error_details = $e->error_details;
if (is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? $error_details : $e->getMessage();
throw new PaymentFailed($human_exception, 422);
throw new PaymentFailed($e->getMessage(), $e->getCode());
} catch (CheckoutAuthorizationException $e) {
// Bad Invalid authorization
$error_details = $e->error_details;
if (is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? $error_details : $e->getMessage();
throw new PaymentFailed($human_exception, 401);
throw new PaymentFailed("There is a problem with your Checkout Gateway API keys", 401);
}
}
@ -280,8 +260,6 @@ class CreditCard implements MethodInterface
}
} catch (CheckoutApiException $e) {
// API error
$request_id = $e->request_id;
$http_status_code = $e->http_status_code;
$error_details = $e->error_details;
if (is_array($error_details)) {
@ -293,7 +271,7 @@ class CreditCard implements MethodInterface
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
SystemLogger::dispatch(
$human_exception->getMessage(),
$e->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_ERROR,
SystemLog::TYPE_CHECKOUT,
@ -305,18 +283,10 @@ class CreditCard implements MethodInterface
} catch (CheckoutArgumentException $e) {
// Bad arguments
$error_details = $e->error_details;
if (is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$this->checkout->unWindGatewayFees($this->checkout->payment_hash);
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
SystemLogger::dispatch(
$human_exception->getMessage(),
$e->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_ERROR,
SystemLog::TYPE_CHECKOUT,
@ -324,23 +294,15 @@ class CreditCard implements MethodInterface
$this->checkout->client->company,
);
return $this->checkout->processInternallyFailedPayment($this->checkout, $human_exception);
return new PaymentFailed($e->getMessage(), $e->getCode());
// return $this->checkout->processInternallyFailedPayment($this->checkout, $human_exception);
} catch (CheckoutAuthorizationException $e) {
// Bad Invalid authorization
$error_details = $e->error_details;
if (is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$this->checkout->unWindGatewayFees($this->checkout->payment_hash);
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
SystemLogger::dispatch(
$human_exception->getMessage(),
$e->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_ERROR,
SystemLog::TYPE_CHECKOUT,
@ -348,7 +310,9 @@ class CreditCard implements MethodInterface
$this->checkout->client->company,
);
return $this->checkout->processInternallyFailedPayment($this->checkout, $human_exception);
return new PaymentFailed("There was a problem communicating with the API credentials for Checkout", $e->getCode());
// return $this->checkout->processInternallyFailedPayment($this->checkout, $human_exception);
}
}
}

View File

@ -94,6 +94,16 @@ trait Utilities
$error_message = 'Error processing payment.';
}
if(isset($_payment['actions'][0]['response_summary']) ?? false) {
$error_message = $_payment['actions'][0]['response_summary'];
}
if(isset($_payment['actions'][0]['response_code']) ?? false) {
$error_code = $_payment['actions'][0]['response_code'];
}
else
$error_code = 400;
$this->getParent()->sendFailureMail($error_message);
$message = [
@ -111,7 +121,7 @@ trait Utilities
);
if ($throw_exception) {
throw new PaymentFailed($error_message, 500);
throw new PaymentFailed($error_message, $error_code);
}
}

View File

@ -25,29 +25,28 @@ use App\Models\Payment;
use App\Models\PaymentHash;
use App\Models\PaymentType;
use App\Models\SystemLog;
use App\PaymentDrivers\CheckoutCom\CheckoutWebhook;
use App\PaymentDrivers\CheckoutCom\CreditCard;
use App\PaymentDrivers\CheckoutCom\Utilities;
use App\PaymentDrivers\CheckoutCom\CheckoutWebhook;
use App\Utils\Traits\SystemLogTrait;
use Checkout\CheckoutApi;
use Checkout\CheckoutApiException;
use Checkout\CheckoutArgumentException;
use Checkout\CheckoutAuthorizationException;
use Checkout\CheckoutDefaultSdk;
use Checkout\CheckoutFourSdk;
use Checkout\CheckoutSdk;
use Checkout\Common\Phone;
use Checkout\Customers\CustomerRequest;
use Checkout\Customers\Four\CustomerRequest as FourCustomerRequest;
use Checkout\Environment;
use Checkout\Models\Payments\Refund;
use Checkout\Payments\Four\Request\PaymentRequest;
use Checkout\Payments\Four\Request\Source\RequestIdSource as SourceRequestIdSource;
use Checkout\Payments\PaymentRequest as PaymentsPaymentRequest;
use Checkout\Payments\Previous\PaymentRequest as PreviousPaymentRequest;
use Checkout\Payments\Previous\Source\RequestIdSource as SourceRequestIdSource;
use Checkout\Payments\RefundRequest;
use Checkout\Payments\Source\RequestIdSource;
use Checkout\Payments\Request\PaymentRequest;
use Checkout\Payments\Request\Source\RequestIdSource;
use Exception;
use Illuminate\Support\Facades\Auth;
//use Checkout\Customers\Four\CustomerRequest as FourCustomerRequest;
//use Checkout\Payments\Four\Request\Source\RequestIdSource as SourceRequestIdSource;
class CheckoutComPaymentDriver extends BaseDriver
{
use SystemLogTrait, Utilities;
@ -70,7 +69,7 @@ class CheckoutComPaymentDriver extends BaseDriver
public $is_four_api = false;
/**
* @var CheckoutApi;
* @var CheckoutSdk;
*/
public $gateway;
@ -117,32 +116,36 @@ class CheckoutComPaymentDriver extends BaseDriver
*/
public function init()
{
$config = [
'secret' => $this->company_gateway->getConfigField('secretApiKey'),
'public' => $this->company_gateway->getConfigField('publicApiKey'),
'sandbox' => $this->company_gateway->getConfigField('testMode'),
];
if (strlen($config['secret']) <= 38) {
$this->is_four_api = true;
$builder = CheckoutFourSdk::staticKeys();
$builder->setPublicKey($config['public']); // optional, only required for operations related with tokens
$builder->setSecretKey($config['secret']);
$builder->setEnvironment($config['sandbox'] ? Environment::sandbox() : Environment::production());
if (str_contains($this->company_gateway->getConfigField('secretApiKey'), '-')) {
$this->is_four_api = true; //was four api, now known as previous.
$builder = CheckoutSdk::builder()
->previous()
->staticKeys()
->environment($this->company_gateway->getConfigField('testMode') ? Environment::sandbox() : Environment::production())
->publicKey($this->company_gateway->getConfigField('publicApiKey'))
->secretKey($this->company_gateway->getConfigField('secretApiKey'));
$this->gateway = $builder->build();
} else {
$builder = CheckoutDefaultSdk::staticKeys();
$builder->setPublicKey($config['public']); // optional, only required for operations related with tokens
$builder->setSecretKey($config['secret']);
$builder->setEnvironment($config['sandbox'] ? Environment::sandbox() : Environment::production());
$this->gateway = $builder->build();
}
$builder = CheckoutSdk::builder()->staticKeys()
->publicKey($this->company_gateway->getConfigField('publicApiKey'))
->secretKey($this->company_gateway->getConfigField('secretApiKey'))
->environment($this->company_gateway->getConfigField('testMode') ? Environment::sandbox() : Environment::production());
$this->gateway = $builder->build();
}
return $this;
}
/**
* Process different view depending on payment type
*
* @param int $gateway_type_id The gateway type
* @return string The view string
*/
@ -151,11 +154,23 @@ class CheckoutComPaymentDriver extends BaseDriver
return 'gateways.checkout.credit_card.pay';
}
/**
* Authorize View
*
* @param array $data
* @return \Illuminate\View\View
*/
public function authorizeView($data)
{
return $this->payment_method->authorizeView($data);
}
/**
* Authorize Response
*
* @param array $data
* @return \Illuminate\View\View
*/
public function authorizeResponse($data)
{
return $this->payment_method->authorizeResponse($data);
@ -183,6 +198,12 @@ class CheckoutComPaymentDriver extends BaseDriver
return $this->payment_method->paymentResponse($request);
}
/**
* Store PaymentMethod
*
* @param array $data
* @return ?ClientGatewayToken $token
*/
public function storePaymentMethod(array $data)
{
return $this->storeGatewayToken($data);
@ -197,7 +218,7 @@ class CheckoutComPaymentDriver extends BaseDriver
$request->amount = $this->convertToCheckoutAmount($amount, $this->client->getCurrencyCode());
try {
// or, refundPayment("payment_id") for a full refund
$response = $this->gateway->getPaymentsClient()->refundPayment($payment->transaction_reference, $request);
return [
@ -213,7 +234,7 @@ class CheckoutComPaymentDriver extends BaseDriver
} catch (CheckoutArgumentException $e) {
// Bad arguments
throw new PaymentFailed($e->getMessage(), $e->getCode());
// throw new PaymentFailed($e->getMessage(), $e->getCode());
return [
'transaction_reference' => null,
@ -223,9 +244,8 @@ class CheckoutComPaymentDriver extends BaseDriver
'code' => $e->getCode(),
];
} catch (CheckoutAuthorizationException $e) {
// Bad Invalid authorization
throw new PaymentFailed($e->getMessage(), $e->getCode());
// throw new PaymentFailed("The was a problem with the Checkout Gateway Credentials.", $e->getCode());
return [
'transaction_reference' => null,
@ -244,11 +264,8 @@ class CheckoutComPaymentDriver extends BaseDriver
return $response;
} catch (\Exception $e) {
if ($this->is_four_api) {
$request = new FourCustomerRequest();
} else {
$request = new CustomerRequest();
}
$phone = new Phone();
// $phone->number = $this->client->present()->phone();
@ -262,59 +279,45 @@ class CheckoutComPaymentDriver extends BaseDriver
$response = $this->gateway->getCustomersClient()->create($request);
} catch (CheckoutApiException $e) {
// API error
$request_id = $e->request_id;
$http_status_code = $e->http_status_code;
$error_details = $e->error_details;
if (is_array($error_details)) {
if (isset($error_details['error_codes']) ?? false) {
$error_details = end($e->error_details['error_codes']);
} else {
$error_details = $e->getMessage();
}
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
throw new PaymentFailed($human_exception);
throw new PaymentFailed($error_details, 400);
} catch (CheckoutArgumentException $e) {
// Bad arguments
$error_details = $e->error_details;
if (is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
throw new PaymentFailed($human_exception);
throw new PaymentFailed($e->getMessage(), $e->getCode());
} catch (CheckoutAuthorizationException $e) {
// Bad Invalid authorization
$error_details = $e->error_details;
if (is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
throw new PaymentFailed($human_exception);
throw new PaymentFailed("Checkout Gateway credentials are invalid", 400);
}
return $response;
}
}
/**
* Boots a request for a token payment
*
* @param string $token
* @return PreviousPaymentRequest | PaymentRequest
*/
public function bootTokenRequest($token)
{
if ($this->is_four_api) {
$token_source = new SourceRequestIdSource();
$token_source->id = $token;
$request = new PaymentRequest();
$request = new PreviousPaymentRequest();
$request->source = $token_source;
} else {
$token_source = new RequestIdSource();
$token_source->id = $token;
$request = new PaymentsPaymentRequest();
$request = new PaymentRequest();
$request->source = $token_source;
}
@ -325,6 +328,8 @@ class CheckoutComPaymentDriver extends BaseDriver
{
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
$invoice = Invoice::query()->whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
$this->client = $invoice->client;
$this->payment_hash = $payment_hash;
$this->init();
@ -340,7 +345,6 @@ class CheckoutComPaymentDriver extends BaseDriver
$request->request->add(['payment_hash' => $payment_hash->hash]);
try {
// $response = $this->gateway->payments()->request($payment);
$response = $this->gateway->getPaymentsClient()->requestPayment($paymentRequest);
if ($response['status'] == 'Authorized') {
@ -386,14 +390,16 @@ class CheckoutComPaymentDriver extends BaseDriver
return false;
}
} catch (Exception | CheckoutApiException $e) {
} catch (CheckoutApiException $e) {
$this->unWindGatewayFees($payment_hash);
$message = $e->getMessage();
$error_details = '';
if (property_exists($e, 'error_details')) {
$error_details = $e->error_details;
if (isset($error_details['error_codes']) ?? false) {
$error_details = end($e->error_details['error_codes']);
} else {
$error_details = $e->getMessage();
}
$data = [
@ -401,10 +407,10 @@ class CheckoutComPaymentDriver extends BaseDriver
'error_type' => '',
'error_code' => $e->getCode(),
'param' => '',
'message' => $message,
'message' => $e->getMessage(),
];
$this->sendFailureMail($message);
$this->sendFailureMail($e->getMessage());
SystemLogger::dispatch(
$data,
@ -425,8 +431,7 @@ class CheckoutComPaymentDriver extends BaseDriver
if($request->header('cko-signature') == hash_hmac('sha256', $webhook_payload, $this->company_gateway->company->company_key)) {
CheckoutWebhook::dispatch($request->all(), $request->company_key, $this->company_gateway->id)->delay(10);
}
else {
} else {
nlog("Hash Mismatch = {$request->header('cko-signature')} ".hash_hmac('sha256', $webhook_payload, $this->company_gateway->company->company_key));
nlog($request->all());
}
@ -442,6 +447,7 @@ class CheckoutComPaymentDriver extends BaseDriver
//11-08-2022 check the user is authenticated
if (!Auth::guard('contact')->check()) {
$client = $request->getClient();
$this->client = $client;
auth()->guard('contact')->loginUsingId($client->contacts()->first()->id, true);
}

View File

@ -333,16 +333,16 @@ class EmailDefaults
}
/* Company Documents */
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->company->documents->pluck('id')->toArray());
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->company->documents()->where('is_public', true)->pluck('id')->toArray());
/** Entity Documents */
if ($this->email->email_object->entity?->documents) {
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->email_object->entity->documents->pluck('id')->toArray());
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->email_object->entity->documents()->where('is_public', true)->pluck('id')->toArray());
}
/** Recurring Invoice Documents */
if ($this->email->email_object->entity instanceof Invoice && $this->email->email_object->entity->recurring_id != null) {
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->email_object->entity->recurring_invoice->documents->pluck('id')->toArray());
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->email_object->entity->recurring_invoice->documents()->where('is_public', true)->pluck('id')->toArray());
}
/** Task / Expense Documents */
@ -365,7 +365,7 @@ class EmailDefaults
->where('invoice_documents', 1)
->cursor()
->each(function ($expense) {
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $expense->documents->pluck('id')->toArray());
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $expense->documents()->where('is_public',true)->pluck('id')->toArray());
});
}
@ -373,7 +373,7 @@ class EmailDefaults
Task::query()->whereIn('id', $this->transformKeys($task_ids))
->cursor()
->each(function ($task) {
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $task->documents->pluck('id')->toArray());
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $task->documents()->where('is_public',true)->pluck('id')->toArray());
});
}
}

View File

@ -88,7 +88,7 @@ class RefundPayment
/**
* Process the refund through the gateway.
*
* @var array $response
* $response
* [
* 'transaction_reference' => (string),
* 'transaction_response' => (string),

View File

@ -64,7 +64,7 @@ class ConvertQuoteToProject
$task->project_id = $this->quote->project_id;
$task->description = $item->notes;
$task->status_id = $task_status->id;
$task->rate = $item->unit_cost;
$task->rate = $item->cost;
$task_repo->save([], $task);
});

245
composer.lock generated
View File

@ -525,16 +525,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.280.2",
"version": "3.281.4",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "d68b83b3bc39b70bf33e9b8b5166facbe3e4fe9b"
"reference": "c37035bcfb67a9d54f91dae303b3fe8f98ea59f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d68b83b3bc39b70bf33e9b8b5166facbe3e4fe9b",
"reference": "d68b83b3bc39b70bf33e9b8b5166facbe3e4fe9b",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c37035bcfb67a9d54f91dae303b3fe8f98ea59f4",
"reference": "c37035bcfb67a9d54f91dae303b3fe8f98ea59f4",
"shasum": ""
},
"require": {
@ -614,9 +614,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.280.2"
"source": "https://github.com/aws/aws-sdk-php/tree/3.281.4"
},
"time": "2023-09-01T18:06:10+00:00"
"time": "2023-09-11T18:07:49+00:00"
},
{
"name": "bacon/bacon-qr-code",
@ -830,16 +830,16 @@
},
{
"name": "checkout/checkout-sdk-php",
"version": "3.0.13",
"version": "3.0.14",
"source": {
"type": "git",
"url": "https://github.com/checkout/checkout-sdk-php.git",
"reference": "09f50d3df10f99681b535b53c395d2ee1ddab22b"
"reference": "e8a34d34abac3fb6e7b2227731eb2e75f6ff036f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/checkout/checkout-sdk-php/zipball/09f50d3df10f99681b535b53c395d2ee1ddab22b",
"reference": "09f50d3df10f99681b535b53c395d2ee1ddab22b",
"url": "https://api.github.com/repos/checkout/checkout-sdk-php/zipball/e8a34d34abac3fb6e7b2227731eb2e75f6ff036f",
"reference": "e8a34d34abac3fb6e7b2227731eb2e75f6ff036f",
"shasum": ""
},
"require": {
@ -892,9 +892,9 @@
],
"support": {
"issues": "https://github.com/checkout/checkout-sdk-php/issues",
"source": "https://github.com/checkout/checkout-sdk-php/tree/3.0.13"
"source": "https://github.com/checkout/checkout-sdk-php/tree/3.0.14"
},
"time": "2023-06-09T09:09:30+00:00"
"time": "2023-09-07T11:00:14+00:00"
},
{
"name": "cleverit/ubl_invoice",
@ -2586,16 +2586,16 @@
},
{
"name": "google/apiclient-services",
"version": "v0.314.0",
"version": "v0.315.0",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "fe2f7513dc5a4a6cf82715fd0edf7589423d6535"
"reference": "9fe675be642888cded64be861891901f092ab72d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/fe2f7513dc5a4a6cf82715fd0edf7589423d6535",
"reference": "fe2f7513dc5a4a6cf82715fd0edf7589423d6535",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/9fe675be642888cded64be861891901f092ab72d",
"reference": "9fe675be642888cded64be861891901f092ab72d",
"shasum": ""
},
"require": {
@ -2624,22 +2624,22 @@
],
"support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.314.0"
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.315.0"
},
"time": "2023-09-03T01:04:12+00:00"
"time": "2023-09-10T01:10:37+00:00"
},
{
"name": "google/auth",
"version": "v1.29.1",
"version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-auth-library-php.git",
"reference": "f199ed635b945e5adfd3c1a203543d8d86aff239"
"reference": "6028b072aa444d7edecbed603431322026704627"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/f199ed635b945e5adfd3c1a203543d8d86aff239",
"reference": "f199ed635b945e5adfd3c1a203543d8d86aff239",
"url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/6028b072aa444d7edecbed603431322026704627",
"reference": "6028b072aa444d7edecbed603431322026704627",
"shasum": ""
},
"require": {
@ -2682,9 +2682,9 @@
"support": {
"docs": "https://googleapis.github.io/google-auth-library-php/main/",
"issues": "https://github.com/googleapis/google-auth-library-php/issues",
"source": "https://github.com/googleapis/google-auth-library-php/tree/v1.29.1"
"source": "https://github.com/googleapis/google-auth-library-php/tree/v1.30.0"
},
"time": "2023-08-23T08:49:35+00:00"
"time": "2023-09-07T19:13:44+00:00"
},
{
"name": "graham-campbell/result-type",
@ -4331,16 +4331,16 @@
},
{
"name": "laravel/framework",
"version": "v10.21.0",
"version": "v10.22.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "96b15c7ac382a9adb4a56d40c640e782d669a112"
"reference": "9234388a895206d4e1df37342b61adc67e5c5d31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/96b15c7ac382a9adb4a56d40c640e782d669a112",
"reference": "96b15c7ac382a9adb4a56d40c640e782d669a112",
"url": "https://api.github.com/repos/laravel/framework/zipball/9234388a895206d4e1df37342b61adc67e5c5d31",
"reference": "9234388a895206d4e1df37342b61adc67e5c5d31",
"shasum": ""
},
"require": {
@ -4527,7 +4527,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2023-08-29T13:55:56+00:00"
"time": "2023-09-05T13:20:01+00:00"
},
{
"name": "laravel/prompts",
@ -4700,16 +4700,16 @@
},
{
"name": "laravel/socialite",
"version": "v5.8.1",
"version": "v5.9.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/socialite.git",
"reference": "9989b4530331597fae811bca240bf4e8f15e804b"
"reference": "14acfa3262875f180fba51efe3c7aaa089a9ef24"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/socialite/zipball/9989b4530331597fae811bca240bf4e8f15e804b",
"reference": "9989b4530331597fae811bca240bf4e8f15e804b",
"url": "https://api.github.com/repos/laravel/socialite/zipball/14acfa3262875f180fba51efe3c7aaa089a9ef24",
"reference": "14acfa3262875f180fba51efe3c7aaa089a9ef24",
"shasum": ""
},
"require": {
@ -4766,7 +4766,7 @@
"issues": "https://github.com/laravel/socialite/issues",
"source": "https://github.com/laravel/socialite"
},
"time": "2023-08-21T13:06:52+00:00"
"time": "2023-09-05T15:20:21+00:00"
},
{
"name": "laravel/tinker",
@ -5315,16 +5315,16 @@
},
{
"name": "league/flysystem",
"version": "3.15.1",
"version": "3.16.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "a141d430414fcb8bf797a18716b09f759a385bed"
"reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a141d430414fcb8bf797a18716b09f759a385bed",
"reference": "a141d430414fcb8bf797a18716b09f759a385bed",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fdf372ca6b63c6e281b1c01a624349ccb757729",
"reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729",
"shasum": ""
},
"require": {
@ -5333,6 +5333,8 @@
"php": "^8.0.2"
},
"conflict": {
"async-aws/core": "<1.19.0",
"async-aws/s3": "<1.14.0",
"aws/aws-sdk-php": "3.209.31 || 3.210.0",
"guzzlehttp/guzzle": "<7.0",
"guzzlehttp/ringphp": "<1.1.1",
@ -5352,7 +5354,7 @@
"microsoft/azure-storage-blob": "^1.1",
"phpseclib/phpseclib": "^3.0.14",
"phpstan/phpstan": "^0.12.26",
"phpunit/phpunit": "^9.5.11",
"phpunit/phpunit": "^9.5.11|^10.0",
"sabre/dav": "^4.3.1"
},
"type": "library",
@ -5387,7 +5389,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/3.15.1"
"source": "https://github.com/thephpleague/flysystem/tree/3.16.0"
},
"funding": [
{
@ -5399,20 +5401,20 @@
"type": "github"
}
],
"time": "2023-05-04T09:04:26+00:00"
"time": "2023-09-07T19:22:17+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
"version": "3.15.0",
"version": "3.16.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
"reference": "d8de61ee10b6a607e7996cff388c5a3a663e8c8a"
"reference": "ded9ba346bb01cb9cc4cc7f2743c2c0e14d18e1c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/d8de61ee10b6a607e7996cff388c5a3a663e8c8a",
"reference": "d8de61ee10b6a607e7996cff388c5a3a663e8c8a",
"url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/ded9ba346bb01cb9cc4cc7f2743c2c0e14d18e1c",
"reference": "ded9ba346bb01cb9cc4cc7f2743c2c0e14d18e1c",
"shasum": ""
},
"require": {
@ -5453,7 +5455,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
"source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.15.0"
"source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.16.0"
},
"funding": [
{
@ -5465,20 +5467,20 @@
"type": "github"
}
],
"time": "2023-05-02T20:02:14+00:00"
"time": "2023-08-30T10:14:57+00:00"
},
{
"name": "league/flysystem-local",
"version": "3.15.0",
"version": "3.16.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-local.git",
"reference": "543f64c397fefdf9cfeac443ffb6beff602796b3"
"reference": "ec7383f25642e6fd4bb0c9554fc2311245391781"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/543f64c397fefdf9cfeac443ffb6beff602796b3",
"reference": "543f64c397fefdf9cfeac443ffb6beff602796b3",
"url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ec7383f25642e6fd4bb0c9554fc2311245391781",
"reference": "ec7383f25642e6fd4bb0c9554fc2311245391781",
"shasum": ""
},
"require": {
@ -5513,7 +5515,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem-local/issues",
"source": "https://github.com/thephpleague/flysystem-local/tree/3.15.0"
"source": "https://github.com/thephpleague/flysystem-local/tree/3.16.0"
},
"funding": [
{
@ -5525,7 +5527,7 @@
"type": "github"
}
],
"time": "2023-05-02T20:02:14+00:00"
"time": "2023-08-30T10:23:59+00:00"
},
{
"name": "league/fractal",
@ -5867,16 +5869,16 @@
},
{
"name": "microsoft/microsoft-graph",
"version": "1.105.0",
"version": "1.106.0",
"source": {
"type": "git",
"url": "https://github.com/microsoftgraph/msgraph-sdk-php.git",
"reference": "d137bb44a1f4ec949c814471ee94265db002fc2c"
"reference": "a9f43d74131bb13cb1b5a999101d486b26601b8f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/microsoftgraph/msgraph-sdk-php/zipball/d137bb44a1f4ec949c814471ee94265db002fc2c",
"reference": "d137bb44a1f4ec949c814471ee94265db002fc2c",
"url": "https://api.github.com/repos/microsoftgraph/msgraph-sdk-php/zipball/a9f43d74131bb13cb1b5a999101d486b26601b8f",
"reference": "a9f43d74131bb13cb1b5a999101d486b26601b8f",
"shasum": ""
},
"require": {
@ -5913,9 +5915,9 @@
"homepage": "https://developer.microsoft.com/en-us/graph",
"support": {
"issues": "https://github.com/microsoftgraph/msgraph-sdk-php/issues",
"source": "https://github.com/microsoftgraph/msgraph-sdk-php/tree/1.105.0"
"source": "https://github.com/microsoftgraph/msgraph-sdk-php/tree/1.106.0"
},
"time": "2023-08-22T13:28:28+00:00"
"time": "2023-09-08T06:02:27+00:00"
},
{
"name": "mollie/mollie-api-php",
@ -6403,16 +6405,16 @@
},
{
"name": "nesbot/carbon",
"version": "2.69.0",
"version": "2.70.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "4308217830e4ca445583a37d1bf4aff4153fa81c"
"reference": "d3298b38ea8612e5f77d38d1a99438e42f70341d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4308217830e4ca445583a37d1bf4aff4153fa81c",
"reference": "4308217830e4ca445583a37d1bf4aff4153fa81c",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/d3298b38ea8612e5f77d38d1a99438e42f70341d",
"reference": "d3298b38ea8612e5f77d38d1a99438e42f70341d",
"shasum": ""
},
"require": {
@ -6505,7 +6507,7 @@
"type": "tidelift"
}
],
"time": "2023-08-03T09:00:52+00:00"
"time": "2023-09-07T16:43:50+00:00"
},
{
"name": "nette/schema",
@ -7366,6 +7368,7 @@
"issues": "https://github.com/PayFast/payfast-php-sdk/issues",
"source": "https://github.com/PayFast/payfast-php-sdk/tree/v1.1.4"
},
"abandoned": true,
"time": "2022-12-20T10:39:51+00:00"
},
{
@ -8169,16 +8172,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.23.1",
"version": "1.24.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26"
"reference": "3510b0a6274cc42f7219367cb3abfc123ffa09d6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26",
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/3510b0a6274cc42f7219367cb3abfc123ffa09d6",
"reference": "3510b0a6274cc42f7219367cb3abfc123ffa09d6",
"shasum": ""
},
"require": {
@ -8210,9 +8213,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.1"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.0"
},
"time": "2023-08-03T16:32:59+00:00"
"time": "2023-09-07T20:46:32+00:00"
},
{
"name": "pragmarx/google2fa",
@ -9152,16 +9155,16 @@
},
{
"name": "razorpay/razorpay",
"version": "2.8.6",
"version": "2.8.7",
"source": {
"type": "git",
"url": "https://github.com/razorpay/razorpay-php.git",
"reference": "c151dadbb3d0a64d92574e9789b970196e629cac"
"reference": "2180c8c3c39678623f5cb8f639c39a706de14c44"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/razorpay/razorpay-php/zipball/c151dadbb3d0a64d92574e9789b970196e629cac",
"reference": "c151dadbb3d0a64d92574e9789b970196e629cac",
"url": "https://api.github.com/repos/razorpay/razorpay-php/zipball/2180c8c3c39678623f5cb8f639c39a706de14c44",
"reference": "2180c8c3c39678623f5cb8f639c39a706de14c44",
"shasum": ""
},
"require": {
@ -9213,20 +9216,20 @@
"issues": "https://github.com/Razorpay/razorpay-php/issues",
"source": "https://github.com/Razorpay/razorpay-php"
},
"time": "2023-06-16T10:31:14+00:00"
"time": "2023-09-11T08:31:26+00:00"
},
{
"name": "rmccue/requests",
"version": "v2.0.7",
"version": "v2.0.8",
"source": {
"type": "git",
"url": "https://github.com/WordPress/Requests.git",
"reference": "e14a6f4e7438d3f8da3f2657759e6367b906ee23"
"reference": "fae75bcb83d9d00d0e31ee86a472a036f9f91519"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/WordPress/Requests/zipball/e14a6f4e7438d3f8da3f2657759e6367b906ee23",
"reference": "e14a6f4e7438d3f8da3f2657759e6367b906ee23",
"url": "https://api.github.com/repos/WordPress/Requests/zipball/fae75bcb83d9d00d0e31ee86a472a036f9f91519",
"reference": "fae75bcb83d9d00d0e31ee86a472a036f9f91519",
"shasum": ""
},
"require": {
@ -9244,6 +9247,12 @@
"wp-coding-standards/wpcs": "^2.0",
"yoast/phpunit-polyfills": "^1.0.0"
},
"suggest": {
"art4/requests-psr18-adapter": "For using Requests as a PSR-18 HTTP Client",
"ext-curl": "For improved performance",
"ext-openssl": "For secure transport support",
"ext-zlib": "For improved performance when decompressing encoded streams"
},
"type": "library",
"autoload": {
"files": [
@ -9294,7 +9303,7 @@
"issues": "https://github.com/WordPress/Requests/issues",
"source": "https://github.com/WordPress/Requests"
},
"time": "2023-06-02T07:35:42+00:00"
"time": "2023-09-11T08:27:57+00:00"
},
{
"name": "sabre/uri",
@ -9583,16 +9592,16 @@
},
{
"name": "sentry/sentry-laravel",
"version": "3.7.3",
"version": "3.8.0",
"source": {
"type": "git",
"url": "https://github.com/getsentry/sentry-laravel.git",
"reference": "2aee4ad217be8ef04ffcde6e9f7dd17af5a3b0bf"
"reference": "c7e7611553f9f90af10ed98dde1a680220f02e4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/2aee4ad217be8ef04ffcde6e9f7dd17af5a3b0bf",
"reference": "2aee4ad217be8ef04ffcde6e9f7dd17af5a3b0bf",
"url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/c7e7611553f9f90af10ed98dde1a680220f02e4d",
"reference": "c7e7611553f9f90af10ed98dde1a680220f02e4d",
"shasum": ""
},
"require": {
@ -9605,6 +9614,7 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.11",
"laravel/folio": "^1.0",
"laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0",
"mockery/mockery": "^1.3",
"orchestra/testbench": "^4.7 | ^5.1 | ^6.0 | ^7.0 | ^8.0",
@ -9658,7 +9668,7 @@
],
"support": {
"issues": "https://github.com/getsentry/sentry-laravel/issues",
"source": "https://github.com/getsentry/sentry-laravel/tree/3.7.3"
"source": "https://github.com/getsentry/sentry-laravel/tree/3.8.0"
},
"funding": [
{
@ -9670,7 +9680,7 @@
"type": "custom"
}
],
"time": "2023-08-03T10:10:23+00:00"
"time": "2023-09-05T11:02:34+00:00"
},
{
"name": "setasign/fpdf",
@ -9906,26 +9916,26 @@
},
{
"name": "socialiteproviders/apple",
"version": "5.5.2",
"version": "5.6.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Apple.git",
"reference": "82febc9805143d1ebea6c3e66db402d43bf511e1"
"reference": "4f0f06e463824f0df6151c768db2fec6610ea055"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/SocialiteProviders/Apple/zipball/82febc9805143d1ebea6c3e66db402d43bf511e1",
"reference": "82febc9805143d1ebea6c3e66db402d43bf511e1",
"url": "https://api.github.com/repos/SocialiteProviders/Apple/zipball/4f0f06e463824f0df6151c768db2fec6610ea055",
"reference": "4f0f06e463824f0df6151c768db2fec6610ea055",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-openssl": "*",
"firebase/php-jwt": "^6.2",
"firebase/php-jwt": "^6.8",
"lcobucci/clock": "^2.0 || ^3.0",
"lcobucci/jwt": "^4.1.5 || ^5.0.0",
"php": "^7.4 || ^8.0",
"socialiteproviders/manager": "~4.0"
"php": "^8.0",
"socialiteproviders/manager": "^4.4"
},
"suggest": {
"ahilmurugesan/socialite-apple-helper": "Automatic Apple client key generation and management."
@ -9974,7 +9984,7 @@
"issues": "https://github.com/socialiteproviders/providers/issues",
"source": "https://github.com/socialiteproviders/providers"
},
"time": "2023-05-24T23:29:11+00:00"
"time": "2023-09-11T21:59:09+00:00"
},
{
"name": "socialiteproviders/manager",
@ -15068,16 +15078,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.25.1",
"version": "v3.26.1",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "8e21d69801de6b5ecb0dbe0bcdf967b335b1260b"
"reference": "d023ba6684055f6ea1da1352d8a02baca0426983"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/8e21d69801de6b5ecb0dbe0bcdf967b335b1260b",
"reference": "8e21d69801de6b5ecb0dbe0bcdf967b335b1260b",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/d023ba6684055f6ea1da1352d8a02baca0426983",
"reference": "d023ba6684055f6ea1da1352d8a02baca0426983",
"shasum": ""
},
"require": {
@ -15151,7 +15161,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.25.1"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.26.1"
},
"funding": [
{
@ -15159,7 +15169,7 @@
"type": "github"
}
],
"time": "2023-09-04T01:22:52+00:00"
"time": "2023-09-08T19:09:07+00:00"
},
{
"name": "hamcrest/hamcrest-php",
@ -15870,16 +15880,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.32",
"version": "1.10.33",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44"
"reference": "03b1cf9f814ba0863c4e9affea49a4d1ed9a2ed1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c47e47d3ab03137c0e121e77c4d2cb58672f6d44",
"reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/03b1cf9f814ba0863c4e9affea49a4d1ed9a2ed1",
"reference": "03b1cf9f814ba0863c4e9affea49a4d1ed9a2ed1",
"shasum": ""
},
"require": {
@ -15928,7 +15938,7 @@
"type": "tidelift"
}
],
"time": "2023-08-24T21:54:50+00:00"
"time": "2023-09-04T12:20:53+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -16253,16 +16263,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.3.2",
"version": "10.3.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "0dafb1175c366dd274eaa9a625e914451506bcd1"
"reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0dafb1175c366dd274eaa9a625e914451506bcd1",
"reference": "0dafb1175c366dd274eaa9a625e914451506bcd1",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/241ed4dd0db1c096984e62d414c4e1ac8d5dbff4",
"reference": "241ed4dd0db1c096984e62d414c4e1ac8d5dbff4",
"shasum": ""
},
"require": {
@ -16334,7 +16344,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.2"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.3"
},
"funding": [
{
@ -16350,7 +16360,7 @@
"type": "tidelift"
}
],
"time": "2023-08-15T05:34:23+00:00"
"time": "2023-09-05T04:34:51+00:00"
},
{
"name": "sebastian/cli-parser",
@ -16787,16 +16797,16 @@
},
{
"name": "sebastian/exporter",
"version": "5.0.0",
"version": "5.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0"
"reference": "32ff03d078fed1279c4ec9a407d08c5e9febb480"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0",
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/32ff03d078fed1279c4ec9a407d08c5e9febb480",
"reference": "32ff03d078fed1279c4ec9a407d08c5e9febb480",
"shasum": ""
},
"require": {
@ -16852,7 +16862,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0"
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
"source": "https://github.com/sebastianbergmann/exporter/tree/5.0.1"
},
"funding": [
{
@ -16860,7 +16871,7 @@
"type": "github"
}
],
"time": "2023-02-03T07:06:49+00:00"
"time": "2023-09-08T04:46:58+00:00"
},
{
"name": "sebastian/global-state",

View File

@ -657,8 +657,6 @@ $LANG = array(
'created_by_invoice' => 'Created by :invoice',
'primary_user' => 'Primary User',
'help' => 'Help',
'customize_help' => '<p>We use :pdfmake_link to define the invoice designs declaratively. The pdfmake :playground_link provides a great way to see the library in action.</p>
<p>If you need help figuring something out post a question to our :forum_link with the design you\'re using.</p>',
'playground' => 'playground',
'support_forum' => 'Support Forums',
'invoice_due_date' => 'Due Date',
@ -1822,7 +1820,6 @@ $LANG = array(
'bot_emailed_notify_paid' => 'I\'ll email you when it\'s paid.',
'add_product_to_invoice' => 'Add 1 :product',
'not_authorized' => 'You are not authorized',
'bot_get_email' => 'Hi! (wave)<br/>Thanks for trying the Invoice Ninja Bot.<br/>You need to create a free account to use this bot.<br/>Send me your account email address to get started.',
'bot_get_code' => 'Thanks! I\'ve sent a you an email with your security code.',
'bot_welcome' => 'That\'s it, your account is verified.<br/>',
'email_not_found' => 'I wasn\'t able to find an available account for :email',
@ -1830,7 +1827,6 @@ $LANG = array(
'security_code_email_subject' => 'Security code for Invoice Ninja Bot',
'security_code_email_line1' => 'This is your Invoice Ninja Bot security code.',
'security_code_email_line2' => 'Note: it will expire in 10 minutes.',
'bot_help_message' => 'I currently support:<br/>• Create\update\email an invoice<br/>• List products<br/>For example:<br/><i>invoice bob for 2 tickets, set the due date to next thursday and the discount to 10 percent</i>',
'list_products' => 'List Products',
'include_item_taxes_inline' => 'Include <b>line item taxes in line total</b>',
@ -1843,7 +1839,6 @@ $LANG = array(
'update_invoiceninja_warning' => 'Before start upgrading Invoice Ninja create a backup of your database and files!',
'update_invoiceninja_available' => 'A new version of Invoice Ninja is available.',
'update_invoiceninja_unavailable' => 'No new version of Invoice Ninja available.',
'update_invoiceninja_instructions' => 'Please install the new version <strong>:version</strong> by clicking the <em>Update now</em> button below. Afterwards you\'ll be redirected to the dashboard.',
'update_invoiceninja_update_start' => 'Update now',
'update_invoiceninja_download_start' => 'Download :version',
'create_new' => 'Create New',
@ -5161,6 +5156,9 @@ $LANG = array(
'show_document_preview' => 'Show Document Preview',
'cash_accounting' => 'Cash accounting',
'click_or_drop_files_here' => 'Click or drop files here',
'set_public' => 'Set public',
'set_private' => 'Set private',
'in_stock_quantity' => 'Stock quantity',
);
return $LANG;

View File

@ -133,7 +133,7 @@ $LANG = array(
'balance' => 'Solde',
'action' => 'Action',
'status' => 'État',
'invoice_total' => 'Montant total',
'invoice_total' => 'Total de facture',
'frequency' => 'Fréquence',
'range' => 'Période',
'start_date' => 'Date de début',
@ -289,7 +289,7 @@ $LANG = array(
'fill_products_help' => 'La sélection d\'un produit entrainera la mise à jour de <b>la description et du prix</b>',
'update_products' => 'Mise à jour automatique des produits',
'update_products_help' => 'La mise à jour d\'une facture entraîne la <b>mise à jour des produits</b>',
'create_product' => 'Nouveau produit',
'create_product' => 'Ajouter un produit',
'edit_product' => 'Modifier le produit',
'archive_product' => 'Archiver le produit',
'updated_product' => 'Le produit a été mis à jour',
@ -320,7 +320,7 @@ $LANG = array(
'delete_quote' => 'Supprimer la soumission',
'save_quote' => 'Enregistrer la soumission',
'email_quote' => 'Envoyer la soumission par courriel',
'clone_quote' => 'Dupliquer vers une nouvelle soumission',
'clone_quote' => 'Dupliquer en soumission',
'convert_to_invoice' => 'Convertir en facture',
'view_invoice' => 'Voir la facture',
'view_client' => 'Voir le client',
@ -433,7 +433,7 @@ $LANG = array(
'token_billing_type_id' => 'Jeton de facturation',
'token_billing_help' => 'Enregistrez les informations de paiement avec WePay, Stripe, Braintree ou GoCardless.',
'token_billing_1' => 'Désactivé',
'token_billing_2' => 'Adhérer - case à cocher affichée et non sélectionner',
'token_billing_2' => 'Adhérer - case à cocher affichée et non sélectionnée',
'token_billing_3' => 'Se soustraire - case à cocher affichée et sélectionnée',
'token_billing_4' => 'Toujours',
'token_billing_checkbox' => 'Mémoriser les informations de carte de crédit',
@ -450,7 +450,7 @@ $LANG = array(
'billing_method' => 'Méthode de facturation',
'order_overview' => 'Récapitulatif de la commande',
'match_address' => '*L\'adresse doit correspondre à l\'adresse associée à la carte de crédit.',
'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.',
'click_once' => '*Veuillez cliquer sur "PAYER MAINTENANT" une seule fois - la transaction peut prendre jusqu\'à 1 minute.',
'invoice_footer' => 'Pied de facture',
'save_as_default_footer' => 'Sauvegarder comme pied de facture par défaut',
'token_management' => 'Gestion des jetons',
@ -492,7 +492,7 @@ $LANG = array(
'email_approved' => 'M\'envoyer un courriel lorsqu\'une soumission a été <b>acceptée</b>',
'notification_quote_approved_subject' => 'La soumission :invoice a été acceptée par :client',
'notification_quote_approved' => 'Le client :client a accepté la soumission :invoice pour :amount.',
'resend_confirmation' => 'Renvoyer la confirmation par courriel',
'resend_confirmation' => 'Renvoyer le courriel de confirmation',
'confirmation_resent' => 'La confirmation a été renvoyée',
'payment_type_credit_card' => 'Carte de crédit',
'payment_type_paypal' => 'PayPal',
@ -560,8 +560,8 @@ $LANG = array(
'time_log' => 'Journal de temps',
'end_time' => 'Arrêtée à',
'end' => 'Fin',
'invoiced' => 'Facturée',
'logged' => 'Enregistrée',
'invoiced' => 'Facturé',
'logged' => 'Connecté',
'running' => 'En cours',
'task_error_multiple_clients' => 'Une tâche ne peut appartenir à plusieurs clients',
'task_error_running' => 'Merci d\'arrêter les tâches en cours',
@ -663,10 +663,10 @@ $LANG = array(
'invoice_sent' => ':count facture envoyée',
'invoices_sent' => ':count factures envoyées',
'status_draft' => 'Brouillon',
'status_sent' => 'Envoyée',
'status_sent' => 'Envoyé',
'status_viewed' => 'Consulté',
'status_partial' => 'Partiel',
'status_paid' => 'Payée',
'status_paid' => 'Payé',
'status_unpaid' => 'Impayé',
'status_all' => 'Tous',
'show_line_item_tax' => 'Afficher la <b>taxe</b> sur la même ligne',
@ -681,8 +681,8 @@ $LANG = array(
'templates_and_reminders' => 'Modèles et rappels',
'subject' => 'Sujet',
'body' => 'Corps',
'first_reminder' => '1er rappel',
'second_reminder' => '2e rappel',
'first_reminder' => 'Premier rappel',
'second_reminder' => 'Deuxième rappel',
'third_reminder' => 'Troisième rappel',
'num_days_reminder' => 'Jours après la date d\'échéance',
'reminder_subject' => 'Rappel: facture :invoice de :account',
@ -885,7 +885,7 @@ $LANG = array(
'days_before' => 'jours avant le',
'days_after' => 'jours après le',
'field_due_date' => 'Échéance',
'field_invoice_date' => 'date de la facture',
'field_invoice_date' => 'date de facturation',
'schedule' => 'Planification',
'email_designs' => 'Modèles de courriel',
'assigned_when_sent' => 'Assignée lors de l\'envoi',
@ -930,7 +930,7 @@ $LANG = array(
'convert_currency' => 'Conversion de devise',
'num_days' => 'Nombre de jours',
'create_payment_term' => 'Nouveau délai de paiement',
'edit_payment_terms' => 'Modifier les délais de paiement',
'edit_payment_terms' => 'Modifier le délai de paiement',
'edit_payment_term' => 'Modifier le délai de paiement',
'archive_payment_term' => 'Archiver le délai de paiement',
'recurring_due_dates' => 'Dates d\'échéances des factures récurrentes',
@ -984,7 +984,7 @@ $LANG = array(
'account_number' => 'N° du compte',
'account_name' => 'Nom du compte',
'bank_account_error' => 'Impossible de récupérer les informations de compte, veuillez vérifier les informations entrées.',
'status_approved' => 'Acceptée',
'status_approved' => 'Approuvé',
'quote_settings' => 'Paramètres des soumissions',
'auto_convert_quote' => 'Conversion automatique',
'auto_convert_quote_help' => 'Convertir automatiquement une soumission lorsque celle-ci est approuvée.',
@ -996,7 +996,7 @@ $LANG = array(
'expense_error_mismatch_currencies' => 'La devise du client ne correspond par à la devise de la dépense.',
'trello_roadmap' => 'Feuille de route Trello',
'header_footer' => 'En-tête / pied de page',
'first_page' => 'première page',
'first_page' => 'Première page',
'all_pages' => 'toutes les pages',
'last_page' => 'dernière page',
'all_pages_header' => 'Afficher l\'en-tête sur',
@ -1009,7 +1009,7 @@ $LANG = array(
'trial_message' => 'Vous allez bénéficier d\'un essai gratuit de 2 semaines au Plan Pro.',
'trial_footer' => 'Vous avez encore :count jours pour votre essai gratuit Pro Plan, :link pour s\'inscrire.',
'trial_footer_last_day' => 'C\'est le dernier jour de votre essai gratuit Pro Plan, :link pour s\'inscrire.',
'trial_call_to_action' => 'Démarrez votre essai gratuit',
'trial_call_to_action' => 'Démarrer la période d\'essai',
'trial_success' => 'Le Plan Pro, version d\'essai gratuit pour 2 semaines a été activé',
'overdue' => 'En souffrance',
@ -1113,7 +1113,7 @@ $LANG = array(
'expense_documents' => 'Justificatifs de dépense',
'invoice_embed_documents' => 'Documents intégrés',
'invoice_embed_documents_help' => 'Inclure les images jointes dans la facture.',
'document_email_attachment' => 'Documents joints',
'document_email_attachment' => 'Joindre un document',
'ubl_email_attachment' => 'Joindre un UBL',
'download_documents' => 'Télécharger les documents (:size)',
'documents_from_expenses' => 'Des dépenses:',
@ -1198,8 +1198,8 @@ $LANG = array(
'refund' => 'Rembousement',
'are_you_sure_refund' => 'Rembourser les paiements sélectionnés?',
'status_pending' => 'En attente',
'status_completed' => 'Terminée',
'status_failed' => 'Échouée',
'status_completed' => 'Complété',
'status_failed' => 'Échoué',
'status_partially_refunded' => 'Remboursement partiel',
'status_partially_refunded_amount' => ':amount remboursé',
'status_refunded' => 'Remboursé',
@ -1286,8 +1286,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'ach_authorization' => 'J\'autorise :company à utiliser mon compte bancaire pour les paiements futurs et, si nécessaire, créditer électroniquement mon compte pour corriger d\'éventuels débits erronés. Je comprends que je peux annuler cette autorisation à tout moment en supprimant le mode de paiement ou en contactant :email.',
'ach_authorization_required' => 'Vous devez consentir aux transactions ACH.',
'off' => 'Désactivé',
'opt_in' => 'Activer',
'opt_out' => 'Désactiver',
'opt_in' => 'Adhérer',
'opt_out' => 'Désadhérer',
'always' => 'Toujours',
'opted_out' => 'Désactivé',
'opted_in' => 'Activé',
@ -1325,8 +1325,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'created_wepay_confirmation_required' => 'Veuillez vérifier vos courriel et confirmer votre adresse courriel avec WePay.',
'switch_to_wepay' => 'Changer pour WePay',
'switch' => 'Commutateur',
'restore_account_gateway' => 'Restaurer la passerelle de paiement',
'restored_account_gateway' => 'La passerelle de paiement a été restaurée',
'restore_account_gateway' => 'Restaurer la passerelle',
'restored_account_gateway' => 'La passerelle a été restaurée',
'united_states' => 'États-Unis',
'canada' => 'Canada',
'accept_debit_cards' => 'Accepter les cartes de débit',
@ -1398,11 +1398,11 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'freq_biweekly' => 'Bihebdomadaire',
'freq_two_weeks' => 'Aux deux semaines',
'freq_four_weeks' => 'Aux quatre semaines',
'freq_monthly' => 'Mensuelle',
'freq_monthly' => 'Mensuel',
'freq_three_months' => 'Trimestrielle',
'freq_four_months' => '4 mois',
'freq_six_months' => 'Semestrielle',
'freq_annually' => 'Annuelle',
'freq_annually' => 'Annuellement',
'freq_two_years' => 'Deux ans',
// Payment types
@ -1813,7 +1813,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'bot_emailed_notify_paid' => 'Recevez un courriel lorsqu\'elle sera payée.',
'add_product_to_invoice' => 'Ajouter 1 :product',
'not_authorized' => 'Vous n\'êtes pas autorisé',
'bot_get_email' => 'Salut! (wave)<br/>Merci d\'essayer le Bot de Invoice Ninja.<br/>Envoie-moi l\'adresse courriel de ton compte pour commencer.',
'bot_get_email' => 'Bonjour! (wave)<br/>Thanks for trying the Invoice Ninja Bot.<br/>Vous devez vous créer un compte gratuit pour utiliser ce bot.<br/>Veuillez envoyer votre adresse courriel associée à votre compte pour démarrer.',
'bot_get_code' => 'Merci! Je vous ai envoyé un courriel avec votre code de sécurité.',
'bot_welcome' => 'Ça y est, votre compte est maintenant vérifié.>br/>',
'email_not_found' => 'Je n\'ai pas pu trouver un compte disponible pour :email',
@ -1973,12 +1973,12 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'toggle_menu' => 'Basculer la navigation',
'new_...' => 'Nouveau ...',
'list_...' => 'Liste ...',
'created_at' => 'Créé le',
'created_at' => 'Date de création',
'contact_us' => 'Nous joindre',
'user_guide' => 'Guide de l\'utilisateur',
'promo_message' => 'Profitez de l\'offre avant le :expires et épargnez :amount sur la première année de notre plan Pro ou Entreprise.',
'discount_message' => 'L\'offre de :amount expire le :expires',
'mark_paid' => 'Marquer comme payée',
'mark_paid' => 'Marquer comme payé',
'marked_sent_invoice' => 'La facture marquée a été envoyée',
'marked_sent_invoices' => 'Les factures marquées ont été envoyées',
'invoice_name' => 'Facture',
@ -2046,7 +2046,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'age_group_120' => '120+ jours',
'invoice_details' => 'Informations de facture',
'qty' => 'Quantité',
'profit_and_loss' => 'Profit et perte',
'profit_and_loss' => 'Bénéfice et perte',
'revenue' => 'Revenu',
'profit' => 'Profit',
'group_when_sorted' => 'Trier par groupe',
@ -2120,8 +2120,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'location' => 'Endroit',
'line_item' => 'Ligne d\'article',
'surcharge' => 'surcharge',
'location_first_surcharge' => 'Activer - Première surcharge',
'location_second_surcharge' => 'Activer - Deuxième surcharge',
'location_first_surcharge' => 'Activée - Première surcharge',
'location_second_surcharge' => 'Activée - Deuxième surcharge',
'location_line_item' => 'Activer - Ligne d\'article',
'online_payment_surcharge' => 'Surcharge de paiement en ligne',
'gateway_fees' => 'Frais de passerelle',
@ -2408,12 +2408,12 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'tax2' => 'Deuxième taxe',
'fee_help' => 'Les frais de la passerelle sont les coûts facturés pour l\'accès aux réseaux financiers qui traitent le traitement des paiements en ligne.',
'format_export' => 'Format d\'exportation',
'custom1' => 'Personnalisation 1',
'custom2' => 'Personnalisation 2',
'custom1' => 'Première personnalisation',
'custom2' => 'Dexième personnalisation',
'contact_first_name' => 'Prénom du contact',
'contact_last_name' => 'Nom du contact',
'contact_custom1' => 'Personnalisation 1 du contact',
'contact_custom2' => 'Personnalisation 2 du contact',
'contact_custom1' => 'Première personnalisation du contact',
'contact_custom2' => 'Deuxième personnalisation du contact',
'currency' => 'Devise',
'ofx_help' => 'Pour résoudre un problème, consultez les commentaires sur :ofxhome_link et testez avec :ofxget_link.',
'comments' => 'commentaires',
@ -2721,7 +2721,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'icon' => 'Icône',
'proposal_not_found' => 'La proposition demandée n\'est pas accessible',
'create_proposal_category' => 'Créer une catégorie',
'clone_proposal_template' => 'Cloner un modèle',
'clone_proposal_template' => 'Dupliquer un modèle',
'proposal_email' => 'Courriel de proposition',
'proposal_subject' => 'Nouvelle proposition :number pour :account',
'proposal_message' => 'Pour visualiser votre proposition de :amount, suivez le lien ci-dessous.',
@ -2751,10 +2751,10 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'reactivate' => 'Réactiver',
'reactivated_email' => 'L\'adresse courriel a été réactivée',
'emails' => 'Courriels',
'opened' => 'Ouverts',
'opened' => 'Ouvert',
'bounced' => 'Rejetés',
'total_sent' => 'Total envoyés',
'total_opened' => 'Total ouverts',
'total_opened' => 'Total ouvert',
'total_bounced' => 'Total rejetés',
'total_spam' => 'Total pourriels',
'platforms' => 'Plateformes',
@ -2788,7 +2788,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'purge_client' => 'Purger client',
'purged_client' => 'Le client a été purgé',
'purge_client_warning' => 'Tous les enregistrements (factures, tâches, dépenses, documents, etc...) seront aussi supprimés.',
'clone_product' => 'Cloner le produit',
'clone_product' => 'Dupliquer le produit',
'item_details' => 'Détails de l\'article',
'send_item_details_help' => 'Envoyer les détails de l\'article à la passerelle de paiement.',
'view_proposal' => 'Voir la proposition',
@ -2875,20 +2875,20 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'please_enter_a_quote_number' => 'Veuillez saisir un numéro de soumission',
'clients_invoices' => 'Factures de :client\'s',
'viewed' => 'Consulté',
'approved' => 'Approuvée',
'approved' => 'Approuvé',
'invoice_status_1' => 'Brouillon',
'invoice_status_2' => 'Envoyée',
'invoice_status_2' => 'Envoyé',
'invoice_status_3' => 'Consulté',
'invoice_status_4' => 'Approuvée',
'invoice_status_5' => 'Partielle',
'invoice_status_6' => 'Payée',
'invoice_status_4' => 'Approuvé',
'invoice_status_5' => 'Partiel',
'invoice_status_6' => 'Payé',
'marked_invoice_as_sent' => 'La facture a été marquée comme envoyée',
'please_enter_a_client_or_contact_name' => 'Veuillez saisir un nom de client ou de contact',
'restart_app_to_apply_change' => 'Redémarrez l\'app pour mettre à jour les changements',
'refresh_data' => 'Actualiser les données',
'blank_contact' => 'Contact vide',
'no_records_found' => 'Aucun enregistrement trouvé',
'industry' => 'Entreprise',
'industry' => 'Secteur d\'activité',
'size' => 'Taille',
'net' => 'Net',
'show_tasks' => 'Afficher les tâches',
@ -2908,9 +2908,9 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'payment_status' => 'État du paiement',
'payment_status_1' => 'Em attente',
'payment_status_2' => 'Annulée',
'payment_status_3' => 'Échouée',
'payment_status_4' => 'Complétée',
'payment_status_5' => 'Partiellement remboursée',
'payment_status_3' => 'Échoué',
'payment_status_4' => 'Complété',
'payment_status_5' => 'Remboursement partiel',
'payment_status_6' => 'Remboursé',
'send_receipt_to_client' => 'Envoyer un reçu au client',
'refunded' => 'Remboursé',
@ -3057,14 +3057,14 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'previous_year' => 'Année précédente',
'compare_to' => 'Comparer à',
'last_week' => 'Dernière semaine',
'clone_to_invoice' => 'Cloner en facture',
'clone_to_quote' => 'Cloner en soumission',
'clone_to_invoice' => 'Dupliquer en facture',
'clone_to_quote' => 'Dupliquer en soumission',
'convert' => 'Convertir',
'last7_days' => '7 derniers jours',
'last30_days' => '30 derniers jours',
'custom_js' => 'JS personnalisé',
'adjust_fee_percent_help' => 'Ajuster le frais de pourcentage au compte',
'show_product_notes' => 'Afficher le détail des produits',
'show_product_notes' => 'Afficher les détails du produit',
'show_product_notes_help' => 'Inclure la <b>description et le coût</b> dans le menu déroulant du produit',
'important' => 'Important',
'thank_you_for_using_our_app' => 'Merci d\'utiliser notre app!',
@ -3117,7 +3117,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'please_agree_to_terms_and_privacy' => 'Vous devez accepter les conditions et la politique de confidentialité pour créer un compte.',
'i_agree_to_the' => 'J\'accepte',
'terms_of_service_link' => 'les conditions',
'privacy_policy_link' => 'la politique de confidentialité',
'privacy_policy_link' => 'politique de confidentialité',
'view_website' => 'Visiter le site web',
'create_account' => 'Créer un compte',
'email_login' => 'Courriel de connexion',
@ -3143,7 +3143,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'portal_mode' => 'Mode portail',
'attach_pdf' => 'Joindre un PDF',
'attach_documents' => 'Joindre un document',
'attach_ubl' => 'Joindre UBL',
'attach_ubl' => 'Joindre un UBL',
'email_style' => 'Style de courriel',
'processed' => 'Traité',
'fee_amount' => 'Montant des frais',
@ -3301,8 +3301,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'tablet' => 'Tablette',
'layout' => 'Affichage',
'module' => 'Module',
'first_custom' => 'Premier personnalisé',
'second_custom' => 'Second personnalisé',
'first_custom' => 'Première personnalisation',
'second_custom' => 'Deuxième personnalisation',
'third_custom' => 'Troisième rappel',
'show_cost' => 'Afficher le coût',
'show_cost_help' => 'Afficher un champ de coût du produit pour suivre le profit',
@ -3459,14 +3459,14 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'slack_webhook_url' => 'URL du Webhook Slack',
'partial_payment' => 'Paiement partiel',
'partial_payment_email' => 'Courriel du paiement partiel',
'clone_to_credit' => 'Cloner au crédit',
'clone_to_credit' => 'Dupliquer en crédit',
'emailed_credit' => 'Le crédit a envoyé par courriel',
'marked_credit_as_sent' => 'Le crédit a été marqué comme envoyé',
'email_subject_payment_partial' => 'Sujet du courriel de paiement partiel',
'is_approved' => 'Est approuvé',
'migration_went_wrong' => 'Oups, quelque chose n\'a pas bien fonctionné! Veuillez vous assurer que vous avez bien configuré une instance de Invoice Ninja v5 avant de commencer la migration.',
'cross_migration_message' => 'La migration entre comptes n\'est pas autorisée. Pour en savoir plus: <a href="https://invoiceninja.github.io/cross-site-migration.html">https://invoiceninja.github.io/docs/migration/#troubleshooting</a>',
'email_credit' => 'Crédit par courriel',
'email_credit' => 'Envoyer le crédit par courriel',
'client_email_not_set' => 'Le client n\'a pas d\'adresse courriel définie',
'ledger' => 'Grand livre',
'view_pdf' => 'Voir le PDF',
@ -3496,7 +3496,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'selfhosted' => 'Auto-hébergé',
'hide_menu' => 'Masquer',
'show_menu' => 'Afficher',
'partially_refunded' => 'Partiellement remboursé',
'partially_refunded' => 'Remboursement partiel',
'search_documents' => 'Recherche de documents',
'search_designs' => 'Recherche de modèles',
'search_invoices' => 'Recherche de factures',
@ -3536,8 +3536,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'system_logs' => 'Journaux système',
'copy_link' => 'Copier le lien',
'welcome_to_invoice_ninja' => 'Bienvenue dans Invoice Ninja',
'optin' => 'Adhésion',
'optout' => 'Désadhésion',
'optin' => 'Adhérer',
'optout' => 'Désadhérer',
'auto_convert' => 'Conversion automatique',
'reminder1_sent' => 'Rappel 1 envoyé',
'reminder2_sent' => 'Rappel 2 envoyé',
@ -3642,7 +3642,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'add_documents_to_invoice_help' => 'Rendre les documents visibles pour le client',
'convert_currency_help' => 'Définir un taux d\'échange',
'expense_settings' => 'Paramètres des dépenses',
'clone_to_recurring' => 'Cloner en récurrence',
'clone_to_recurring' => 'Dupliquer en récurrence',
'crypto' => 'Crypto',
'user_field' => 'Champs utilisateur',
'variables' => 'Variables',
@ -3747,8 +3747,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'sidebar_editor' => 'Éditeur de barre latérale',
'please_type_to_confirm' => 'Veuillez saisir ":value" pour confirmer',
'purge' => 'Purger',
'clone_to' => 'Cloner vers',
'clone_to_other' => 'Cloner vers Autre',
'clone_to' => 'Dupliquer en',
'clone_to_other' => 'Dupliquer en Autre',
'labels' => 'Étiquettes',
'add_custom' => 'Ajout personnalisé',
'payment_tax' => 'Paiement de taxe',
@ -3815,7 +3815,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'no_file_selected' => 'Aucun fichier sélectionné',
'import_type' => 'Type d\'importation',
'draft_mode' => 'Mode brouillon',
'draft_mode_help' => 'Prévisualisations mises à jour plus rapidement mais moins précises',
'draft_mode_help' => 'Prévisualisation plus rapide, mais moins précise',
'show_product_discount' => 'Afficher le rabais de produit',
'show_product_discount_help' => 'Afficher un champ rabais de ligne d\'article',
'tax_name3' => 'Nom de taxe 3',
@ -3830,7 +3830,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'save_and_preview' => 'Enregistrer et prévisualiser',
'save_and_email' => 'Enregistrer et envoyer par courriel',
'converted_balance' => 'Solde converti',
'is_sent' => 'Est Envoyé',
'is_sent' => 'Est envoyé',
'document_upload' => 'Téléversement de document',
'document_upload_help' => 'Autoriser les clients à téléverser des documents',
'expense_total' => 'Total des dépenses',
@ -4335,7 +4335,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'expense_tax_help' => 'Les taxes par article sont désactivées',
'enable_markdown' => 'Activer Markdown',
'enable_markdown_help' => 'Convertir Markdown en HTML dans le PDF',
'add_second_contact' => 'Ajouter un 2e contact',
'add_second_contact' => 'Ajouter un dexième contact',
'previous_page' => 'Page précédente',
'next_page' => 'Page suivante',
'export_colors' => 'Exporter les couleurs',
@ -4360,7 +4360,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'change_email' => 'Modifier l\'adresse courriel',
'client_portal_domain_hint' => 'Paramétrez de façon optionnelle, un domaine séparé pour le portail du client',
'tasks_shown_in_portal' => 'Tâches affichées dans le portail',
'uninvoiced' => 'facturé',
'uninvoiced' => 'Non facturé',
'subdomain_guide' => 'Le sous-domaine est utilisé dans le portail du client pour personnaliser les liens à votre marque. Ex. https://votremarque.invoicing.co',
'send_time' => 'Heure d\'envoi',
'import_settings' => 'Importer les paramètres',
@ -4685,7 +4685,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'fields_per_row' => 'Champs par ligne',
'total_active_invoices' => 'Factures en cours',
'total_outstanding_invoices' => 'Factures impayées',
'total_completed_payments' => 'Paiements reçus',
'total_completed_payments' => 'Paiements complétés',
'total_refunded_payments' => 'Paiements remboursés',
'total_active_quotes' => 'Soumissions en cours',
'total_approved_quotes' => 'Soumissions approuvées',
@ -4942,7 +4942,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'no_documents_to_download' => 'Aucun document disponible en téléchargement pour les enregistrements sélectionnés',
'pixels' => 'Pixels',
'logo_size' => 'Taille du logo',
'failed' => 'Échec',
'failed' => 'Échoué',
'client_contacts' => 'Personne contact du client',
'sync_from' => 'Sync de',
'gateway_payment_text' => 'Factures: :invoices pour :amount pour :client',
@ -5132,7 +5132,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'default_payment_type' => 'Type de paiement par défaut',
'number_precision' => 'Précision du nombre',
'number_precision_help' => 'Contrôle le nombre de décimales supportées dans l\'interface',
'is_tax_exempt' => 'Exonéré de taxe',
'is_tax_exempt' => 'Exemption de taxes',
'drop_files_here' => 'Déposez les fichiers ici',
'upload_files' => 'Téléverser les fichiers',
'download_e_invoice' => 'Télécharger la facture électronique',
@ -5150,6 +5150,9 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'unlinked_transaction' => 'La transactions a été déliée',
'view_dashboard_permission' => 'Autoriser l\'accès de l\'utilisateur au tableau de bord. Les données sont limitées aux permissions disponibles.',
'marked_sent_credits' => 'Les crédits envoyés ont été marqués',
'show_document_preview' => 'Afficher la visualisation de document',
'cash_accounting' => 'Comptabilité de caisse',
'click_or_drop_files_here' => 'Cliquez ou déposez les fichiers ici',
);
return $LANG;

View File

@ -1,235 +1,235 @@
<link rel="modulepreload" href="/react/@sentry.53d33e51.js">
<link rel="modulepreload" href="/react/react-router-dom.246be94a.js">
<link rel="modulepreload" href="/react/unist-builder.67dd3ae6.js">
<link rel="modulepreload" href="/react/css-box-model.6f12fb31.js">
<link rel="modulepreload" href="/react/rehype.8e8eadfe.js">
<link rel="modulepreload" href="/react/react-resize-detector.f0db5e95.js">
<link rel="modulepreload" href="/react/lodash.curry.982dd407.js">
<link rel="modulepreload" href="/react/i18next.2068112b.js">
<link rel="modulepreload" href="/react/react-base16-styling.34dca7ff.js">
<link rel="modulepreload" href="/react/redux.64c13450.js">
<link rel="modulepreload" href="/react/react-debounce-input.4eed444a.js">
<link rel="modulepreload" href="/react/qr.js.da7c6dcd.js">
<link rel="modulepreload" href="/react/rehype-attr.f14a5c46.js">
<link rel="modulepreload" href="/react/hast-util-to-html.9c907bdf.js">
<link rel="modulepreload" href="/react/inline-style-parser.ec55a77b.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-task-list-item.36dc3006.js">
<link rel="modulepreload" href="/react/unist-util-position.207de903.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-table.f1729384.js">
<link rel="modulepreload" href="/react/react-onclickoutside.16a5d5c9.js">
<link rel="modulepreload" href="/react/react-use.225f2d99.js">
<link rel="modulepreload" href="/react/rehype-prism-plus.f2c8a7e0.js">
<link rel="modulepreload" href="/react/react.0133eee9.js">
<link rel="modulepreload" href="/react/lodash-es.9cfcbab2.js">
<link rel="modulepreload" href="/react/unist-util-filter.14ea3396.js">
<link rel="modulepreload" href="/react/unist-util-generated.22d80e74.js">
<link rel="modulepreload" href="/react/date-fns.c9928d0b.js">
<link rel="modulepreload" href="/react/react-hot-toast.7d7a6ca8.js">
<link rel="modulepreload" href="/react/hast-util-parse-selector.1be1f27a.js">
<link rel="stylesheet" href="/react/react-datepicker.ad954662.css">
<link rel="modulepreload" href="/react/@leecheuk.5575d56c.js">
<link rel="modulepreload" href="/react/rehype-stringify.b4be5192.js">
<link rel="modulepreload" href="/react/bail.58fa4042.js">
<link rel="modulepreload" href="/react/mdast-util-to-markdown.7e1348ca.js">
<link rel="modulepreload" href="/react/micromark-util-decode-string.d48b94e2.js">
<link rel="modulepreload" href="/react/tiny-warning.14328f40.js">
<link rel="modulepreload" href="/react/d3-shape.e4c3f864.js">
<link rel="modulepreload" href="/react/react-universal-interface.9dd0ac79.js">
<link rel="modulepreload" href="/react/mdast-util-find-and-replace.60a1bc2c.js">
<link rel="modulepreload" href="/react/micromark-util-encode.2bb97fcf.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-strikethrough.39c8bc63.js">
<link rel="modulepreload" href="/react/react-router.2acabcc1.js">
<link rel="modulepreload" href="/react/rehype-raw.dff21c1a.js">
<link rel="modulepreload" href="/react/micromark-factory-whitespace.4e8e9fce.js">
<link rel="modulepreload" href="/react/hast-util-from-parse5.59a95153.js">
<link rel="modulepreload" href="/react/micromark-util-character.811e4fc8.js">
<link rel="modulepreload" href="/react/react-dom.7dba7d7c.js">
<link rel="modulepreload" href="/react/css-unit-converter.d617fa75.js">
<link rel="modulepreload" href="/react/tippy.js.de2d4cc1.js">
<link rel="modulepreload" href="/react/html-parse-stringify.32417b46.js">
<link rel="modulepreload" href="/react/bcp-47-match.d4994b7a.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-task-list-item.33d9f10c.js">
<link rel="modulepreload" href="/react/use-memo-one.5c7a3f6d.js">
<link rel="modulepreload" href="/react/rehype-slug.79dc0c87.js">
<link rel="modulepreload" href="/react/react-query.c174b29d.js">
<link rel="modulepreload" href="/react/vfile-location.85c89654.js">
<link rel="modulepreload" href="/react/hast-util-heading-rank.0127330c.js">
<link rel="modulepreload" href="/react/is-buffer.f91fab60.js">
<link rel="modulepreload" href="/react/reselect.1a98dab5.js">
<link rel="modulepreload" href="/react/color.e5754c42.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-autolink-literal.c934cb3a.js">
<link rel="stylesheet" href="/react/@uiw.ea8ec30a.css">
<link rel="modulepreload" href="/react/react-string-replace.6d88c783.js">
<link rel="modulepreload" href="/react/web-namespaces.a24d7a09.js">
<link rel="modulepreload" href="/react/eventemitter3.4d80f9fd.js">
<link rel="modulepreload" href="/react/@emotion.ad14dc62.js">
<link rel="modulepreload" href="/react/@floating-ui.1d43bb23.js">
<link rel="modulepreload" href="/react/extend.ca41ef84.js">
<link rel="modulepreload" href="/react/classnames.83095105.js">
<link rel="modulepreload" href="/react/unist-util-stringify-position.7846d611.js">
<link rel="modulepreload" href="/react/is-decimal.247a88b7.js">
<link rel="modulepreload" href="/react/react-qr-code.990bb400.js">
<link rel="modulepreload" href="/react/dayjs.6e7fb39c.js">
<link rel="modulepreload" href="/react/react-icons.1523f42c.js">
<link rel="modulepreload" href="/react/d3-interpolate.a37b569a.js">
<link rel="modulepreload" href="/react/is-plain-obj.9b15794f.js">
<link rel="modulepreload" href="/react/react-markdown.2794167a.js">
<link rel="modulepreload" href="/react/micromark-factory-space.84551a41.js">
<link rel="modulepreload" href="/react/is-alphanumerical.39e15300.js">
<link rel="modulepreload" href="/react/copy-to-clipboard.107ef1da.js">
<link rel="modulepreload" href="/react/mdast-util-definitions.f453f0a8.js">
<link rel="modulepreload" href="/react/micromark-util-normalize-identifier.f045ed13.js">
<link rel="modulepreload" href="/react/micromark-factory-title.f4e107a2.js">
<link rel="modulepreload" href="/react/stylis.967d24d9.js">
<link rel="modulepreload" href="/react/react-microsoft-login.29ce1383.js">
<link rel="modulepreload" href="/react/hast-util-whitespace.b9acc724.js">
<link rel="modulepreload" href="/react/hast-util-to-string.03a4649a.js">
<link rel="modulepreload" href="/react/react-dropzone.9262fdd4.js">
<link rel="modulepreload" href="/react/micromark-util-decode-numeric-character-reference.029edbd3.js">
<link rel="modulepreload" href="/react/direction.5373b893.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-footnote.b328c551.js">
<link rel="modulepreload" href="/react/lodash.89b99a1e.js">
<link rel="modulepreload" href="/react/html-void-elements.d3b5aea3.js">
<link rel="modulepreload" href="/react/unist-util-is.bd39e4f2.js">
<link rel="modulepreload" href="/react/memoize-one.a4543f92.js">
<link rel="modulepreload" href="/react/space-separated-tokens.59bb4ca8.js">
<link rel="modulepreload" href="/react/vfile.65955e00.js">
<link rel="modulepreload" href="/react/character-entities-legacy.cb27298c.js">
<link rel="modulepreload" href="/react/d3-format.37b93b2c.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-footnote.9832ece2.js">
<link rel="modulepreload" href="/react/axios.83da4cff.js">
<link rel="modulepreload" href="/react/hoist-non-react-statics.c7b65a01.js">
<link rel="modulepreload" href="/react/scheduler.159558c9.js">
<link rel="modulepreload" href="/react/array-move.20870289.js">
<link rel="modulepreload" href="/react/@headlessui.1a209fe0.js">
<link rel="modulepreload" href="/react/lodash.debounce.637814ae.js">
<link rel="modulepreload" href="/react/base16.0b555128.js">
<link rel="modulepreload" href="/react/redux-thunk.cb293941.js">
<link rel="modulepreload" href="/react/decimal.js-light.bb502cb3.js">
<link rel="modulepreload" href="/react/currency.js.cd0659e5.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-strikethrough.326b57f9.js">
<link rel="modulepreload" href="/react/deepmerge.a6dda5ef.js">
<link rel="modulepreload" href="/react/nth-check.53474b0f.js">
<link rel="modulepreload" href="/react/hast-to-hyperscript.619bd0ae.js">
<link rel="modulepreload" href="/react/prop-types.55845d93.js">
<link rel="modulepreload" href="/react/reduce-css-calc.8f09a23d.js">
<link rel="modulepreload" href="/react/internmap.bf78dfdb.js">
<link rel="modulepreload" href="/react/tiny-invariant.4a33ca3a.js">
<link rel="modulepreload" href="/react/screenfull.4b7dcae0.js">
<link rel="modulepreload" href="/react/vfile-message.531f27be.js">
<link rel="modulepreload" href="/react/style-to-object.74a4cfe6.js">
<link rel="modulepreload" href="/react/ccount.24f4fb2a.js">
<link rel="modulepreload" href="/react/@babel.b136989e.js">
<link rel="modulepreload" href="/react/pretty-bytes.1ef697de.js">
<link rel="modulepreload" href="/react/fast-deep-equal.acde360c.js">
<link rel="modulepreload" href="/react/comma-separated-tokens.826a3e44.js">
<link rel="modulepreload" href="/react/set-harmonic-interval.933d0946.js">
<link rel="modulepreload" href="/react/react-fast-compare.d5a5a050.js">
<link rel="modulepreload" href="/react/react-select.dfccd9d4.js">
<link rel="modulepreload" href="/react/toggle-selection.25b8be4d.js">
<link rel="modulepreload" href="/react/remark-parse.2678f4ff.js">
<link rel="modulepreload" href="/react/fast-equals.9df4a03b.js">
<link rel="modulepreload" href="/react/jotai.b2000ad6.js">
<link rel="modulepreload" href="/react/react-smooth.2b9effc5.js">
<link rel="modulepreload" href="/react/d3-color.69ee14e3.js">
<link rel="modulepreload" href="/react/github-slugger.b13b4a70.js">
<link rel="modulepreload" href="/react/unified.8e87ff8a.js">
<link rel="modulepreload" href="/react/hast-util-is-element.4ae8c712.js">
<link rel="modulepreload" href="/react/warning.3e618ed9.js">
<link rel="modulepreload" href="/react/mdast-util-to-string.717718d5.js">
<link rel="modulepreload" href="/react/mdast-util-to-hast.f08c5f84.js">
<link rel="modulepreload" href="/react/micromark-core-commonmark.53c183e2.js">
<link rel="modulepreload" href="/react/micromark-factory-destination.08e32d90.js">
<link rel="modulepreload" href="/react/nano-css.abede7db.js">
<link rel="modulepreload" href="/react/hast-util-raw.da8704cb.js">
<link rel="modulepreload" href="/react/stringify-entities.a8aab4da.js">
<link rel="modulepreload" href="/react/rehype-ignore.74e5ec67.js">
<link rel="modulepreload" href="/react/hast-util-select.70119e15.js">
<link rel="modulepreload" href="/react/react-lifecycles-compat.8d6bbd76.js">
<link rel="modulepreload" href="/react/collect.js.6c048100.js">
<link rel="modulepreload" href="/react/unist-util-visit-parents.56bb16a1.js">
<link rel="modulepreload" href="/react/js-cookie.a424ed8d.js">
<link rel="modulepreload" href="/react/ts-easing.fc2b827e.js">
<link rel="modulepreload" href="/react/react-i18next.6e7ac069.js">
<link rel="modulepreload" href="/react/recharts.5f1f7161.js">
<link rel="modulepreload" href="/react/is-alphabetical.a64573e0.js">
<link rel="modulepreload" href="/react/fast-shallow-equal.b907bd6a.js">
<link rel="modulepreload" href="/react/react-colorful.df9e628a.js">
<link rel="modulepreload" href="/react/color-string.429a2c75.js">
<link rel="modulepreload" href="/react/react-feather.f471c410.js">
<link rel="modulepreload" href="/react/raf-schd.1b70a490.js">
<link rel="modulepreload" href="/react/react-popper.449be6ef.js">
<link rel="modulepreload" href="/react/goober.f083a39d.js">
<link rel="modulepreload" href="/react/@reduxjs.c59f0af0.js">
<link rel="stylesheet" href="/react/index.8f8e7d31.css">
<link rel="modulepreload" href="/react/parse5.761ebfb8.js">
<link rel="modulepreload" href="/react/micromark-util-chunked.62b98efd.js">
<link rel="modulepreload" href="/react/d3-time-format.c538854d.js">
<link rel="modulepreload" href="/react/hastscript.32892f91.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-table.f7983341.js">
<link rel="modulepreload" href="/react/immer.d85b7db2.js">
<link rel="modulepreload" href="/react/@popperjs.bd37d762.js">
<script type="module" crossorigin src="/react/index.e986f53b.js"></script>
<link rel="modulepreload" href="/react/trough.e66b541d.js">
<link rel="modulepreload" href="/react/vfile-location.85c89654.js">
<link rel="modulepreload" href="/react/classnames.83095105.js">
<link rel="modulepreload" href="/react/@tippyjs.a35a67e6.js">
<link rel="modulepreload" href="/react/decode-named-character-reference.58fdd984.js">
<link rel="modulepreload" href="/react/recharts-scale.3b840976.js">
<link rel="modulepreload" href="/react/react-redux.aa877b79.js">
<link rel="modulepreload" href="/react/color-name.65e26c0b.js">
<link rel="modulepreload" href="/react/character-entities-html4.bfdecac8.js">
<link rel="modulepreload" href="/react/parse-numeric-range.352ca5bb.js">
<link rel="modulepreload" href="/react/markdown-table.f01db7c9.js">
<link rel="modulepreload" href="/react/use-isomorphic-layout-effect.38f47e1d.js">
<link rel="modulepreload" href="/react/msal.bb42b8d0.js">
<link rel="modulepreload" href="/react/property-information.85abbb0b.js">
<link rel="modulepreload" href="/react/d3-scale.e979474b.js">
<link rel="modulepreload" href="/react/parse-entities.0d3eeb8f.js">
<link rel="modulepreload" href="/react/remark-rehype.2632fc98.js">
<link rel="modulepreload" href="/react/mdast-util-from-markdown.b50f44f1.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-tagfilter.97dd1d78.js">
<link rel="modulepreload" href="/react/micromark-util-combine-extensions.fc6008e7.js">
<link rel="modulepreload" href="/react/micromark.ea2d7513.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm.f1ea290d.js">
<link rel="modulepreload" href="/react/character-reference-invalid.9429e209.js">
<link rel="modulepreload" href="/react/color-convert.5e8bad7a.js">
<link rel="modulepreload" href="/react/@remix-run.bc8e0a8a.js">
<link rel="modulepreload" href="/react/unist-util-visit.7a4beab6.js">
<link rel="modulepreload" href="/react/rehype-parse.ed87eeb5.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-autolink-literal.75d97f6e.js">
<link rel="modulepreload" href="/react/file-selector.c939a338.js">
<link rel="modulepreload" href="/react/micromark-factory-label.2e0fc916.js">
<link rel="modulepreload" href="/react/zwitch.4efcb291.js">
<link rel="modulepreload" href="/react/micromark-util-html-tag-name.86a7a283.js">
<link rel="modulepreload" href="/react/@xobotyi.606f64fa.js">
<link rel="modulepreload" href="/react/simple-swizzle.a9455484.js">
<link rel="modulepreload" href="/react/react-datepicker.60c50a3c.js">
<link rel="modulepreload" href="/react/throttle-debounce.006a1c17.js">
<link rel="modulepreload" href="/react/micromark-util-subtokenize.0273ad72.js">
<link rel="modulepreload" href="/react/uuid.3e0f6f68.js">
<link rel="modulepreload" href="/react/attr-accept.6d6aa28d.js">
<link rel="modulepreload" href="/react/formik.215fe8e4.js">
<link rel="modulepreload" href="/react/micromark-util-classify-character.bb02743f.js">
<link rel="modulepreload" href="/react/react-json-tree.d195a734.js">
<link rel="modulepreload" href="/react/css-selector-parser.b3f82639.js">
<link rel="modulepreload" href="/react/micromark-util-sanitize-uri.dc741380.js">
<link rel="modulepreload" href="/react/hast-util-to-parse5.56a1d940.js">
<link rel="modulepreload" href="/react/hast-util-has-property.bf6e0cae.js">
<link rel="modulepreload" href="/react/refractor.77887221.js">
<link rel="modulepreload" href="/react/rehype-autolink-headings.44122af2.js">
<link rel="modulepreload" href="/react/remark-gfm.7adf8a5a.js">
<link rel="modulepreload" href="/react/trim-lines.a08a0c08.js">
<link rel="modulepreload" href="/react/rehype-rewrite.8d95b6e8.js">
<link rel="modulepreload" href="/react/tslib.5ad378ed.js">
<link rel="modulepreload" href="/react/boolbase.bd705bc9.js">
<link rel="modulepreload" href="/react/@uiw.f97db60f.js">
<link rel="modulepreload" href="/react/react-is.43cb5aff.js">
<link rel="modulepreload" href="/react/is-hexadecimal.d91cb013.js">
<link rel="modulepreload" href="/react/d3-time.4599568f.js">
<link rel="modulepreload" href="/react/micromark-util-resolve-all.b4e652e2.js">
<link rel="modulepreload" href="/react/d3-array.2eff27df.js">
<link rel="modulepreload" href="/react/client-only.9e01468e.js">
<link rel="modulepreload" href="/react/mdast-util-gfm.9c5e4fde.js">
<link rel="modulepreload" href="/react/base16.0b555128.js">
<link rel="modulepreload" href="/react/fast-equals.9df4a03b.js">
<link rel="modulepreload" href="/react/hast-util-to-html.9c907bdf.js">
<link rel="modulepreload" href="/react/screenfull.4b7dcae0.js">
<link rel="modulepreload" href="/react/rehype-prism-plus.f2c8a7e0.js">
<link rel="modulepreload" href="/react/react-onclickoutside.16a5d5c9.js">
<link rel="modulepreload" href="/react/@hello-pangea.a21298ea.js">
<link rel="modulepreload" href="/react/react-qr-code.990bb400.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-footnote.9832ece2.js">
<link rel="modulepreload" href="/react/mdast-util-find-and-replace.60a1bc2c.js">
<link rel="modulepreload" href="/react/style-to-object.74a4cfe6.js">
<link rel="modulepreload" href="/react/toggle-selection.25b8be4d.js">
<link rel="modulepreload" href="/react/d3-path.a0f08300.js">
<link rel="modulepreload" href="/react/boolbase.bd705bc9.js">
<link rel="modulepreload" href="/react/react-is.43cb5aff.js">
<link rel="modulepreload" href="/react/react-debounce-input.4eed444a.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-table.f1729384.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-table.f7983341.js">
<link rel="modulepreload" href="/react/d3-array.2eff27df.js">
<link rel="modulepreload" href="/react/micromark.ea2d7513.js">
<link rel="modulepreload" href="/react/hast-util-from-parse5.59a95153.js">
<link rel="modulepreload" href="/react/d3-scale.e979474b.js">
<link rel="modulepreload" href="/react/recharts.5f1f7161.js">
<link rel="modulepreload" href="/react/unist-util-visit-parents.56bb16a1.js">
<link rel="modulepreload" href="/react/fast-shallow-equal.b907bd6a.js">
<link rel="modulepreload" href="/react/prop-types.55845d93.js">
<link rel="modulepreload" href="/react/@emotion.ad14dc62.js">
<link rel="modulepreload" href="/react/color-name.65e26c0b.js">
<link rel="modulepreload" href="/react/vfile-message.531f27be.js">
<link rel="modulepreload" href="/react/reselect.1a98dab5.js">
<link rel="modulepreload" href="/react/hast-util-has-property.bf6e0cae.js">
<link rel="modulepreload" href="/react/decimal.js-light.bb502cb3.js">
<link rel="modulepreload" href="/react/is-hexadecimal.d91cb013.js">
<link rel="modulepreload" href="/react/react-fast-compare.d5a5a050.js">
<link rel="modulepreload" href="/react/is-decimal.247a88b7.js">
<link rel="modulepreload" href="/react/hast-util-parse-selector.1be1f27a.js">
<link rel="modulepreload" href="/react/unist-util-stringify-position.7846d611.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-tagfilter.97dd1d78.js">
<link rel="modulepreload" href="/react/unist-util-is.bd39e4f2.js">
<link rel="modulepreload" href="/react/rehype-autolink-headings.44122af2.js">
<link rel="modulepreload" href="/react/d3-interpolate.a37b569a.js">
<link rel="modulepreload" href="/react/character-reference-invalid.9429e209.js">
<link rel="modulepreload" href="/react/d3-shape.e4c3f864.js">
<link rel="modulepreload" href="/react/nano-css.abede7db.js">
<link rel="modulepreload" href="/react/mdast-util-to-hast.f08c5f84.js">
<link rel="modulepreload" href="/react/tiny-warning.14328f40.js">
<link rel="modulepreload" href="/react/hast-util-whitespace.b9acc724.js">
<link rel="modulepreload" href="/react/scheduler.159558c9.js">
<link rel="modulepreload" href="/react/react-select.dfccd9d4.js">
<link rel="stylesheet" href="/react/react-datepicker.ad954662.css">
<link rel="modulepreload" href="/react/react-query.c174b29d.js">
<link rel="modulepreload" href="/react/tiny-invariant.4a33ca3a.js">
<link rel="modulepreload" href="/react/d3-time-format.c538854d.js">
<link rel="modulepreload" href="/react/file-selector.c939a338.js">
<link rel="modulepreload" href="/react/@remix-run.bc8e0a8a.js">
<link rel="modulepreload" href="/react/use-memo-one.5c7a3f6d.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-autolink-literal.75d97f6e.js">
<link rel="modulepreload" href="/react/micromark-util-character.811e4fc8.js">
<link rel="modulepreload" href="/react/reduce-css-calc.8f09a23d.js">
<link rel="modulepreload" href="/react/trim-lines.a08a0c08.js">
<link rel="modulepreload" href="/react/micromark-factory-title.f4e107a2.js">
<link rel="modulepreload" href="/react/attr-accept.6d6aa28d.js">
<link rel="modulepreload" href="/react/stylis.967d24d9.js">
<link rel="modulepreload" href="/react/vfile.65955e00.js">
<link rel="modulepreload" href="/react/trough.e66b541d.js">
<link rel="modulepreload" href="/react/lodash.89b99a1e.js">
<link rel="modulepreload" href="/react/lodash.curry.982dd407.js">
<link rel="modulepreload" href="/react/parse-entities.0d3eeb8f.js">
<link rel="modulepreload" href="/react/js-cookie.a424ed8d.js">
<link rel="modulepreload" href="/react/copy-to-clipboard.107ef1da.js">
<link rel="modulepreload" href="/react/fast-deep-equal.acde360c.js">
<link rel="modulepreload" href="/react/rehype-attr.f14a5c46.js">
<link rel="modulepreload" href="/react/parse-numeric-range.352ca5bb.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-footnote.b328c551.js">
<link rel="stylesheet" href="/react/index.8f8e7d31.css">
<link rel="modulepreload" href="/react/react-markdown.2794167a.js">
<link rel="modulepreload" href="/react/stringify-entities.a8aab4da.js">
<link rel="modulepreload" href="/react/mdast-util-gfm.9c5e4fde.js">
<link rel="modulepreload" href="/react/d3-time.4599568f.js">
<link rel="modulepreload" href="/react/axios.83da4cff.js">
<link rel="modulepreload" href="/react/micromark-factory-destination.08e32d90.js">
<link rel="modulepreload" href="/react/direction.5373b893.js">
<link rel="modulepreload" href="/react/set-harmonic-interval.933d0946.js">
<link rel="modulepreload" href="/react/micromark-util-subtokenize.0273ad72.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-strikethrough.326b57f9.js">
<link rel="modulepreload" href="/react/property-information.85abbb0b.js">
<link rel="modulepreload" href="/react/is-alphanumerical.39e15300.js">
<link rel="modulepreload" href="/react/react.0133eee9.js">
<link rel="modulepreload" href="/react/micromark-factory-whitespace.4e8e9fce.js">
<link rel="modulepreload" href="/react/react-lifecycles-compat.8d6bbd76.js">
<link rel="modulepreload" href="/react/mdast-util-to-markdown.7e1348ca.js">
<link rel="modulepreload" href="/react/remark-rehype.2632fc98.js">
<link rel="modulepreload" href="/react/react-popper.449be6ef.js">
<link rel="modulepreload" href="/react/rehype-rewrite.8d95b6e8.js">
<link rel="modulepreload" href="/react/react-redux.aa877b79.js">
<link rel="modulepreload" href="/react/nth-check.53474b0f.js">
<link rel="modulepreload" href="/react/currency.js.cd0659e5.js">
<link rel="modulepreload" href="/react/react-smooth.2b9effc5.js">
<link rel="modulepreload" href="/react/array-move.20870289.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-task-list-item.36dc3006.js">
<link rel="modulepreload" href="/react/client-only.9e01468e.js">
<link rel="modulepreload" href="/react/react-dom.7dba7d7c.js">
<link rel="modulepreload" href="/react/rehype.8e8eadfe.js">
<link rel="modulepreload" href="/react/date-fns.c9928d0b.js">
<link rel="modulepreload" href="/react/mdast-util-from-markdown.b50f44f1.js">
<link rel="modulepreload" href="/react/deepmerge.a6dda5ef.js">
<link rel="modulepreload" href="/react/eventemitter3.4d80f9fd.js">
<link rel="modulepreload" href="/react/tippy.js.de2d4cc1.js">
<link rel="modulepreload" href="/react/@reduxjs.c59f0af0.js">
<link rel="modulepreload" href="/react/react-colorful.df9e628a.js">
<link rel="modulepreload" href="/react/@sentry.53d33e51.js">
<link rel="modulepreload" href="/react/lodash-es.9cfcbab2.js">
<link rel="modulepreload" href="/react/immer.d85b7db2.js">
<link rel="modulepreload" href="/react/color-convert.5e8bad7a.js">
<link rel="modulepreload" href="/react/bail.58fa4042.js">
<link rel="modulepreload" href="/react/remark-parse.2678f4ff.js">
<link rel="modulepreload" href="/react/unist-util-position.207de903.js">
<link rel="modulepreload" href="/react/uuid.3e0f6f68.js">
<link rel="modulepreload" href="/react/use-isomorphic-layout-effect.38f47e1d.js">
<link rel="modulepreload" href="/react/react-i18next.6e7ac069.js">
<link rel="modulepreload" href="/react/micromark-factory-space.84551a41.js">
<link rel="modulepreload" href="/react/hast-util-heading-rank.0127330c.js">
<link rel="modulepreload" href="/react/parse5.761ebfb8.js">
<link rel="modulepreload" href="/react/unified.8e87ff8a.js">
<link rel="modulepreload" href="/react/micromark-util-normalize-identifier.f045ed13.js">
<link rel="modulepreload" href="/react/tslib.5ad378ed.js">
<link rel="modulepreload" href="/react/html-void-elements.d3b5aea3.js">
<link rel="modulepreload" href="/react/hast-util-select.70119e15.js">
<link rel="modulepreload" href="/react/zwitch.4efcb291.js">
<link rel="modulepreload" href="/react/react-hot-toast.7d7a6ca8.js">
<link rel="modulepreload" href="/react/hast-util-to-string.03a4649a.js">
<link rel="modulepreload" href="/react/micromark-util-chunked.62b98efd.js">
<link rel="modulepreload" href="/react/css-selector-parser.b3f82639.js">
<link rel="modulepreload" href="/react/@headlessui.1a209fe0.js">
<link rel="modulepreload" href="/react/character-entities-legacy.cb27298c.js">
<link rel="modulepreload" href="/react/inline-style-parser.ec55a77b.js">
<link rel="modulepreload" href="/react/unist-builder.67dd3ae6.js">
<link rel="modulepreload" href="/react/color.e5754c42.js">
<link rel="modulepreload" href="/react/d3-color.69ee14e3.js">
<link rel="modulepreload" href="/react/rehype-raw.dff21c1a.js">
<link rel="modulepreload" href="/react/react-datepicker.60c50a3c.js">
<link rel="modulepreload" href="/react/micromark-core-commonmark.53c183e2.js">
<link rel="modulepreload" href="/react/react-icons.1523f42c.js">
<link rel="modulepreload" href="/react/hast-util-is-element.4ae8c712.js">
<link rel="modulepreload" href="/react/mdast-util-gfm-autolink-literal.c934cb3a.js">
<link rel="modulepreload" href="/react/react-universal-interface.9dd0ac79.js">
<link rel="modulepreload" href="/react/comma-separated-tokens.826a3e44.js">
<link rel="modulepreload" href="/react/markdown-table.f01db7c9.js">
<link rel="modulepreload" href="/react/mdast-util-to-string.717718d5.js">
<link rel="modulepreload" href="/react/character-entities-html4.bfdecac8.js">
<link rel="modulepreload" href="/react/decode-named-character-reference.58fdd984.js">
<link rel="modulepreload" href="/react/d3-format.37b93b2c.js">
<link rel="modulepreload" href="/react/is-alphabetical.a64573e0.js">
<link rel="modulepreload" href="/react/simple-swizzle.a9455484.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-task-list-item.33d9f10c.js">
<link rel="modulepreload" href="/react/css-unit-converter.d617fa75.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm.f1ea290d.js">
<link rel="modulepreload" href="/react/index.e986f53b.js">
<link rel="modulepreload" href="/react/qr.js.da7c6dcd.js">
<link rel="modulepreload" href="/react/jotai.b2000ad6.js">
<link rel="modulepreload" href="/react/@uiw.f97db60f.js">
<link rel="modulepreload" href="/react/@xobotyi.606f64fa.js">
<link rel="modulepreload" href="/react/raf-schd.1b70a490.js">
<link rel="modulepreload" href="/react/micromark-util-resolve-all.b4e652e2.js">
<link rel="modulepreload" href="/react/unist-util-filter.14ea3396.js">
<link rel="modulepreload" href="/react/hast-util-raw.da8704cb.js">
<link rel="modulepreload" href="/react/rehype-slug.79dc0c87.js">
<link rel="modulepreload" href="/react/web-namespaces.a24d7a09.js">
<link rel="modulepreload" href="/react/react-microsoft-login.29ce1383.js">
<link rel="modulepreload" href="/react/react-router.2acabcc1.js">
<link rel="modulepreload" href="/react/space-separated-tokens.59bb4ca8.js">
<link rel="modulepreload" href="/react/msal.bb42b8d0.js">
<link rel="modulepreload" href="/react/throttle-debounce.006a1c17.js">
<link rel="modulepreload" href="/react/rehype-ignore.74e5ec67.js">
<link rel="modulepreload" href="/react/hastscript.32892f91.js">
<link rel="modulepreload" href="/react/void-elements.fa8a9e47.js">
<link rel="modulepreload" href="/react/micromark-util-html-tag-name.86a7a283.js">
<link rel="modulepreload" href="/react/warning.3e618ed9.js">
<link rel="modulepreload" href="/react/react-use.225f2d99.js">
<link rel="modulepreload" href="/react/lodash.debounce.637814ae.js">
<link rel="modulepreload" href="/react/@leecheuk.5575d56c.js">
<link rel="modulepreload" href="/react/micromark-util-encode.2bb97fcf.js">
<link rel="modulepreload" href="/react/refractor.77887221.js">
<link rel="modulepreload" href="/react/react-dropzone.9262fdd4.js">
<link rel="modulepreload" href="/react/ccount.24f4fb2a.js">
<link rel="modulepreload" href="/react/rehype-stringify.b4be5192.js">
<link rel="modulepreload" href="/react/micromark-util-decode-string.d48b94e2.js">
<link rel="modulepreload" href="/react/react-base16-styling.34dca7ff.js">
<link rel="stylesheet" href="/react/@uiw.ea8ec30a.css">
<link rel="modulepreload" href="/react/hoist-non-react-statics.c7b65a01.js">
<link rel="modulepreload" href="/react/goober.f083a39d.js">
<link rel="modulepreload" href="/react/mdast-util-definitions.f453f0a8.js">
<link rel="modulepreload" href="/react/rehype-parse.ed87eeb5.js">
<link rel="modulepreload" href="/react/github-slugger.b13b4a70.js">
<link rel="modulepreload" href="/react/i18next.2068112b.js">
<link rel="modulepreload" href="/react/micromark-util-classify-character.bb02743f.js">
<link rel="modulepreload" href="/react/collect.js.6c048100.js">
<link rel="modulepreload" href="/react/color-string.429a2c75.js">
<link rel="modulepreload" href="/react/unist-util-generated.22d80e74.js">
<link rel="modulepreload" href="/react/redux.64c13450.js">
<link rel="modulepreload" href="/react/react-resize-detector.f0db5e95.js">
<link rel="modulepreload" href="/react/hast-to-hyperscript.619bd0ae.js">
<link rel="modulepreload" href="/react/redux-thunk.cb293941.js">
<link rel="modulepreload" href="/react/react-string-replace.6d88c783.js">
<link rel="modulepreload" href="/react/recharts-scale.3b840976.js">
<link rel="modulepreload" href="/react/micromark-util-decode-numeric-character-reference.029edbd3.js">
<link rel="modulepreload" href="/react/micromark-util-sanitize-uri.dc741380.js">
<link rel="modulepreload" href="/react/extend.ca41ef84.js">
<link rel="modulepreload" href="/react/unist-util-visit.7a4beab6.js">
<link rel="modulepreload" href="/react/micromark-factory-label.2e0fc916.js">
<link rel="modulepreload" href="/react/hast-util-to-parse5.56a1d940.js">
<link rel="modulepreload" href="/react/dayjs.6e7fb39c.js">
<link rel="modulepreload" href="/react/css-box-model.6f12fb31.js">
<link rel="modulepreload" href="/react/@floating-ui.1d43bb23.js">
<link rel="modulepreload" href="/react/memoize-one.a4543f92.js">
<link rel="modulepreload" href="/react/@babel.b136989e.js">
<link rel="modulepreload" href="/react/internmap.bf78dfdb.js">
<link rel="modulepreload" href="/react/bcp-47-match.d4994b7a.js">
<link rel="modulepreload" href="/react/micromark-util-combine-extensions.fc6008e7.js">
<link rel="modulepreload" href="/react/is-buffer.f91fab60.js">
<link rel="modulepreload" href="/react/formik.215fe8e4.js">
<link rel="modulepreload" href="/react/pretty-bytes.1ef697de.js">
<link rel="modulepreload" href="/react/ts-easing.fc2b827e.js">
<link rel="modulepreload" href="/react/use-sync-external-store.73901a40.js">
<link rel="modulepreload" href="/react/micromark-extension-gfm-strikethrough.39c8bc63.js">
<link rel="modulepreload" href="/react/is-plain-obj.9b15794f.js">
<link rel="modulepreload" href="/react/react-json-tree.d195a734.js">
<link rel="modulepreload" href="/react/html-parse-stringify.32417b46.js">
<link rel="modulepreload" href="/react/@popperjs.bd37d762.js">
<link rel="modulepreload" href="/react/react-router-dom.246be94a.js">
<link rel="modulepreload" href="/react/remark-gfm.7adf8a5a.js">

View File

@ -293,17 +293,17 @@ class ReportCsvGenerationTest extends TestCase
])->post('/api/v1/reports/vendors', $data);
$csv = $response->streamedContent();
$this->assertEquals('Vendor 1', $this->getFirstValueByColumn($csv, 'Name'));
$this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Number'));
$this->assertEquals('city', $this->getFirstValueByColumn($csv, 'City'));
$this->assertEquals('address1', $this->getFirstValueByColumn($csv, 'Street'));
$this->assertEquals('address2', $this->getFirstValueByColumn($csv, 'Apt/Suite'));
$this->assertEquals('postal_code', $this->getFirstValueByColumn($csv, 'Postal Code'));
$this->assertEquals('work_phone', $this->getFirstValueByColumn($csv, 'Phone'));
$this->assertEquals('private_notes', $this->getFirstValueByColumn($csv, 'Private Notes'));
$this->assertEquals('public_notes', $this->getFirstValueByColumn($csv, 'Public Notes'));
$this->assertEquals('website', $this->getFirstValueByColumn($csv, 'Website'));
nlog($csv);
$this->assertEquals('Vendor 1', $this->getFirstValueByColumn($csv, 'Vendor Name'));
$this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Vendor Number'));
$this->assertEquals('city', $this->getFirstValueByColumn($csv, 'Vendor City'));
$this->assertEquals('address1', $this->getFirstValueByColumn($csv, 'Vendor Street'));
$this->assertEquals('address2', $this->getFirstValueByColumn($csv, 'Vendor Apt/Suite'));
$this->assertEquals('postal_code', $this->getFirstValueByColumn($csv, 'Vendor Postal Code'));
$this->assertEquals('work_phone', $this->getFirstValueByColumn($csv, 'Vendor Phone'));
$this->assertEquals('private_notes', $this->getFirstValueByColumn($csv, 'Vendor Private Notes'));
$this->assertEquals('public_notes', $this->getFirstValueByColumn($csv, 'Vendor Public Notes'));
$this->assertEquals('website', $this->getFirstValueByColumn($csv, 'Vendor Website'));
}
@ -345,9 +345,9 @@ class ReportCsvGenerationTest extends TestCase
$csv = $response->streamedContent();
$this->assertEquals('Vendor 1', $this->getFirstValueByColumn($csv, 'Name'));
$this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Number'));
$this->assertEquals('city', $this->getFirstValueByColumn($csv, 'City'));
$this->assertEquals('Vendor 1', $this->getFirstValueByColumn($csv, 'Vendor Name'));
$this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Vendor Number'));
$this->assertEquals('city', $this->getFirstValueByColumn($csv, 'Vendor City'));
}
@ -488,14 +488,14 @@ class ReportCsvGenerationTest extends TestCase
$csv = $response->streamedContent();
$this->assertEquals(3600, $this->getFirstValueByColumn($csv, 'Duration'));
$this->assertEquals('test', $this->getFirstValueByColumn($csv, 'Description'));
$this->assertEquals('16/Jul/2023', $this->getFirstValueByColumn($csv, 'Start Date'));
$this->assertEquals('16/Jul/2023', $this->getFirstValueByColumn($csv, 'End Date'));
$this->assertEquals('Custom 1', $this->getFirstValueByColumn($csv, 'Custom Value 1'));
$this->assertEquals('Custom 2', $this->getFirstValueByColumn($csv, 'Custom Value 2'));
$this->assertEquals('Custom 3', $this->getFirstValueByColumn($csv, 'Custom Value 3'));
$this->assertEquals('Custom 4', $this->getFirstValueByColumn($csv, 'Custom Value 4'));
$this->assertEquals(3600, $this->getFirstValueByColumn($csv, 'Task Duration'));
$this->assertEquals('test', $this->getFirstValueByColumn($csv, 'Task Description'));
$this->assertEquals('16/Jul/2023', $this->getFirstValueByColumn($csv, 'Task Start Date'));
$this->assertEquals('16/Jul/2023', $this->getFirstValueByColumn($csv, 'Task End Date'));
$this->assertEquals('Custom 1', $this->getFirstValueByColumn($csv, 'Task Custom Value 1'));
$this->assertEquals('Custom 2', $this->getFirstValueByColumn($csv, 'Task Custom Value 2'));
$this->assertEquals('Custom 3', $this->getFirstValueByColumn($csv, 'Task Custom Value 3'));
$this->assertEquals('Custom 4', $this->getFirstValueByColumn($csv, 'Task Custom Value 4'));
}
@ -1567,29 +1567,29 @@ class ReportCsvGenerationTest extends TestCase
$csv = $response->streamedContent();
$this->assertEquals('100', $this->getFirstValueByColumn($csv, 'Amount'));
$this->assertEquals('50', $this->getFirstValueByColumn($csv, 'Balance'));
$this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Discount'));
$this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'PO Number'));
$this->assertEquals('Public', $this->getFirstValueByColumn($csv, 'Public Notes'));
$this->assertEquals('Private', $this->getFirstValueByColumn($csv, 'Private Notes'));
$this->assertEquals('Terms', $this->getFirstValueByColumn($csv, 'Terms'));
$this->assertEquals('2020-01-01', $this->getFirstValueByColumn($csv, 'Date'));
$this->assertEquals('2021-01-02', $this->getFirstValueByColumn($csv, 'Due Date'));
$this->assertEquals('2021-01-03', $this->getFirstValueByColumn($csv, 'Partial Due Date'));
$this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Partial/Deposit'));
$this->assertEquals('Custom 1', $this->getFirstValueByColumn($csv, 'Custom Value 1'));
$this->assertEquals('Custom 2', $this->getFirstValueByColumn($csv, 'Custom Value 2'));
$this->assertEquals('Custom 3', $this->getFirstValueByColumn($csv, 'Custom Value 3'));
$this->assertEquals('Custom 4', $this->getFirstValueByColumn($csv, 'Custom Value 4'));
$this->assertEquals('Footer', $this->getFirstValueByColumn($csv, 'Footer'));
$this->assertEquals('Tax 1', $this->getFirstValueByColumn($csv, 'Tax Name 1'));
$this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Tax Rate 1'));
$this->assertEquals('Tax 2', $this->getFirstValueByColumn($csv, 'Tax Name 2'));
$this->assertEquals('20', $this->getFirstValueByColumn($csv, 'Tax Rate 2'));
$this->assertEquals('Tax 3', $this->getFirstValueByColumn($csv, 'Tax Name 3'));
$this->assertEquals('30', $this->getFirstValueByColumn($csv, 'Tax Rate 3'));
$this->assertEquals('Daily', $this->getFirstValueByColumn($csv, 'How Often'));
$this->assertEquals('100', $this->getFirstValueByColumn($csv, 'Recurring Invoice Amount'));
$this->assertEquals('50', $this->getFirstValueByColumn($csv, 'Recurring Invoice Balance'));
$this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Recurring Invoice Discount'));
$this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Recurring Invoice PO Number'));
$this->assertEquals('Public', $this->getFirstValueByColumn($csv, 'Recurring Invoice Public Notes'));
$this->assertEquals('Private', $this->getFirstValueByColumn($csv, 'Recurring Invoice Private Notes'));
$this->assertEquals('Terms', $this->getFirstValueByColumn($csv, 'Recurring Invoice Terms'));
$this->assertEquals('2020-01-01', $this->getFirstValueByColumn($csv, 'Recurring Invoice Date'));
$this->assertEquals('2021-01-02', $this->getFirstValueByColumn($csv, 'Recurring Invoice Due Date'));
$this->assertEquals('2021-01-03', $this->getFirstValueByColumn($csv, 'Recurring Invoice Partial Due Date'));
$this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Recurring Invoice Partial/Deposit'));
$this->assertEquals('Custom 1', $this->getFirstValueByColumn($csv, 'Recurring Invoice Custom Value 1'));
$this->assertEquals('Custom 2', $this->getFirstValueByColumn($csv, 'Recurring Invoice Custom Value 2'));
$this->assertEquals('Custom 3', $this->getFirstValueByColumn($csv, 'Recurring Invoice Custom Value 3'));
$this->assertEquals('Custom 4', $this->getFirstValueByColumn($csv, 'Recurring Invoice Custom Value 4'));
$this->assertEquals('Footer', $this->getFirstValueByColumn($csv, 'Recurring Invoice Footer'));
$this->assertEquals('Tax 1', $this->getFirstValueByColumn($csv, 'Recurring Invoice Tax Name 1'));
$this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Recurring Invoice Tax Rate 1'));
$this->assertEquals('Tax 2', $this->getFirstValueByColumn($csv, 'Recurring Invoice Tax Name 2'));
$this->assertEquals('20', $this->getFirstValueByColumn($csv, 'Recurring Invoice Tax Rate 2'));
$this->assertEquals('Tax 3', $this->getFirstValueByColumn($csv, 'Recurring Invoice Tax Name 3'));
$this->assertEquals('30', $this->getFirstValueByColumn($csv, 'Recurring Invoice Tax Rate 3'));
$this->assertEquals('Daily', $this->getFirstValueByColumn($csv, 'Recurring Invoice How Often'));
}

View File

@ -74,19 +74,19 @@ class QuoteTest extends TestCase
'line_items' =>[
[
'type_id' => 2,
'unit_cost' => 200,
'cost' => 200,
'quantity' => 2,
'notes' => 'Test200',
],
[
'type_id' => 2,
'unit_cost' => 100,
'cost' => 100,
'quantity' => 1,
'notes' => 'Test100',
],
[
'type_id' => 1,
'unit_cost' => 10,
'cost' => 10,
'quantity' => 1,
'notes' => 'Test',
],
@ -171,7 +171,7 @@ class QuoteTest extends TestCase
$project = Project::find($this->decodePrimaryKey($res['data'][0]['project_id']));
$this->assertEquals($project->name, ctrans('texts.quote_number_short') . " " . $this->quote->number);
$this->assertEquals($project->name, ctrans('texts.quote_number_short') . " " . $this->quote->number."[{$this->quote->client->present()->name()}]");
}
public function testQuoteList()

View File

@ -49,6 +49,58 @@ class InvoiceTest extends TestCase
$this->invoice_calc = new InvoiceSum($this->invoice);
}
public function testGrossTaxAmountCalcuations()
{
$invoice = InvoiceFactory::create($this->company->id, $this->user->id);
$invoice->client_id = $this->client->id;
$invoice->uses_inclusive_taxes = false;
$invoice->discount = 14191;
$invoice->is_amount_discount = true;
$invoice->status_id = 2;
$line_items = [];
$line_item = new InvoiceItem;
$line_item->quantity = 1;
$line_item->cost = 8000;
$line_item->tax_rate1 = 5;
$line_item->tax_name1 = 'CA';
$line_item->product_key = 'line1';
$line_item->notes = 'Test';
$line_item->tax_id = 1;
$line_items[] = $line_item;
$line_item = new InvoiceItem;
$line_item->quantity = 1;
$line_item->cost = 9500;
$line_item->tax_rate1 = 5;
$line_item->tax_name1 = 'CA';
$line_item->product_key = 'line2';
$line_item->notes = 'Test';
$line_item->tax_id = 1;
$line_items[] = $line_item;
$invoice->line_items = $line_items;
$invoice->save();
$calc = $invoice->calc();
$invoice = $calc->getInvoice();
$this->assertEquals(3474.45, $invoice->amount);
$this->assertEquals(14191, $invoice->discount);
$this->assertEquals(165.45, $invoice->total_taxes);
$item = collect($invoice->line_items)->firstWhere('product_key', 'line1');
$this->assertEquals(75.63, $item->tax_amount);
$this->assertEquals(8075.63, $item->gross_line_total);
$item = collect($invoice->line_items)->firstWhere('product_key', 'line2');
$this->assertEquals(89.82, $item->tax_amount);
$this->assertEquals(9589.82, $item->gross_line_total);
}
public function testTaskRoundingPrecisionThree()
{
$invoice = InvoiceFactory::create($this->company->id, $this->user->id);